مراية لـ
https://github.com/postalserver/postal.git
تم المزامنة 2025-12-01 05:43:04 +00:00
feat: new configuration system (and schema) (#2819)
هذا الالتزام موجود في:
2
.gitignore
مباع
2
.gitignore
مباع
@@ -31,3 +31,5 @@ Procfile.local
|
||||
VERSION
|
||||
|
||||
.rubocop-https*
|
||||
.env*
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ bundle install
|
||||
|
||||
## Configuration
|
||||
|
||||
At present, configuration is handled using a config file. This lives in `config/postal/postal.yml`. An example configuration file is provided in `config/postal.example.yml`. This example is for development use only and not an example for production use.
|
||||
Configuration is handled using a config file. This lives in `config/postal/postal.yml`. An example configuration file is provided in `config/examples/development.yml`. This example is for development use only and not an example for production use.
|
||||
|
||||
You'll also need a key for signing. You can generate one of these like this:
|
||||
|
||||
@@ -32,6 +32,10 @@ You'll also need a key for signing. You can generate one of these like this:
|
||||
openssl genrsa -out config/postal/signing.key 2048
|
||||
```
|
||||
|
||||
If you're running the tests (and you probably should be), you'll find an example file for test configuration in `config/examples/test.yml`. This should be placed in `config/postal/postal.test.yml` with the appropriate values.
|
||||
|
||||
If you prefer, you can configure Postal using environment variables. These should be placed in `.env` or `.env.test` as apprpriate.
|
||||
|
||||
## Running
|
||||
|
||||
The neatest way to run postal is to ensure that `./bin` is your `$PATH` and then use one of the following commands.
|
||||
|
||||
@@ -46,8 +46,11 @@ COPY --chown=postal . .
|
||||
ARG VERSION=unspecified
|
||||
RUN echo $VERSION > VERSION
|
||||
|
||||
# Set the path to the config
|
||||
ENV POSTAL_CONFIG_ROOT=/config
|
||||
# Set paths for when running in a container
|
||||
ENV POSTAL_CONFIG_FILE_PATH=/config/postal.yml
|
||||
ENV POSTAL_SIGNING_KEY_PATH=/config/signing.key
|
||||
ENV SMTP_SERVER_TLS_CERTIFICATE_PATH=/config/smtp.cert
|
||||
ENV SMTP_SERVER_TLS_PRIVATE_KEY_PATH=/config/smtp.key
|
||||
|
||||
# Set the CMD
|
||||
ENTRYPOINT [ "/docker-entrypoint.sh" ]
|
||||
@@ -59,5 +62,5 @@ FROM base AS ci
|
||||
# full target - default if no --target option is given
|
||||
FROM base AS full
|
||||
|
||||
RUN POSTAL_SKIP_CONFIG_CHECK=1 RAILS_GROUPS=assets bundle exec rake assets:precompile
|
||||
RUN RAILS_GROUPS=assets bundle exec rake assets:precompile
|
||||
RUN touch /opt/postal/app/public/assets/.prebuilt
|
||||
|
||||
3
Gemfile
3
Gemfile
@@ -5,7 +5,7 @@ gem "authie"
|
||||
gem "autoprefixer-rails"
|
||||
gem "bcrypt"
|
||||
gem "chronic"
|
||||
gem "dotenv-rails"
|
||||
gem "dotenv"
|
||||
gem "dynamic_form"
|
||||
gem "encrypto_signo"
|
||||
gem "execjs", "~> 2.7", "< 2.8"
|
||||
@@ -15,6 +15,7 @@ gem "hashie"
|
||||
gem "highline", require: false
|
||||
gem "kaminari"
|
||||
gem "klogger-logger"
|
||||
gem "konfig-config", "~> 2.0"
|
||||
gem "mail"
|
||||
gem "moonrope"
|
||||
gem "mysql2"
|
||||
|
||||
@@ -102,9 +102,6 @@ GEM
|
||||
deep_merge (1.2.2)
|
||||
diff-lcs (1.5.0)
|
||||
dotenv (3.0.2)
|
||||
dotenv-rails (3.0.2)
|
||||
dotenv (= 3.0.2)
|
||||
railties (>= 6.1)
|
||||
dynamic_form (1.3.1)
|
||||
actionview (> 5.2.0)
|
||||
activemodel (> 5.2.0)
|
||||
@@ -151,6 +148,8 @@ GEM
|
||||
concurrent-ruby (>= 1.0, < 2.0)
|
||||
json
|
||||
rouge (>= 3.30, < 5.0)
|
||||
konfig-config (2.1.1)
|
||||
hashie
|
||||
loofah (2.22.0)
|
||||
crass (~> 1.0.2)
|
||||
nokogiri (>= 1.12.0)
|
||||
@@ -335,7 +334,7 @@ DEPENDENCIES
|
||||
chronic
|
||||
coffee-rails (~> 5.0)
|
||||
database_cleaner
|
||||
dotenv-rails
|
||||
dotenv
|
||||
dynamic_form
|
||||
encrypto_signo
|
||||
execjs (~> 2.7, < 2.8)
|
||||
@@ -347,6 +346,7 @@ DEPENDENCIES
|
||||
jquery-rails
|
||||
kaminari
|
||||
klogger-logger
|
||||
konfig-config (~> 2.0)
|
||||
mail
|
||||
moonrope
|
||||
mysql2
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
web: bundle exec puma -C config/puma.rb
|
||||
web: unset PORT; bundle exec puma -C config/puma.rb
|
||||
worker: bundle exec ruby script/worker.rb
|
||||
smtp: bundle exec ruby script/smtp_server.rb
|
||||
smtp: unset PORT; bundle exec ruby script/smtp_server.rb
|
||||
|
||||
@@ -8,9 +8,9 @@ class DKIMHeader
|
||||
@dkim_key = domain.dkim_key
|
||||
@dkim_identifier = domain.dkim_identifier
|
||||
else
|
||||
@domain_name = Postal.config.dns.return_path
|
||||
@domain_name = Postal::Config.dns.return_path_domain
|
||||
@dkim_key = Postal.signing_key
|
||||
@dkim_identifier = Postal.config.dns.dkim_identifier
|
||||
@dkim_identifier = Postal::Config.dns.dkim_identifier
|
||||
end
|
||||
@domain = domain
|
||||
@message = message
|
||||
|
||||
@@ -48,7 +48,7 @@ module MessageDequeuer
|
||||
end
|
||||
|
||||
def check_delivery_attempts
|
||||
return if queued_message.attempts < Postal.config.general.maximum_delivery_attempts
|
||||
return if queued_message.attempts < Postal::Config.postal.default_maximum_delivery_attempts
|
||||
|
||||
details = "Maximum number of delivery attempts (#{queued_message.attempts}) has been reached."
|
||||
if queued_message.message.scope == "incoming"
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
class ReceivedHeader
|
||||
|
||||
OUR_HOSTNAMES = {
|
||||
smtp: Postal.config.dns.smtp_server_hostname,
|
||||
http: Postal.config.web.host
|
||||
smtp: Postal::Config.postal.smtp_hostname,
|
||||
http: Postal::Config.postal.web_hostname
|
||||
}.freeze
|
||||
|
||||
class << self
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Postal
|
||||
class ReplySeparator
|
||||
|
||||
RULES = [
|
||||
@@ -33,4 +32,3 @@ module Postal
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
@@ -32,7 +32,9 @@ module SMTPServer
|
||||
end
|
||||
|
||||
def check_ip_address
|
||||
return unless @ip_address && Postal.config.smtp_server.log_exclude_ips && @ip_address =~ Regexp.new(Postal.config.smtp_server.log_exclude_ips)
|
||||
return unless @ip_address &&
|
||||
Postal::Config.smtp_server.log_ip_address_exclusion_matcher &&
|
||||
@ip_address =~ Regexp.new(Postal::Config.smtp_server.log_ip_address_exclusion_matcher)
|
||||
|
||||
@logging_enabled = false
|
||||
end
|
||||
@@ -109,7 +111,7 @@ module SMTPServer
|
||||
@state = :welcome
|
||||
log "\e[35m Client identified as #{@ip_address}\e[0m"
|
||||
increment_command_count("PROXY")
|
||||
"220 #{Postal.config.dns.smtp_server_hostname} ESMTP Postal/#{id}"
|
||||
"220 #{Postal::Config.postal.smtp_hostname} ESMTP Postal/#{id}"
|
||||
else
|
||||
@finished = true
|
||||
increment_error_count("proxy-error")
|
||||
@@ -123,7 +125,7 @@ module SMTPServer
|
||||
end
|
||||
|
||||
def starttls
|
||||
if Postal.config.smtp_server.tls_enabled?
|
||||
if Postal::Config.smtp_server.tls_enabled?
|
||||
@start_tls = true
|
||||
@tls = true
|
||||
increment_command_count("STARTLS")
|
||||
@@ -141,7 +143,7 @@ module SMTPServer
|
||||
increment_command_count("EHLO")
|
||||
[
|
||||
"250-My capabilities are",
|
||||
Postal.config.smtp_server.tls_enabled? && !@tls ? "250-STARTTLS" : nil,
|
||||
Postal::Config.smtp_server.tls_enabled? && !@tls ? "250-STARTTLS" : nil,
|
||||
"250 AUTH CRAM-MD5 PLAIN LOGIN"
|
||||
].compact
|
||||
end
|
||||
@@ -151,7 +153,7 @@ module SMTPServer
|
||||
transaction_reset
|
||||
@state = :welcomed
|
||||
increment_command_count("HELO")
|
||||
"250 #{Postal.config.dns.smtp_server_hostname}"
|
||||
"250 #{Postal::Config.postal.smtp_hostname}"
|
||||
end
|
||||
|
||||
def rset
|
||||
@@ -231,7 +233,7 @@ module SMTPServer
|
||||
increment_command_count("AUTH CRAM-MD5")
|
||||
|
||||
challenge = Digest::SHA1.hexdigest(Time.now.to_i.to_s + rand(100_000).to_s)
|
||||
challenge = "<#{challenge[0, 20]}@#{Postal.config.dns.smtp_server_hostname}>"
|
||||
challenge = "<#{challenge[0, 20]}@#{Postal::Config.postal.smtp_hostname}>"
|
||||
|
||||
handler = proc do |idata|
|
||||
@proc = nil
|
||||
@@ -309,7 +311,7 @@ module SMTPServer
|
||||
|
||||
uname, tag = uname.split("+", 2)
|
||||
|
||||
if domain == Postal.config.dns.return_path || domain =~ /\A#{Regexp.escape(Postal.config.dns.custom_return_path_prefix)}\./
|
||||
if domain == Postal::Config.dns.return_path_domain || domain =~ /\A#{Regexp.escape(Postal::Config.dns.custom_return_path_prefix)}\./
|
||||
# This is a return path
|
||||
@state = :rcpt_to_received
|
||||
if server = ::Server.where(token: uname).first
|
||||
@@ -326,7 +328,7 @@ module SMTPServer
|
||||
"550 Invalid server token"
|
||||
end
|
||||
|
||||
elsif domain == Postal.config.dns.route_domain
|
||||
elsif domain == Postal::Config.dns.route_domain
|
||||
# This is an email direct to a route. This isn't actually supported yet.
|
||||
@state = :rcpt_to_received
|
||||
if route = Route.where(token: uname).first
|
||||
@@ -446,14 +448,14 @@ module SMTPServer
|
||||
end
|
||||
|
||||
def finished
|
||||
if @data.bytesize > Postal.config.smtp_server.max_message_size.megabytes.to_i
|
||||
if @data.bytesize > Postal::Config.smtp_server.max_message_size.megabytes.to_i
|
||||
transaction_reset
|
||||
@state = :welcomed
|
||||
increment_error_count("message-too-large")
|
||||
return format("552 Message too large (maximum size %dMB)", Postal.config.smtp_server.max_message_size)
|
||||
return format("552 Message too large (maximum size %dMB)", Postal::Config.smtp_server.max_message_size)
|
||||
end
|
||||
|
||||
if @headers["received"].grep(/by #{Postal.config.dns.smtp_server_hostname}/).count > 4
|
||||
if @headers["received"].grep(/by #{Postal::Config.postal.smtp_hostname}/).count > 4
|
||||
transaction_reset
|
||||
@state = :welcomed
|
||||
increment_error_count("loop-detected")
|
||||
|
||||
@@ -8,6 +8,24 @@ module SMTPServer
|
||||
|
||||
include HasPrometheusMetrics
|
||||
|
||||
class << self
|
||||
|
||||
def tls_private_key
|
||||
@tls_private_key ||= OpenSSL::PKey.read(File.read(Postal::Config.smtp_server.tls_private_key_path))
|
||||
end
|
||||
|
||||
def tls_certificates
|
||||
@tls_certificates ||= begin
|
||||
data = File.read(Postal::Config.smtp_server.tls_certificate_path)
|
||||
certs = data.scan(/-----BEGIN CERTIFICATE-----.+?-----END CERTIFICATE-----/m)
|
||||
certs.map do |c|
|
||||
OpenSSL::X509::Certificate.new(c)
|
||||
end.freeze
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
def initialize(options = {})
|
||||
@options = options
|
||||
@options[:debug] ||= false
|
||||
@@ -43,16 +61,19 @@ module SMTPServer
|
||||
@ssl_context ||= begin
|
||||
ssl_context = OpenSSL::SSL::SSLContext.new
|
||||
ssl_context.cert = Postal.smtp_certificates[0]
|
||||
ssl_context.extra_chain_cert = Postal.smtp_certificates[1..]
|
||||
ssl_context.key = Postal.smtp_private_key
|
||||
ssl_context.ssl_version = Postal.config.smtp_server.ssl_version if Postal.config.smtp_server.ssl_version
|
||||
ssl_context.ciphers = Postal.config.smtp_server.tls_ciphers if Postal.config.smtp_server.tls_ciphers
|
||||
ssl_context.extra_chain_cert = self.class.tls_certificates[1..]
|
||||
ssl_context.key = self.class.tls_private_key
|
||||
ssl_context.ssl_version = Postal::Config.smtp_server.ssl_version if Postal::Config.smtp_server.ssl_version
|
||||
ssl_context.ciphers = Postal::Config.smtp_server.tls_ciphers if Postal::Config.smtp_server.tls_ciphers
|
||||
ssl_context
|
||||
end
|
||||
end
|
||||
|
||||
def listen
|
||||
@server = TCPServer.open(Postal.config.smtp_server.bind_address, Postal.config.smtp_server.port)
|
||||
bind_address = ENV.fetch("BIND_ADDRESS", Postal::Config.smtp_server.default_bind_address)
|
||||
port = ENV.fetch("PORT", Postal::Config.smtp_server.default_port)
|
||||
|
||||
@server = TCPServer.open(bind_address, port)
|
||||
@server.autoclose = false
|
||||
@server.close_on_exec = false
|
||||
if defined?(Socket::SOL_SOCKET) && defined?(Socket::SO_KEEPALIVE)
|
||||
@@ -63,7 +84,8 @@ module SMTPServer
|
||||
@server.setsockopt(Socket::SOL_TCP, Socket::TCP_KEEPINTVL, 10)
|
||||
@server.setsockopt(Socket::SOL_TCP, Socket::TCP_KEEPCNT, 5)
|
||||
end
|
||||
logger.info "Listening on #{Postal.config.smtp_server.bind_address}:#{Postal.config.smtp_server.port}"
|
||||
|
||||
logger.info "Listening on #{bind_address}:#{port}"
|
||||
end
|
||||
|
||||
def unlisten
|
||||
@@ -90,22 +112,22 @@ module SMTPServer
|
||||
# Accept the connection
|
||||
new_io = io.accept
|
||||
increment_prometheus_counter :postal_smtp_server_connections_total
|
||||
if Postal.config.smtp_server.proxy_protocol
|
||||
if Postal::Config.smtp_server.proxy_protocol?
|
||||
# If we are using the haproxy proxy protocol, we will be sent the
|
||||
# client's IP later. Delay the welcome process.
|
||||
client = Client.new(nil)
|
||||
if Postal.config.smtp_server.log_connect
|
||||
if Postal::Config.smtp_server.log_connections?
|
||||
logger.debug "[#{client.id}] \e[35m Connection opened from #{new_io.remote_address.ip_address}\e[0m"
|
||||
end
|
||||
else
|
||||
# We're not using the proxy protocol so we already know the client's IP
|
||||
client = Client.new(new_io.remote_address.ip_address)
|
||||
if Postal.config.smtp_server.log_connect
|
||||
if Postal::Config.smtp_server.log_connections?
|
||||
logger.debug "[#{client.id}] \e[35m Connection opened from #{new_io.remote_address.ip_address}\e[0m"
|
||||
end
|
||||
# We know who the client is, welcome them.
|
||||
client.log "\e[35m Client identified as #{new_io.remote_address.ip_address}\e[0m"
|
||||
new_io.print("220 #{Postal.config.dns.smtp_server_hostname} ESMTP Postal/#{client.id}")
|
||||
new_io.print("220 #{Postal::Config.postal.smtp_hostname} ESMTP Postal/#{client.id}")
|
||||
end
|
||||
# Register the client and its socket with nio4r
|
||||
monitor = @io_selector.register(new_io, :r)
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
class ApplicationMailer < ActionMailer::Base
|
||||
|
||||
default from: "#{Postal.smtp_from_name} <#{Postal.smtp_from_address}>"
|
||||
default from: "#{Postal::Config.smtp.from_name} <#{Postal::Config.smtp.from_address}>"
|
||||
layout false
|
||||
|
||||
end
|
||||
|
||||
@@ -14,7 +14,7 @@ class BounceMessage
|
||||
mail.subject = "Mail Delivery Failed (#{@message.subject})"
|
||||
mail.text_part = body
|
||||
mail.attachments["Original Message.eml"] = { mime_type: "message/rfc822", encoding: "quoted-printable", content: @message.raw_message }
|
||||
mail.message_id = "<#{SecureRandom.uuid}@#{Postal.config.dns.return_path}>"
|
||||
mail.message_id = "<#{SecureRandom.uuid}@#{Postal::Config.dns.return_path_domain}>"
|
||||
mail.to_s
|
||||
end
|
||||
|
||||
@@ -32,7 +32,7 @@ class BounceMessage
|
||||
end
|
||||
|
||||
def postmaster_address
|
||||
@server.postmaster_address || "postmaster@#{@message.domain&.name || Postal.config.web.host}"
|
||||
@server.postmaster_address || "postmaster@#{@message.domain&.name || Postal::Config.postal.web_hostname}"
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
@@ -49,10 +49,10 @@ module HasDNSChecks
|
||||
self.spf_status = "Missing"
|
||||
self.spf_error = "No SPF record exists for this domain"
|
||||
else
|
||||
suitable_spf_records = spf_records.grep(/include:\s*#{Regexp.escape(Postal.config.dns.spf_include)}/)
|
||||
suitable_spf_records = spf_records.grep(/include:\s*#{Regexp.escape(Postal::Config.dns.spf_include)}/)
|
||||
if suitable_spf_records.empty?
|
||||
self.spf_status = "Invalid"
|
||||
self.spf_error = "An SPF record exists but it doesn't include #{Postal.config.dns.spf_include}"
|
||||
self.spf_error = "An SPF record exists but it doesn't include #{Postal::Config.dns.spf_include}"
|
||||
false
|
||||
else
|
||||
self.spf_status = "OK"
|
||||
@@ -108,11 +108,11 @@ module HasDNSChecks
|
||||
self.mx_status = "Missing"
|
||||
self.mx_error = "There are no MX records for #{name}"
|
||||
else
|
||||
missing_records = Postal.config.dns.mx_records.dup - records.map { |r| r.to_s.downcase }
|
||||
missing_records = Postal::Config.dns.mx_records.dup - records.map { |r| r.to_s.downcase }
|
||||
if missing_records.empty?
|
||||
self.mx_status = "OK"
|
||||
self.mx_error = nil
|
||||
elsif missing_records.size == Postal.config.dns.mx_records.size
|
||||
elsif missing_records.size == Postal::Config.dns.mx_records.size
|
||||
self.mx_status = "Missing"
|
||||
self.mx_error = "You have MX records but none of them point to us."
|
||||
else
|
||||
@@ -136,12 +136,12 @@ module HasDNSChecks
|
||||
if records.empty?
|
||||
self.return_path_status = "Missing"
|
||||
self.return_path_error = "There is no return path record at #{return_path_domain}"
|
||||
elsif records.size == 1 && records.first == Postal.config.dns.return_path
|
||||
elsif records.size == 1 && records.first == Postal::Config.dns.return_path_domain
|
||||
self.return_path_status = "OK"
|
||||
self.return_path_error = nil
|
||||
else
|
||||
self.return_path_status = "Invalid"
|
||||
self.return_path_error = "There is a CNAME record at #{return_path_domain} but it points to #{records.first} which is incorrect. It should point to #{Postal.config.dns.return_path}."
|
||||
self.return_path_error = "There is a CNAME record at #{return_path_domain} but it points to #{records.first} which is incorrect. It should point to #{Postal::Config.dns.return_path_domain}."
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -104,7 +104,7 @@ class Domain < ApplicationRecord
|
||||
end
|
||||
|
||||
def spf_record
|
||||
"v=spf1 a mx include:#{Postal.config.dns.spf_include} ~all"
|
||||
"v=spf1 a mx include:#{Postal::Config.dns.spf_include} ~all"
|
||||
end
|
||||
|
||||
def dkim_record
|
||||
@@ -117,7 +117,7 @@ class Domain < ApplicationRecord
|
||||
def dkim_identifier
|
||||
return nil unless dkim_identifier_string
|
||||
|
||||
Postal.config.dns.dkim_identifier + "-#{dkim_identifier_string}"
|
||||
Postal::Config.dns.dkim_identifier + "-#{dkim_identifier_string}"
|
||||
end
|
||||
|
||||
def dkim_record_name
|
||||
@@ -128,7 +128,7 @@ class Domain < ApplicationRecord
|
||||
end
|
||||
|
||||
def return_path_domain
|
||||
"#{Postal.config.dns.custom_return_path_prefix}.#{name}"
|
||||
"#{Postal::Config.dns.custom_return_path_prefix}.#{name}"
|
||||
end
|
||||
|
||||
# Returns a DNSResolver instance that can be used to perform DNS lookups needed for
|
||||
@@ -136,13 +136,13 @@ class Domain < ApplicationRecord
|
||||
#
|
||||
# @return [DNSResolver]
|
||||
def resolver
|
||||
return DNSResolver.local if Postal.config.general.use_local_ns_for_domains?
|
||||
return DNSResolver.local if Postal::Config.postal.use_local_ns_for_domain_verification?
|
||||
|
||||
@resolver ||= DNSResolver.for_domain(name)
|
||||
end
|
||||
|
||||
def dns_verification_string
|
||||
"#{Postal.config.dns.domain_verify_prefix} #{verification_token}"
|
||||
"#{Postal::Config.dns.domain_verify_prefix} #{verification_token}"
|
||||
end
|
||||
|
||||
def verify_with_dns
|
||||
|
||||
@@ -88,7 +88,7 @@ class IncomingMessagePrototype
|
||||
mail.from = @from
|
||||
mail.subject = @subject
|
||||
mail.text_part = @plain_body
|
||||
mail.message_id = "<#{SecureRandom.uuid}@#{Postal.config.dns.return_path}>"
|
||||
mail.message_id = "<#{SecureRandom.uuid}@#{Postal::Config.dns.return_path_domain}>"
|
||||
attachments.each do |attachment|
|
||||
mail.attachments[attachment[:name]] = {
|
||||
mime_type: attachment[:content_type],
|
||||
|
||||
@@ -25,7 +25,7 @@ class OutgoingMessagePrototype
|
||||
@source_type = source_type
|
||||
@custom_headers = {}
|
||||
@attachments = []
|
||||
@message_id = "#{SecureRandom.uuid}@#{Postal.config.dns.return_path}"
|
||||
@message_id = "#{SecureRandom.uuid}@#{Postal::Config.dns.return_path_domain}"
|
||||
attributes.each do |key, value|
|
||||
instance_variable_set("@#{key}", value)
|
||||
end
|
||||
|
||||
@@ -89,7 +89,7 @@ class Route < ApplicationRecord
|
||||
end
|
||||
|
||||
def forward_address
|
||||
@forward_address ||= "#{token}@#{Postal.config.dns.route_domain}"
|
||||
@forward_address ||= "#{token}@#{Postal::Config.dns.route_domain}"
|
||||
end
|
||||
|
||||
def wildcard?
|
||||
|
||||
@@ -71,8 +71,8 @@ class Server < ApplicationRecord
|
||||
default_value :raw_message_retention_days, -> { 30 }
|
||||
default_value :raw_message_retention_size, -> { 2048 }
|
||||
default_value :message_retention_days, -> { 60 }
|
||||
default_value :spam_threshold, -> { Postal.config.general.default_spam_threshold }
|
||||
default_value :spam_failure_threshold, -> { Postal.config.general.default_spam_failure_threshold }
|
||||
default_value :spam_threshold, -> { Postal::Config.postal.default_spam_threshold }
|
||||
default_value :spam_failure_threshold, -> { Postal::Config.postal.default_spam_failure_threshold }
|
||||
|
||||
validates :name, presence: true, uniqueness: { scope: :organization_id, case_sensitive: false }
|
||||
validates :mode, inclusion: { in: MODES }
|
||||
|
||||
@@ -58,12 +58,12 @@ class TrackDomain < ApplicationRecord
|
||||
if records.empty?
|
||||
self.dns_status = "Missing"
|
||||
self.dns_error = "There is no record at #{full_name}"
|
||||
elsif records.size == 1 && records.first == Postal.config.dns.track_domain
|
||||
elsif records.size == 1 && records.first == Postal::Config.dns.track_domain
|
||||
self.dns_status = "OK"
|
||||
self.dns_error = nil
|
||||
else
|
||||
self.dns_status = "Invalid"
|
||||
self.dns_error = "There is a CNAME record at #{full_name} but it points to #{records.first} which is incorrect. It should point to #{Postal.config.dns.track_domain}."
|
||||
self.dns_error = "There is a CNAME record at #{full_name} but it points to #{records.first} which is incorrect. It should point to #{Postal::Config.dns.track_domain}."
|
||||
end
|
||||
self.dns_checked_at = Time.now
|
||||
save!
|
||||
|
||||
@@ -47,8 +47,8 @@ class SMTPSender < BaseSender
|
||||
end
|
||||
|
||||
smtp_client = Net::SMTP.new(@remote_ip, port)
|
||||
smtp_client.open_timeout = Postal.config.smtp_client.open_timeout
|
||||
smtp_client.read_timeout = Postal.config.smtp_client.read_timeout
|
||||
smtp_client.open_timeout = Postal::Config.smtp_client.open_timeout
|
||||
smtp_client.read_timeout = Postal::Config.smtp_client.read_timeout
|
||||
smtp_client.tls_hostname = hostname
|
||||
|
||||
if @source_ip_address
|
||||
@@ -140,9 +140,9 @@ class SMTPSender < BaseSender
|
||||
elsif message.domain.return_path_status == "OK"
|
||||
mail_from = "#{message.server.token}@#{message.domain.return_path_domain}"
|
||||
else
|
||||
mail_from = "#{message.server.token}@#{Postal.config.dns.return_path}"
|
||||
mail_from = "#{message.server.token}@#{Postal::Config.dns.return_path_domain}"
|
||||
end
|
||||
if Postal.config.general.use_resent_sender_header
|
||||
if Postal::Config.postal.use_resent_sender_header
|
||||
raw_message = "Resent-Sender: #{mail_from}\r\n" + message.raw_message
|
||||
else
|
||||
raw_message = message.raw_message
|
||||
@@ -270,15 +270,20 @@ class SMTPSender < BaseSender
|
||||
end
|
||||
|
||||
def default_helo_hostname
|
||||
Postal.config.dns.helo_hostname || Postal.config.dns.smtp_server_hostname || "localhost"
|
||||
Postal::Config.dns.helo_hostname ||
|
||||
Postal::Config.postal.smtp_hostname ||
|
||||
"localhost"
|
||||
end
|
||||
|
||||
def relay_hosts
|
||||
hosts = Postal.config.smtp_relays.map do |relay|
|
||||
next unless relay.hostname.present?
|
||||
relays = Postal::Config.postal.smtp_relays
|
||||
return nil if relays.nil?
|
||||
|
||||
hosts = relays.map do |relay|
|
||||
next unless relay.host.present?
|
||||
|
||||
{
|
||||
hostname: relay.hostname,
|
||||
hostname: relay.host,
|
||||
port: relay.port,
|
||||
ssl_mode: relay.ssl_mode
|
||||
}
|
||||
|
||||
@@ -51,8 +51,9 @@ class HealthServer
|
||||
|
||||
class << self
|
||||
|
||||
def run(default_port: 9090, **options)
|
||||
def run(default_port:, default_bind_address:, **options)
|
||||
port = ENV.fetch("HEALTH_SERVER_PORT", default_port)
|
||||
bind_address = ENV.fetch("HEALTH_SERVER_BIND_ADDRESS", default_bind_address)
|
||||
|
||||
Rack::Handler::WEBrick.run(new(**options),
|
||||
Port: port,
|
||||
@@ -64,10 +65,6 @@ class HealthServer
|
||||
"in use, not starting health server"
|
||||
end
|
||||
|
||||
def bind_address
|
||||
ENV.fetch("HEALTH_SERVER_BIND_ADDRESS", "127.0.0.1")
|
||||
end
|
||||
|
||||
def start(**options)
|
||||
thread = Thread.new { run(**options) }
|
||||
thread.abort_on_exception = false
|
||||
|
||||
@@ -8,5 +8,5 @@ If you didn't request this, you can ignore this e-mail.
|
||||
|
||||
Thanks,
|
||||
|
||||
<%= Postal.smtp_from_name %>
|
||||
<%= Postal.smtp_from_address %>
|
||||
<%= Postal::Config.smtp.from_name %>
|
||||
<%= Postal::Config.smtp.from_address %>
|
||||
|
||||
@@ -13,5 +13,5 @@ You can view more information about this server at:
|
||||
|
||||
Thanks,
|
||||
|
||||
<%= Postal.smtp_from_name %>
|
||||
<%= Postal.smtp_from_address %>
|
||||
<%= Postal::Config.smtp.from_name %>
|
||||
<%= Postal::Config.smtp.from_address %>
|
||||
|
||||
@@ -13,5 +13,5 @@ You can view more information about this server at:
|
||||
|
||||
Thanks,
|
||||
|
||||
<%= Postal.smtp_from_name %>
|
||||
<%= Postal.smtp_from_address %>
|
||||
<%= Postal::Config.smtp.from_name %>
|
||||
<%= Postal::Config.smtp.from_address %>
|
||||
|
||||
@@ -8,5 +8,5 @@ Reason: <%= @server.actual_suspension_reason %>
|
||||
|
||||
Thanks,
|
||||
|
||||
<%= Postal.smtp_from_name %>
|
||||
<%= Postal.smtp_from_address %>
|
||||
<%= Postal::Config.smtp.from_name %>
|
||||
<%= Postal::Config.smtp.from_address %>
|
||||
|
||||
@@ -10,5 +10,5 @@ If you don't agree, just ignore this e-mail.
|
||||
|
||||
Thanks,
|
||||
|
||||
<%= Postal.smtp_from_name %>
|
||||
<%= Postal.smtp_from_address %>
|
||||
<%= Postal::Config.smtp.from_name %>
|
||||
<%= Postal::Config.smtp.from_address %>
|
||||
|
||||
@@ -43,7 +43,7 @@
|
||||
%p.pageContent__text
|
||||
You need to add a TXT record at the apex/root of your domain (@) with the following
|
||||
content. If you already send mail from another service, you may just need to add
|
||||
<b>include:#{Postal.config.dns.spf_include}</b> to your existing record.
|
||||
<b>include:#{Postal::Config.dns.spf_include}</b> to your existing record.
|
||||
%pre.codeBlock.u-margin= @domain.spf_record
|
||||
|
||||
%h3.pageContent__subTitle DKIM Record
|
||||
@@ -78,7 +78,7 @@
|
||||
%p.pageContent__text
|
||||
This is optional but we recommend adding this to improve deliverability. You should add
|
||||
a <b>CNAME</b> record at <b>#{@domain.return_path_domain}</b> to point to the hostname below.
|
||||
%pre.codeBlock.u-margin= Postal.config.dns.return_path
|
||||
%pre.codeBlock.u-margin= Postal::Config.dns.return_path_domain
|
||||
|
||||
|
||||
%h3.pageContent__subTitle MX Records
|
||||
@@ -99,6 +99,4 @@
|
||||
If you wish to receive incoming e-mail for this domain, you need to add the following MX records
|
||||
to the domain. You don't have to do this and we'll only tell you if they're set up or not. Both
|
||||
records should be priority <b>10</b>.
|
||||
%pre.codeBlock.u-margin= Postal.config.dns.mx_records.join("\n")
|
||||
|
||||
|
||||
%pre.codeBlock.u-margin= Postal::Config.dns.mx_records.join("\n")
|
||||
|
||||
@@ -36,6 +36,5 @@
|
||||
%dl.pageContent__definitions
|
||||
%dt MX Records
|
||||
%dd
|
||||
- for mx in Postal.config.dns.mx_records
|
||||
- for mx in Postal::Config.dns.mx_records
|
||||
%p.pageContent__definitionCode= mx
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
Mail servers can be enabled to send mail from any domain by the administrator.
|
||||
%li
|
||||
If a message cannot be delivered, the system will not send you a bounce message but dispatch a webhook (if you set one up).
|
||||
If a message delivery fails but can be retried, the system will try #{Postal.config.general.maximum_delivery_attempts} times to deliver it before giving up.
|
||||
If a message delivery fails but can be retried, the system will try #{Postal::Config.postal.default_maximum_delivery_attempts} times to deliver it before giving up.
|
||||
.u-margin
|
||||
%h2.pageContent__subTitle Sending using SMTP
|
||||
%p.pageContent__text
|
||||
@@ -27,10 +27,10 @@
|
||||
%dl.pageContent__definitions
|
||||
%dt SMTP Server Address
|
||||
%dd
|
||||
%p.pageContent__definitionCode= Postal.config.dns.smtp_server_hostname
|
||||
%p.pageContent__definitionCode= Postal::Config.postal.smtp_hostname
|
||||
%dt Port
|
||||
%dd
|
||||
%p.pageContent__definitionCode= Postal.config.smtp_server.port
|
||||
%p.pageContent__definitionCode= Postal::Config.smtp_server.default_port
|
||||
%p.pageContent__definitionText
|
||||
The SMTP service supports STARTTLS if you wish to send messages securely. Be aware that security
|
||||
cannot guaranteed all the way to their final destination.
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
- else
|
||||
%p.pageContent__intro.u-margin
|
||||
When messages cannot be delivered, addresses are added to the suppression list which stops
|
||||
future messages to the same recipient being sent through. Recipients are removed from the list after #{Postal.config.general.suppression_list_removal_delay} days.
|
||||
future messages to the same recipient being sent through. Recipients are removed from the list after #{Postal::Config.postal.default_suppression_list_automatic_removal_days} days.
|
||||
%ul.suppressionList
|
||||
- for suppression in @suppressions[:records]
|
||||
%li.suppressionList__item
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
= f.select :domain_id, domain_options_for_select(@server, @track_domain.domain), {}, :class => 'input input--select routeNameInput__domain', :disabled => @track_domain.persisted?
|
||||
%p.fieldSet__text
|
||||
This is the domain that requests for tracked links will be directed through when you use click tracking. We recommend using something like
|
||||
<b>click.yourdomain.com</b>. You will need to a CNAME record to point to <b>#{Postal.config.dns.track_domain}</b> for this once you've added it.
|
||||
<b>click.yourdomain.com</b>. You will need to a CNAME record to point to <b>#{Postal::Config.dns.track_domain}</b> for this once you've added it.
|
||||
|
||||
.fieldSet__field
|
||||
= f.label :ssl_enabled, :class => 'fieldSet__label'
|
||||
@@ -47,4 +47,3 @@
|
||||
|
||||
.fieldSetSubmit.buttonSet
|
||||
= f.submit @track_domain.new_record? ? "Create Track Domain" : "Save Track Domain", :class => 'button button--positive js-form-submit'
|
||||
|
||||
|
||||
@@ -3,5 +3,4 @@
|
||||
# This file is used by Rack-based servers to start the application.
|
||||
|
||||
require_relative "config/environment"
|
||||
$0 = "[postal] #{ENV.fetch('PROC_NAME', nil)}"
|
||||
run Rails.application
|
||||
|
||||
@@ -38,9 +38,9 @@ module Postal
|
||||
require "tracking_middleware"
|
||||
config.middleware.insert_before ActionDispatch::HostAuthorization, TrackingMiddleware
|
||||
|
||||
config.hosts << Postal.config.web.host
|
||||
config.hosts << Postal::Config.postal.web_hostname
|
||||
|
||||
if Postal.config.logging.rails_log == false
|
||||
unless Postal::Config.logging.rails_log_enabled?
|
||||
config.logger = Logger.new("/dev/null")
|
||||
end
|
||||
|
||||
|
||||
@@ -5,6 +5,5 @@ ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__)
|
||||
require "bundler/setup" # Set up gems listed in the Gemfile.
|
||||
|
||||
require_relative "../lib/postal/config"
|
||||
Postal.check_config!
|
||||
|
||||
ENV["RAILS_ENV"] = Postal.config.rails&.environment || "development"
|
||||
ENV["RAILS_ENV"] = Postal::Config.rails.environment || "development"
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
default: &default
|
||||
adapter: mysql2
|
||||
reconnect: true
|
||||
encoding: <%= Postal.config.main_db.encoding %>
|
||||
pool: <%= Postal.config.main_db.pool_size %>
|
||||
username: <%= Postal.config.main_db.username %>
|
||||
password: <%= Postal.config.main_db.password %>
|
||||
host: <%= Postal.config.main_db.host %>
|
||||
port: <%= Postal.config.main_db.port %>
|
||||
database: <%= Postal.config.main_db.database %>
|
||||
encoding: "<%= Postal::Config.main_db.encoding %>"
|
||||
pool: <%= Postal::Config.main_db.pool_size %>
|
||||
username: "<%= Postal::Config.main_db.username %>"
|
||||
password: "<%= Postal::Config.main_db.password %>"
|
||||
host: "<%= Postal::Config.main_db.host %>"
|
||||
port: <%= Postal::Config.main_db.port %>
|
||||
database: "<%= Postal::Config.main_db.database %>"
|
||||
|
||||
development:
|
||||
<<: *default
|
||||
|
||||
@@ -1,17 +1,13 @@
|
||||
web:
|
||||
host: postal.example.dev
|
||||
protocol: https
|
||||
# This is an example Postal configuration file for use in
|
||||
# development environments. For a production example, see
|
||||
# the https://github.com/postalserver/install repository.
|
||||
|
||||
web_server:
|
||||
bind_address: 0.0.0.0
|
||||
port: 4010
|
||||
version: 2
|
||||
|
||||
smtp_server:
|
||||
port: 2525
|
||||
|
||||
logging:
|
||||
rails_log: true
|
||||
stdout: true
|
||||
postal:
|
||||
web_hostname: postal.example.com
|
||||
web_protocol: https
|
||||
smtp_hostname: postal.example.com
|
||||
|
||||
main_db:
|
||||
host: 127.0.0.1
|
||||
@@ -25,13 +21,9 @@ message_db:
|
||||
password:
|
||||
prefix: postal
|
||||
|
||||
smtp:
|
||||
host: 127.0.0.1
|
||||
port: 2525
|
||||
username:
|
||||
password:
|
||||
from_name: Postal
|
||||
from_address: postal@yourdomain.com
|
||||
logging:
|
||||
rails_log_enabled: true
|
||||
highlighting_enabled: true
|
||||
|
||||
rails:
|
||||
environment: development
|
||||
25
config/examples/test.yml
Normal file
25
config/examples/test.yml
Normal file
@@ -0,0 +1,25 @@
|
||||
# This is an example Postal configuration file for use in
|
||||
# test environments. For a production example, see
|
||||
# the https://github.com/postalserver/install repository.
|
||||
|
||||
version: 2
|
||||
|
||||
main_db:
|
||||
host: 127.0.0.1
|
||||
username: root
|
||||
password:
|
||||
database: postal-test
|
||||
|
||||
message_db:
|
||||
host: 127.0.0.1
|
||||
username: root
|
||||
password:
|
||||
prefix: postal-test
|
||||
|
||||
logging:
|
||||
enabled: false
|
||||
rails_log_enabled: false
|
||||
|
||||
rails:
|
||||
environment: test
|
||||
secret_key: 7f27856d26e864bafd49d0df37ad3d1339086e86ef0447e0f1814dde5277452fea97dab9e3aad6dfa11bfe359c82ce302d97bf1e58f6103c4408e4fbad4eeccf
|
||||
@@ -1,7 +1,7 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
if Postal.config.rails&.secret_key
|
||||
Rails.application.secrets.secret_key_base = Postal.config.rails.secret_key
|
||||
if Postal::Config.rails.secret_key
|
||||
Rails.application.secrets.secret_key_base = Postal::Config.rails.secret_key
|
||||
else
|
||||
warn "No secret key was specified in the Postal config file. Using one for just this session"
|
||||
Rails.application.secrets.secret_key_base = SecureRandom.hex(128)
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
|
||||
require "postal/config"
|
||||
|
||||
if Postal.config.general&.exception_url
|
||||
if Postal::Config.logging.sentry_dsn
|
||||
Sentry.init do |config|
|
||||
config.dsn = Postal.config.general.exception_url
|
||||
config.dsn = Postal::Config.logging.sentry_dsn
|
||||
end
|
||||
end
|
||||
|
||||
@@ -2,9 +2,10 @@
|
||||
|
||||
require "postal/config"
|
||||
|
||||
if Postal.config&.smtp
|
||||
# TODO: by default, we should just send mail through the local Postal
|
||||
# installation rather than having to actually configure an SMTP server.
|
||||
ActionMailer::Base.delivery_method = :smtp
|
||||
ActionMailer::Base.smtp_settings = { address: Postal.config.smtp.host, user_name: Postal.config.smtp.username, password: Postal.config.smtp.password, port: Postal.config.smtp.port || 25 }
|
||||
end
|
||||
ActionMailer::Base.smtp_settings = {
|
||||
address: Postal::Config.smtp.host,
|
||||
user_name: Postal::Config.smtp.username,
|
||||
password: Postal::Config.smtp.password,
|
||||
port: Postal::Config.smtp.port
|
||||
}
|
||||
|
||||
@@ -1,111 +0,0 @@
|
||||
# These are the default configuration options that will be used if they aren't overriden
|
||||
# in your postal.yml configuration file. No changes should be made to this file for
|
||||
# your installation.
|
||||
|
||||
# You can refer to this for a complete listing all available configuration options.
|
||||
|
||||
web:
|
||||
host: <%= ENV.fetch('POSTAL_HOST', 'postal.example.com') %>
|
||||
protocol: <%= ENV.fetch('POSTAL_PROTOCOL', 'https') %>
|
||||
|
||||
general:
|
||||
use_ip_pools: <%= ENV.fetch('POSTAL_USE_IP_POOLS', 'false') %>
|
||||
exception_url: <%= ENV.fetch('POSTAL_EXCEPTION_URL', '') %>
|
||||
maximum_delivery_attempts: <%= ENV.fetch('POSTAL_MAXIMUM_DELIVERY_ATTEMPTS', '18') %>
|
||||
maximum_hold_expiry_days: <%= ENV.fetch('POSTAL_MAXIMUM_HOLD_EXPIRY_DAYS', '7') %>
|
||||
suppression_list_removal_delay: <%= ENV.fetch('POSTAL_SUPPRESSION_LIST_REMOVAL_DELAY', '30') %>
|
||||
use_local_ns_for_domains: <%= ENV.fetch('POSTAL_USE_LOCAL_NS_FOR_DOMAINS', 'false') %>
|
||||
default_spam_threshold: <%= ENV.fetch('POSTAL_DEFAULT_SPAM_THRESHOLD', '5.0') %>
|
||||
default_spam_failure_threshold: <%= ENV.fetch('POSTAL_DEFAULT_SPAM_FAILURE_THRESHOLD', '20.0') %>
|
||||
use_resent_sender_header: <%= ENV.fetch('POSTAL_USE_RESENT_SENDER_HEADER', 'true') %>
|
||||
|
||||
web_server:
|
||||
bind_address: <%= ENV.fetch('WEB_SERVER_BIND_ADDRESS', '0.0.0.0') %>
|
||||
port: <%= ENV.fetch('WEB_SERVER_PORT', '5000') %>
|
||||
max_threads: <%= ENV.fetch('WEB_SERVER_MAX_THREADS', '5') %>
|
||||
|
||||
main_db:
|
||||
host: <%= ENV.fetch('MAIN_DB_HOST', '127.0.0.1') %>
|
||||
port: <%= ENV.fetch('MAIN_DB_PORT', '3306') %>
|
||||
username: <%= ENV.fetch('MAIN_DB_USERNAME', 'postal') %>
|
||||
password: <%= ENV.fetch('MAIN_DB_PASSWORD', '') %>
|
||||
database: <%= ENV.fetch('MAIN_DB_DATABASE', 'postal') %>
|
||||
pool_size: <%= ENV.fetch('MAIN_DB_POOL_SIZE', '5') %>
|
||||
encoding: <%= ENV.fetch('MAIN_DB_ENCODING', 'utf8mb4') %>
|
||||
|
||||
message_db:
|
||||
host: <%= ENV.fetch('MESSAGE_DB_HOST', '127.0.0.1') %>
|
||||
port: <%= ENV.fetch('MESSAGE_DB_PORT', '3306') %>
|
||||
username: <%= ENV.fetch('MESSAGE_DB_USERNAME', 'postal') %>
|
||||
password: <%= ENV.fetch('MESSAGE_DB_PASSWORD', '') %>
|
||||
prefix: <%= ENV.fetch('MESSAGE_DB_PREFIX', 'postal') %>
|
||||
|
||||
logging:
|
||||
rails_log: <%= ENV.fetch('LOGGING_RAILS_LOG', 'false') %>
|
||||
graylog:
|
||||
host: <%= ENV.fetch('GRAYLOG_HOST', '') %>
|
||||
port: <%= ENV.fetch('GRAYLOG_PORT', '12201') %>
|
||||
facility: <%= ENV.fetch('GRAYLOG_FACILITY', 'postal') %>
|
||||
|
||||
smtp_server:
|
||||
port: <%= ENV.fetch('SMTP_SERVER_PORT', '25') %>
|
||||
bind_address: "<%= ENV.fetch('SMTP_SERVER_BIND_ADDRESS', '::') %>"
|
||||
tls_enabled: <%= ENV.fetch('SMTP_SERVER_TLS_ENABLED', 'false') %>
|
||||
tls_certificate_path: <%= ENV.fetch('SMTP_SERVER_TLS_CERTIFICATE_PATH', '') %> # Defaults to config/smtp.cert
|
||||
tls_private_key_path: <%= ENV.fetch('SMTP_SERVER_TLS_PRIVATE_KEY_PATH', '') %> # Defaults to config/smtp.key
|
||||
tls_ciphers: <%= ENV.fetch('SMTP_SERVER_TLS_CIPHERS', '') %>
|
||||
ssl_version: <%= ENV.fetch('SMTP_SERVER_SSL_VERSION', 'SSLv23') %>
|
||||
proxy_protocol: <%= ENV.fetch('SMTP_SERVER_PROXY_PROTOCOL', 'false') %>
|
||||
log_connect: <%= ENV.fetch('SMTP_SERVER_LOG_CONNECT', 'false') %>
|
||||
max_message_size: <%= ENV.fetch('SMTP_SERVER_MAX_MESSAGE_SIZE', '14') %> # size in Megabytes
|
||||
|
||||
smtp_relays:
|
||||
- hostname:
|
||||
port: 25
|
||||
ssl_mode: Auto
|
||||
|
||||
dns:
|
||||
mx_records: <%= ENV.fetch('DNS_MX_RECORDS', 'mx.postal.example.com'.split(',').inspect) %>
|
||||
smtp_server_hostname: <%= ENV.fetch('DNS_SMTP_SERVER_HOSTNAME', 'postal.example.com') %>
|
||||
spf_include: <%= ENV.fetch('DNS_SPF_INCLUDE', 'spf.postal.example.com') %>
|
||||
return_path: <%= ENV.fetch('DNS_RETURN_PATH', 'rp.postal.example.com') %>
|
||||
route_domain: <%= ENV.fetch('DNS_ROUTE_DOMAIN', 'routes.postal.example.com') %>
|
||||
track_domain: <%= ENV.fetch('DNS_TRACK_DOMAIN', 'track.postal.example.com') %>
|
||||
helo_hostname: <%= ENV.fetch('DNS_HELO_HOSTNAME', '') %> # By default, this will be the same as the `smtp_server_hostname`
|
||||
dkim_identifier: <%= ENV.fetch('DNS_DKIM_IDENTIFIER', 'postal') %>
|
||||
domain_verify_prefix: <%= ENV.fetch('DNS_DOMAIN_VERIFY_PREFIX', 'postal-verification') %>
|
||||
custom_return_path_prefix: <%= ENV.fetch('DNS_CUSTOM_RETURN_PATH_PREFIX', 'psrp') %>
|
||||
|
||||
smtp:
|
||||
host: <%= ENV.fetch('SMTP_HOST', '127.0.0.1') %>
|
||||
port: <%= ENV.fetch('SMTP_PORT', '25') %>
|
||||
username: <%= ENV.fetch('SMTP_USERNAME', '') %> # Complete when Postal is running and you can
|
||||
password: <%= ENV.fetch('SMTP_PASSWORD', '') %> # generate the credentials within the interface.
|
||||
from_name: <%= ENV.fetch('SMTP_FROM_NAME', 'Postal') %>
|
||||
from_address: <%= ENV.fetch('SMTP_FROM_ADDRESS', 'postal@example.com') %>
|
||||
|
||||
rails:
|
||||
environment: <%= ENV.fetch('RAILS_ENV', 'production') %>
|
||||
secret_key: <%= ENV.fetch('RAILS_SECRET_KEY_BASE', '') %>
|
||||
|
||||
rspamd:
|
||||
enabled: <%= ENV.fetch('RSPAMD_ENABLED', 'false') %>
|
||||
host: <%= ENV.fetch('RSPAMD_HOST', '127.0.0.1') %>
|
||||
port: <%= ENV.fetch('RSPAMD_PORT', '11334') %>
|
||||
ssl: <%= ENV.fetch('RSPAMD_SSL', 'false') %>
|
||||
password: <%= ENV.fetch('RSPAMD_PASSWORD', '') %>
|
||||
flags: <%= ENV.fetch('RSPAMD_FLAGS', '') %>
|
||||
|
||||
spamd:
|
||||
enabled: <%= ENV.fetch('SPAMD_ENABLED', 'false') %>
|
||||
host: <%= ENV.fetch('SPAMD_HOST', '127.0.0.1') %>
|
||||
port: <%= ENV.fetch('SPAMD_PORT', '783') %>
|
||||
|
||||
clamav:
|
||||
enabled: <%= ENV.fetch('CLAMAV_ENABLED', 'false') %>
|
||||
host: <%= ENV.fetch('CLAMAV_HOST', '127.0.0.1') %>
|
||||
port: <%= ENV.fetch('CLAMAV_PORT', '2000') %>
|
||||
|
||||
smtp_client:
|
||||
open_timeout: <%= ENV.fetch('SMTP_CLIENT_OPEN_TIMEOUT', '30') %>
|
||||
read_timeout: <%= ENV.fetch('SMTP_CLIENT_READ_TIMEOUT', '60') %>
|
||||
@@ -1,11 +1,12 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require_relative "../lib/postal/config"
|
||||
threads_count = Postal.config.web_server&.max_threads&.to_i || 5
|
||||
|
||||
threads_count = Postal::Config.web_server.max_threads
|
||||
threads threads_count, threads_count
|
||||
bind_address = Postal.config.web_server&.bind_address || "127.0.0.1"
|
||||
bind_port = Postal.config.web_server&.port&.to_i || ENV["PORT"] || 5000
|
||||
bind_address = ENV.fetch("BIND_ADDRESS", Postal::Config.web_server.default_bind_address)
|
||||
bind_port = ENV.fetch("PORT", Postal::Config.web_server.default_port)
|
||||
bind "tcp://#{bind_address}:#{bind_port}"
|
||||
environment Postal.config.rails&.environment || "development"
|
||||
environment Postal::Config.rails.environment || "development"
|
||||
prune_bundler
|
||||
quiet false
|
||||
|
||||
31
doc/config/configuration.md
Normal file
31
doc/config/configuration.md
Normal file
@@ -0,0 +1,31 @@
|
||||
# Configuring Postal
|
||||
|
||||
Postal can be configured in two ways: using a YAML-based configuration file or through environment variables.
|
||||
|
||||
If you choose to use environment variables, you don't need to provide a config file. A full list of environment variables is available in the `environment-variables.md` file in this directory.
|
||||
|
||||
To use a configuration file, the `POSTAL_CONFIG_FILE_PATH` environment variable will dictate where Postal will look for the config file. An example YAML file containing all available configuration is provided in the `yaml.yml` file in this directory. Remember to include the `version: 2` key/value in your configuration file.
|
||||
|
||||
## Development
|
||||
|
||||
When developing with Postal, you can configure the application by placing a configuration file in `config/postal/postal.yml`. Alternatively, you can use environment variables by placing configuration in `.env` in the root of the application.
|
||||
|
||||
### Running tests
|
||||
|
||||
By default, tests will use the `config/postal/postal.test.yml` configuration file and the `.env.test` environment file.
|
||||
|
||||
## Containers
|
||||
|
||||
Within a container, Postal will for a config file in `/config/postal.yml` unless overriden by the `POSTAL_CONFIG_FILE_PATH` environment variable.
|
||||
|
||||
## Ports & Bind Addresses
|
||||
|
||||
The web & SMTP server listen on ports and addresses. The defaults for these can be set through configuration however, if you're running multiple instances of these on a single host you will need to specify different ports for each one.
|
||||
|
||||
You can use the `PORT` and `BIND_ADDRESS` environment variables to provide instance-specific values for these processes.
|
||||
|
||||
Additionally, `HEALTH_SERVER_PORT` and `HEALTH_SERVER_BIND_ADDRESS` can be used to set the port/address to use for running the health server alongside other processes.
|
||||
|
||||
## Legacy configuration
|
||||
|
||||
Legacy configuration files from Postal v1 and v2 are still supported. If you wish to use a new configuration option that is not available in the legacy format, you will need to upgrade the file to version 2.
|
||||
90
doc/config/environment-variables.md
Normal file
90
doc/config/environment-variables.md
Normal file
@@ -0,0 +1,90 @@
|
||||
# Environment Variables
|
||||
|
||||
This document contains all the environment variables which are available for this application.
|
||||
|
||||
| Name | Type | Description | Default |
|
||||
| ---- | ---- | ----------- | ------- |
|
||||
| `POSTAL_WEB_HOSTNAME` | String | The hostname that the Postal web interface runs on | postal.example.com |
|
||||
| `POSTAL_WEB_PROTOCOL` | String | The HTTP protocol to use for the Postal web interface | https |
|
||||
| `POSTAL_SMTP_HOSTNAME` | String | The hostname that the Postal SMTP server runs on | postal.example.com |
|
||||
| `POSTAL_USE_IP_POOLS` | Boolean | Should IP pools be enabled for this installation? | false |
|
||||
| `POSTAL_DEFAULT_MAXIMUM_DELIVERY_ATTEMPTS` | Integer | The maximum number of delivery attempts | 18 |
|
||||
| `POSTAL_DEFAULT_MAXIMUM_HOLD_EXPIRY_DAYS` | Integer | The number of days to hold a message before they will be expired | 7 |
|
||||
| `POSTAL_DEFAULT_SUPPRESSION_LIST_AUTOMATIC_REMOVAL_DAYS` | Integer | The number of days an address will remain in a suppression list before being removed | 30 |
|
||||
| `POSTAL_DEFAULT_SPAM_THRESHOLD` | Integer | The default threshold at which a message should be treated as spam | 5 |
|
||||
| `POSTAL_DEFAULT_SPAM_FAILURE_THRESHOLD` | Integer | The default threshold at which a message should be treated as spam failure | 20 |
|
||||
| `POSTAL_USE_LOCAL_NS_FOR_DOMAIN_VERIFICATION` | Boolean | Domain verification and checking usually checks with a domain's nameserver. Enable this to check with the server's local nameservers. | false |
|
||||
| `POSTAL_USE_RESENT_SENDER_HEADER` | Boolean | Append a Resend-Sender header to all outgoing e-mails | true |
|
||||
| `POSTAL_SIGNING_KEY_PATH` | String | Path to the private key used for signing | config/postal/signing.key |
|
||||
| `POSTAL_SMTP_RELAYS` | Array of strings | An array of SMTP relays in the format of smtp://host:port | |
|
||||
| `WEB_SERVER_DEFAULT_PORT` | Integer | The default port the web server should listen on unless overriden by the PORT environment variable | 5000 |
|
||||
| `WEB_SERVER_DEFAULT_BIND_ADDRESS` | String | The default bind address the web server should listen on unless overriden by the BIND_ADDRESS environment variable | 127.0.0.1 |
|
||||
| `WEB_SERVER_MAX_THREADS` | Integer | The maximum number of threads which can be used by the web server | 5 |
|
||||
| `WORKER_DEFAULT_HEALTH_SERVER_PORT` | Integer | The default port for the worker health server to listen on | 9090 |
|
||||
| `WORKER_DEFAULT_HEALTH_SERVER_BIND_ADDRESS` | String | The default bind address for the worker health server to listen on | 127.0.0.1 |
|
||||
| `MAIN_DB_HOST` | String | Hostname for the main MariaDB server | localhost |
|
||||
| `MAIN_DB_PORT` | Integer | The MariaDB port to connect to | 3306 |
|
||||
| `MAIN_DB_USERNAME` | String | The MariaDB username | postal |
|
||||
| `MAIN_DB_PASSWORD` | String | The MariaDB password | |
|
||||
| `MAIN_DB_DATABASE` | String | The MariaDB database name | postal |
|
||||
| `MAIN_DB_POOL_SIZE` | Integer | The maximum size of the MariaDB connection pool | 5 |
|
||||
| `MAIN_DB_ENCODING` | String | The encoding to use when connecting to the MariaDB database | utf8mb4 |
|
||||
| `MESSAGE_DB_HOST` | String | Hostname for the MariaDB server which stores the mail server databases | localhost |
|
||||
| `MESSAGE_DB_PORT` | Integer | The MariaDB port to connect to | 3306 |
|
||||
| `MESSAGE_DB_USERNAME` | String | The MariaDB username | postal |
|
||||
| `MESSAGE_DB_PASSWORD` | String | The MariaDB password | |
|
||||
| `MESSAGE_DB_DATABASE_NAME_PREFIX` | String | The MariaDB prefix to add to database names | postal |
|
||||
| `LOGGING_RAILS_LOG_ENABLED` | Boolean | Enable the default Rails logger | false |
|
||||
| `LOGGING_SENTRY_DSN` | String | A DSN which should be used to report exceptions to Sentry | |
|
||||
| `LOGGING_ENABLED` | Boolean | Enable the Postal logger to log to STDOUT | true |
|
||||
| `LOGGING_HIGHLIGHTING_ENABLED` | Boolean | Enable highlighting of log lines | false |
|
||||
| `GELF_HOST` | String | GELF-capable host to send logs to | |
|
||||
| `GELF_PORT` | Integer | GELF port to send logs to | 12201 |
|
||||
| `GELF_FACILITY` | String | The facility name to add to all log entries sent to GELF | postal |
|
||||
| `SMTP_SERVER_DEFAULT_PORT` | Integer | The default port the SMTP server should listen on unless overriden by the PORT environment variable | 25 |
|
||||
| `SMTP_SERVER_DEFAULT_BIND_ADDRESS` | String | The default bind address the SMTP server should listen on unless overriden by the BIND_ADDRESS environment variable | 127.0.0.1 |
|
||||
| `SMTP_SERVER_DEFAULT_HEALTH_SERVER_PORT` | Integer | The default port for the SMTP server health server to listen on | 9091 |
|
||||
| `SMTP_SERVER_DEFAULT_HEALTH_SERVER_BIND_ADDRESS` | String | The default bind address for the SMTP server health server to listen on | 127.0.0.1 |
|
||||
| `SMTP_SERVER_TLS_ENABLED` | Boolean | Enable TLS for the SMTP server (requires certificate) | false |
|
||||
| `SMTP_SERVER_TLS_CERTIFICATE_PATH` | String | The path to the SMTP server's TLS certificate | config/postal/smtp.cert |
|
||||
| `SMTP_SERVER_TLS_PRIVATE_KEY_PATH` | String | The path to the SMTP server's TLS private key | config/postal/smtp.key |
|
||||
| `SMTP_SERVER_TLS_CIPHERS` | String | Override ciphers to use for SSL | |
|
||||
| `SMTP_SERVER_SSL_VERSION` | String | The SSL versions which are supported | SSLv23 |
|
||||
| `SMTP_SERVER_PROXY_PROTOCOL` | Boolean | Enable proxy protocol for use behind some load balancers | false |
|
||||
| `SMTP_SERVER_LOG_CONNECTIONS` | Boolean | Enable connection logging | false |
|
||||
| `SMTP_SERVER_MAX_MESSAGE_SIZE` | Integer | The maximum message size to accept from the SMTP server (in MB) | 14 |
|
||||
| `SMTP_SERVER_LOG_IP_ADDRESS_EXCLUSION_MATCHER` | String | A regular expression to use to exclude connections from logging | |
|
||||
| `DNS_MX_RECORDS` | Array of strings | The names of the default MX records | ["mx1.postal.example.com", "mx2.postal.example.com"] |
|
||||
| `DNS_SPF_INCLUDE` | String | The location of the SPF record | spf.postal.example.com |
|
||||
| `DNS_RETURN_PATH_DOMAIN` | String | The return path hostname | rp.postal.example.com |
|
||||
| `DNS_ROUTE_DOMAIN` | String | The domain to use for hosting route-specific addresses | routes.postal.example.com |
|
||||
| `DNS_TRACK_DOMAIN` | String | The CNAME which tracking domains should be pointed to | track.postal.example.com |
|
||||
| `DNS_HELO_HOSTNAME` | String | The hostname to use in HELO/EHLO when connecting to external SMTP servers | |
|
||||
| `DNS_DKIM_IDENTIFIER` | String | The identifier to use for DKIM keys in DNS records | postal |
|
||||
| `DNS_DOMAIN_VERIFY_PREFIX` | String | The prefix to add before TXT record verification string | postal-verification |
|
||||
| `DNS_CUSTOM_RETURN_PATH_PREFIX` | String | The domain to use on external domains which points to the Postal return path domain | psrp |
|
||||
| `SMTP_HOST` | String | The hostname to send application-level e-mails to | 127.0.0.1 |
|
||||
| `SMTP_PORT` | Integer | The port number to send application-level e-mails to | 25 |
|
||||
| `SMTP_USERNAME` | String | The username to use when authentication to the SMTP server | |
|
||||
| `SMTP_PASSWORD` | String | The password to use when authentication to the SMTP server | |
|
||||
| `SMTP_FROM_NAME` | String | The name to use as the from name outgoing emails from Postal | Postal |
|
||||
| `SMTP_FROM_ADDRESS` | String | The e-mail to use as the from address outgoing emails from Postal | postal@example.com |
|
||||
| `RAILS_ENVIRONMENT` | String | The Rails environment to run the application in | production |
|
||||
| `RAILS_SECRET_KEY` | String | The secret key used to sign and encrypt cookies and session data in the application | |
|
||||
| `RSPAMD_ENABLED` | Boolean | Enable rspamd for message inspection | false |
|
||||
| `RSPAMD_HOST` | String | The hostname of the rspamd server | 127.0.0.1 |
|
||||
| `RSPAMD_PORT` | Integer | The port of the rspamd server | 11334 |
|
||||
| `RSPAMD_SSL` | Boolean | Enable SSL for the rspamd connection | false |
|
||||
| `RSPAMD_PASSWORD` | String | The password for the rspamd server | |
|
||||
| `RSPAMD_FLAGS` | String | Any flags for the rspamd server | |
|
||||
| `SPAMD_ENABLED` | Boolean | Enable SpamAssassin for message inspection | false |
|
||||
| `SPAMD_HOST` | String | The hostname for the SpamAssassin server | 127.0.0.1 |
|
||||
| `SPAMD_PORT` | Integer | The port of the SpamAssassin server | 783 |
|
||||
| `CLAMAV_ENABLED` | Boolean | Enable ClamAV for message inspection | false |
|
||||
| `CLAMAV_HOST` | String | The host of the ClamAV server | 127.0.0.1 |
|
||||
| `CLAMAV_PORT` | Integer | The port of the ClamAV server | 2000 |
|
||||
| `SMTP_CLIENT_OPEN_TIMEOUT` | Integer | The open timeout for outgoing SMTP connections | 30 |
|
||||
| `SMTP_CLIENT_READ_TIMEOUT` | Integer | The read timeout for outgoing SMTP connections | 30 |
|
||||
| `MIGRATION_WAITER_ENABLED` | Boolean | Wait for all migrations to run before starting a process | false |
|
||||
| `MIGRATION_WAITER_ATTEMPTS` | Integer | The number of attempts to try waiting for migrations to complete before start | 120 |
|
||||
| `MIGRATION_WAITER_SLEEP_TIME` | Integer | The number of seconds to wait between each migration check | 2 |
|
||||
203
doc/config/yaml.yml
Normal file
203
doc/config/yaml.yml
Normal file
@@ -0,0 +1,203 @@
|
||||
version: 2
|
||||
|
||||
postal:
|
||||
# The hostname that the Postal web interface runs on
|
||||
web_hostname: postal.example.com
|
||||
# The HTTP protocol to use for the Postal web interface
|
||||
web_protocol: https
|
||||
# The hostname that the Postal SMTP server runs on
|
||||
smtp_hostname: postal.example.com
|
||||
# Should IP pools be enabled for this installation?
|
||||
use_ip_pools: false
|
||||
# The maximum number of delivery attempts
|
||||
default_maximum_delivery_attempts: 18
|
||||
# The number of days to hold a message before they will be expired
|
||||
default_maximum_hold_expiry_days: 7
|
||||
# The number of days an address will remain in a suppression list before being removed
|
||||
default_suppression_list_automatic_removal_days: 30
|
||||
# The default threshold at which a message should be treated as spam
|
||||
default_spam_threshold: 5
|
||||
# The default threshold at which a message should be treated as spam failure
|
||||
default_spam_failure_threshold: 20
|
||||
# Domain verification and checking usually checks with a domain's nameserver. Enable this to check with the server's local nameservers.
|
||||
use_local_ns_for_domain_verification: false
|
||||
# Append a Resend-Sender header to all outgoing e-mails
|
||||
use_resent_sender_header: true
|
||||
# Path to the private key used for signing
|
||||
signing_key_path: config/postal/signing.key
|
||||
# An array of SMTP relays in the format of smtp://host:port
|
||||
smtp_relays: []
|
||||
|
||||
web_server:
|
||||
# The default port the web server should listen on unless overriden by the PORT environment variable
|
||||
default_port: 5000
|
||||
# The default bind address the web server should listen on unless overriden by the BIND_ADDRESS environment variable
|
||||
default_bind_address: 127.0.0.1
|
||||
# The maximum number of threads which can be used by the web server
|
||||
max_threads: 5
|
||||
|
||||
worker:
|
||||
# The default port for the worker health server to listen on
|
||||
default_health_server_port: 9090
|
||||
# The default bind address for the worker health server to listen on
|
||||
default_health_server_bind_address: 127.0.0.1
|
||||
|
||||
main_db:
|
||||
# Hostname for the main MariaDB server
|
||||
host: localhost
|
||||
# The MariaDB port to connect to
|
||||
port: 3306
|
||||
# The MariaDB username
|
||||
username: postal
|
||||
# The MariaDB password
|
||||
password:
|
||||
# The MariaDB database name
|
||||
database: postal
|
||||
# The maximum size of the MariaDB connection pool
|
||||
pool_size: 5
|
||||
# The encoding to use when connecting to the MariaDB database
|
||||
encoding: utf8mb4
|
||||
|
||||
message_db:
|
||||
# Hostname for the MariaDB server which stores the mail server databases
|
||||
host: localhost
|
||||
# The MariaDB port to connect to
|
||||
port: 3306
|
||||
# The MariaDB username
|
||||
username: postal
|
||||
# The MariaDB password
|
||||
password:
|
||||
# The MariaDB prefix to add to database names
|
||||
database_name_prefix: postal
|
||||
|
||||
logging:
|
||||
# Enable the default Rails logger
|
||||
rails_log_enabled: false
|
||||
# A DSN which should be used to report exceptions to Sentry
|
||||
sentry_dsn:
|
||||
# Enable the Postal logger to log to STDOUT
|
||||
enabled: true
|
||||
# Enable highlighting of log lines
|
||||
highlighting_enabled: false
|
||||
|
||||
gelf:
|
||||
# GELF-capable host to send logs to
|
||||
host:
|
||||
# GELF port to send logs to
|
||||
port: 12201
|
||||
# The facility name to add to all log entries sent to GELF
|
||||
facility: postal
|
||||
|
||||
smtp_server:
|
||||
# The default port the SMTP server should listen on unless overriden by the PORT environment variable
|
||||
default_port: 25
|
||||
# The default bind address the SMTP server should listen on unless overriden by the BIND_ADDRESS environment variable
|
||||
default_bind_address: 127.0.0.1
|
||||
# The default port for the SMTP server health server to listen on
|
||||
default_health_server_port: 9091
|
||||
# The default bind address for the SMTP server health server to listen on
|
||||
default_health_server_bind_address: 127.0.0.1
|
||||
# Enable TLS for the SMTP server (requires certificate)
|
||||
tls_enabled: false
|
||||
# The path to the SMTP server's TLS certificate
|
||||
tls_certificate_path: config/postal/smtp.cert
|
||||
# The path to the SMTP server's TLS private key
|
||||
tls_private_key_path: config/postal/smtp.key
|
||||
# Override ciphers to use for SSL
|
||||
tls_ciphers:
|
||||
# The SSL versions which are supported
|
||||
ssl_version: SSLv23
|
||||
# Enable proxy protocol for use behind some load balancers
|
||||
proxy_protocol: false
|
||||
# Enable connection logging
|
||||
log_connections: false
|
||||
# The maximum message size to accept from the SMTP server (in MB)
|
||||
max_message_size: 14
|
||||
# A regular expression to use to exclude connections from logging
|
||||
log_ip_address_exclusion_matcher:
|
||||
|
||||
dns:
|
||||
# The names of the default MX records
|
||||
mx_records:
|
||||
- mx1.postal.example.com
|
||||
- mx2.postal.example.com
|
||||
# The location of the SPF record
|
||||
spf_include: spf.postal.example.com
|
||||
# The return path hostname
|
||||
return_path_domain: rp.postal.example.com
|
||||
# The domain to use for hosting route-specific addresses
|
||||
route_domain: routes.postal.example.com
|
||||
# The CNAME which tracking domains should be pointed to
|
||||
track_domain: track.postal.example.com
|
||||
# The hostname to use in HELO/EHLO when connecting to external SMTP servers
|
||||
helo_hostname:
|
||||
# The identifier to use for DKIM keys in DNS records
|
||||
dkim_identifier: postal
|
||||
# The prefix to add before TXT record verification string
|
||||
domain_verify_prefix: postal-verification
|
||||
# The domain to use on external domains which points to the Postal return path domain
|
||||
custom_return_path_prefix: psrp
|
||||
|
||||
smtp:
|
||||
# The hostname to send application-level e-mails to
|
||||
host: 127.0.0.1
|
||||
# The port number to send application-level e-mails to
|
||||
port: 25
|
||||
# The username to use when authentication to the SMTP server
|
||||
username:
|
||||
# The password to use when authentication to the SMTP server
|
||||
password:
|
||||
# The name to use as the from name outgoing emails from Postal
|
||||
from_name: Postal
|
||||
# The e-mail to use as the from address outgoing emails from Postal
|
||||
from_address: postal@example.com
|
||||
|
||||
rails:
|
||||
# The Rails environment to run the application in
|
||||
environment: production
|
||||
# The secret key used to sign and encrypt cookies and session data in the application
|
||||
secret_key:
|
||||
|
||||
rspamd:
|
||||
# Enable rspamd for message inspection
|
||||
enabled: false
|
||||
# The hostname of the rspamd server
|
||||
host: 127.0.0.1
|
||||
# The port of the rspamd server
|
||||
port: 11334
|
||||
# Enable SSL for the rspamd connection
|
||||
ssl: false
|
||||
# The password for the rspamd server
|
||||
password:
|
||||
# Any flags for the rspamd server
|
||||
flags:
|
||||
|
||||
spamd:
|
||||
# Enable SpamAssassin for message inspection
|
||||
enabled: false
|
||||
# The hostname for the SpamAssassin server
|
||||
host: 127.0.0.1
|
||||
# The port of the SpamAssassin server
|
||||
port: 783
|
||||
|
||||
clamav:
|
||||
# Enable ClamAV for message inspection
|
||||
enabled: false
|
||||
# The host of the ClamAV server
|
||||
host: 127.0.0.1
|
||||
# The port of the ClamAV server
|
||||
port: 2000
|
||||
|
||||
smtp_client:
|
||||
# The open timeout for outgoing SMTP connections
|
||||
open_timeout: 30
|
||||
# The read timeout for outgoing SMTP connections
|
||||
read_timeout: 30
|
||||
|
||||
migration_waiter:
|
||||
# Wait for all migrations to run before starting a process
|
||||
enabled: false
|
||||
# The number of attempts to try waiting for migrations to complete before start
|
||||
attempts: 120
|
||||
# The number of seconds to wait between each migration check
|
||||
sleep_time: 2
|
||||
@@ -8,8 +8,14 @@ services:
|
||||
volumes:
|
||||
- "./docker/ci-config:/config"
|
||||
environment:
|
||||
POSTAL_CONFIG_ROOT: /config
|
||||
KATAPULT_CONFIG_FILE: /ci-config.yml
|
||||
POSTAL_SIGNING_KEY_PATH: /config/signing.key
|
||||
MAIN_DB_HOST: mariadb
|
||||
MAIN_DB_USERNAME: root
|
||||
MESSAGE_DB_HOST: mariadb
|
||||
MESSAGE_DB_USERNAME: root
|
||||
LOGGING_ENABLED: "false"
|
||||
RAILS_ENVIRONMENT: test
|
||||
RAILS_LOG_ENABLED: "false"
|
||||
WAIT_FOR_TIMEOUT: 90
|
||||
WAIT_FOR_TARGETS: |-
|
||||
mariadb:3306
|
||||
|
||||
@@ -1,42 +0,0 @@
|
||||
general:
|
||||
use_ip_pools: false
|
||||
|
||||
web_server:
|
||||
bind_address: 127.0.0.1
|
||||
port: 5000
|
||||
|
||||
smtp_server:
|
||||
port: 2525
|
||||
|
||||
main_db:
|
||||
host: mariadb
|
||||
username: root
|
||||
password:
|
||||
database: postal
|
||||
|
||||
message_db:
|
||||
host: mariadb
|
||||
username: root
|
||||
password:
|
||||
prefix: postal
|
||||
|
||||
dns:
|
||||
mx_records:
|
||||
- mx.postal.example.com
|
||||
smtp_server_hostname: postal.example.com
|
||||
spf_include: spf.postal.example.com
|
||||
return_path: rp.postal.example.com
|
||||
route_domain: routes.postal.example.com
|
||||
track_domain: track.postal.example.com
|
||||
|
||||
smtp:
|
||||
host: 127.0.0.1
|
||||
port: 2525
|
||||
username:
|
||||
password:
|
||||
from_name: Postal
|
||||
from_address: postal@yourdomain.com
|
||||
|
||||
rails:
|
||||
secret_key: 5e82ca222ccc8642fb38a8ea4b847408b983b42a497ec16b499ed89ba506a4edcab8ac015b32dbd1ea620119484e0c1dc056f97b4b37cd29146192df5ccd6abec345a0125c424daad12430a4796d108197c746f5eb8e9e1faa36a8ff8d0c5a9cf00191f997c354341fd656ca0ef9d7b40a37ea4b1b1e78fe9030fa9990dbb662
|
||||
environment: test
|
||||
@@ -7,8 +7,8 @@
|
||||
|
||||
class MigrationWaiter
|
||||
|
||||
ATTEMPTS = ENV.fetch("MIGRATION_WAITER_ATTEMPTS", 120)
|
||||
SLEEP_TIME = ENV.fetch("MIGRATION_WAITER_SLEEP_TIME", 2)
|
||||
ATTEMPTS = Postal::Config.migration_waiter.attempts
|
||||
SLEEP_TIME = Postal::Config.migration_waiter.sleep_time
|
||||
|
||||
class << self
|
||||
|
||||
@@ -35,7 +35,7 @@ class MigrationWaiter
|
||||
|
||||
def wait_if_appropriate
|
||||
# Don't wait if not configured
|
||||
return unless ENV.fetch("MIGRATION_WAITER_ENABLED", "false") == "true"
|
||||
return unless Postal::Config.migration_waiter.enabled?
|
||||
|
||||
# Don't wait in the console, rake tasks or rails commands
|
||||
return if console? || rake_task? || rails_command?
|
||||
|
||||
@@ -6,190 +6,119 @@ require "pathname"
|
||||
require "cgi"
|
||||
require "openssl"
|
||||
require "fileutils"
|
||||
require "konfig"
|
||||
require "konfig/sources/environment"
|
||||
require "konfig/sources/yaml"
|
||||
require "dotenv"
|
||||
require "klogger"
|
||||
|
||||
require_relative "error"
|
||||
require_relative "version"
|
||||
require_relative "config_schema"
|
||||
require_relative "legacy_config_source"
|
||||
|
||||
module Postal
|
||||
|
||||
# rubocop:disable Lint/EmptyClass
|
||||
class Config
|
||||
end
|
||||
# rubocop:enable Lint/EmptyClass
|
||||
class << self
|
||||
|
||||
def self.host
|
||||
@host ||= config.web.host || "localhost:5000"
|
||||
end
|
||||
|
||||
def self.protocol
|
||||
@protocol ||= config.web.protocol || "http"
|
||||
end
|
||||
|
||||
def self.host_with_protocol
|
||||
@host_with_protocol ||= "#{protocol}://#{host}"
|
||||
end
|
||||
|
||||
def self.app_root
|
||||
@app_root ||= Pathname.new(File.expand_path("../..", __dir__))
|
||||
end
|
||||
|
||||
def self.config
|
||||
@config ||= begin
|
||||
require "hashie/mash"
|
||||
config = Hashie::Mash.new(defaults)
|
||||
config = config.deep_merge(yaml_config)
|
||||
config.deep_merge(local_yaml_config)
|
||||
end
|
||||
end
|
||||
|
||||
def self.config_root
|
||||
if ENV["POSTAL_CONFIG_ROOT"]
|
||||
@config_root ||= Pathname.new(ENV["POSTAL_CONFIG_ROOT"])
|
||||
else
|
||||
@config_root ||= Pathname.new(File.expand_path("../../config/postal", __dir__))
|
||||
end
|
||||
end
|
||||
|
||||
def self.config_file_path
|
||||
if env == "default"
|
||||
@config_file_path ||= File.join(config_root, "postal.yml")
|
||||
else
|
||||
@config_file_path ||= File.join(config_root, "postal.#{env}.yml")
|
||||
end
|
||||
end
|
||||
|
||||
def self.env
|
||||
@env ||= ENV.fetch("POSTAL_ENV", "default")
|
||||
end
|
||||
|
||||
def self.yaml_config
|
||||
@yaml_config ||= File.exist?(config_file_path) ? YAML.load_file(config_file_path) : {}
|
||||
end
|
||||
|
||||
def self.local_config_file_path
|
||||
@local_config_file_path ||= File.join(config_root, "postal.local.yml")
|
||||
end
|
||||
|
||||
def self.local_yaml_config
|
||||
@local_yaml_config ||= File.exist?(local_config_file_path) ? YAML.load_file(local_config_file_path) : {}
|
||||
end
|
||||
|
||||
def self.defaults_file_path
|
||||
@defaults_file_path ||= app_root.join("config", "postal.defaults.yml")
|
||||
end
|
||||
|
||||
def self.defaults
|
||||
@defaults ||= begin
|
||||
file = File.read(defaults_file_path)
|
||||
yaml = ERB.new(file).result
|
||||
YAML.safe_load(yaml)
|
||||
end
|
||||
end
|
||||
|
||||
# Return a generic logger for use generally throughout Postal.
|
||||
# Return the path to the config file
|
||||
#
|
||||
# @return [Klogger::Logger] A logger instance
|
||||
def self.logger
|
||||
# @return [String]
|
||||
def config_file_path
|
||||
ENV.fetch("POSTAL_CONFIG_FILE_PATH", "config/postal/postal.yml")
|
||||
end
|
||||
|
||||
def initialize_config
|
||||
sources = []
|
||||
|
||||
# Load environment variables to begin with. Any config provided
|
||||
# by an environment variable will override any provided in the
|
||||
# config file.
|
||||
Dotenv.load(".env")
|
||||
sources << Konfig::Sources::Environment.new(ENV)
|
||||
|
||||
# If a config file exists, we need to load that. Config files can
|
||||
# either be legacy (v1) or new (v2). Any file without a 'version'
|
||||
# key is a legacy file whereas new-style config files will include
|
||||
# the 'version: 2' key/value.
|
||||
if File.file?(config_file_path)
|
||||
puts "Loading config from #{config_file_path}"
|
||||
|
||||
config_file = File.read(config_file_path)
|
||||
yaml = YAML.safe_load(config_file)
|
||||
config_version = yaml["version"] || 1
|
||||
case config_version
|
||||
when 1
|
||||
puts "WARNING: Using legacy config file format. Upgrade your postal.yml to use"
|
||||
puts "version 2 of the Postal configuration or configure using environment"
|
||||
puts "variables. See https://postalserver.io/config-v2 for details."
|
||||
sources << LegacyConfigSource.new(yaml)
|
||||
when 2
|
||||
sources << Konfig::Sources::YAML.new(config_file)
|
||||
else
|
||||
raise "Invalid version specified in Postal config file. Must be 1 or 2."
|
||||
end
|
||||
else
|
||||
puts "No configuration file found at #{config_file_path}"
|
||||
puts "Only using environment variables for configuration"
|
||||
end
|
||||
|
||||
# Build configuration with the provided sources.
|
||||
Konfig::Config.build(ConfigSchema, sources: sources)
|
||||
end
|
||||
|
||||
def host_with_protocol
|
||||
@host_with_protocol ||= "#{Config.postal.web_protocol}://#{Config.postal.web_hostname}"
|
||||
end
|
||||
|
||||
def logger
|
||||
@logger ||= begin
|
||||
k = Klogger.new(nil, destination: Rails.env.test? ? "/dev/null" : $stdout, highlight: Rails.env.development?)
|
||||
k.add_destination(graylog_logging_destination) if config.logging&.graylog&.host.present?
|
||||
k = Klogger.new(nil, destination: Config.logging.enabled? ? $stdout : "/dev/null", highlight: Config.logging.highlighting_enabled?)
|
||||
k.add_destination(graylog_logging_destination) if Config.gelf.host.present?
|
||||
k
|
||||
end
|
||||
end
|
||||
|
||||
def self.process_name
|
||||
def process_name
|
||||
@process_name ||= begin
|
||||
string = "host:#{Socket.gethostname} pid:#{Process.pid}"
|
||||
string += " procname:#{ENV['PROC_NAME']}" if ENV["PROC_NAME"]
|
||||
string
|
||||
"host:#{Socket.gethostname} pid:#{Process.pid}"
|
||||
rescue StandardError
|
||||
"pid:#{Process.pid}"
|
||||
end
|
||||
end
|
||||
|
||||
def self.locker_name
|
||||
def locker_name
|
||||
string = process_name.dup
|
||||
string += " job:#{Thread.current[:job_id]}" if Thread.current[:job_id]
|
||||
string += " thread:#{Thread.current.native_thread_id}"
|
||||
string
|
||||
end
|
||||
|
||||
def self.locker_name_with_suffix(suffix)
|
||||
def locker_name_with_suffix(suffix)
|
||||
"#{locker_name} #{suffix}"
|
||||
end
|
||||
|
||||
def self.smtp_from_name
|
||||
config.smtp&.from_name || "Postal"
|
||||
def signing_key
|
||||
@signing_key ||= OpenSSL::PKey::RSA.new(File.read(Config.postal.signing_key_path))
|
||||
end
|
||||
|
||||
def self.smtp_from_address
|
||||
config.smtp&.from_address || "postal@example.com"
|
||||
end
|
||||
|
||||
def self.smtp_private_key_path
|
||||
config.smtp_server.tls_private_key_path || config_root.join("smtp.key")
|
||||
end
|
||||
|
||||
def self.smtp_private_key
|
||||
@smtp_private_key ||= OpenSSL::PKey.read(File.read(smtp_private_key_path))
|
||||
end
|
||||
|
||||
def self.smtp_certificate_path
|
||||
config.smtp_server.tls_certificate_path || config_root.join("smtp.cert")
|
||||
end
|
||||
|
||||
def self.smtp_certificate_data
|
||||
@smtp_certificate_data ||= File.read(smtp_certificate_path)
|
||||
end
|
||||
|
||||
def self.smtp_certificates
|
||||
@smtp_certificates ||= begin
|
||||
certs = smtp_certificate_data.scan(/-----BEGIN CERTIFICATE-----.+?-----END CERTIFICATE-----/m)
|
||||
certs.map do |c|
|
||||
OpenSSL::X509::Certificate.new(c)
|
||||
end.freeze
|
||||
end
|
||||
end
|
||||
|
||||
def self.signing_key_path
|
||||
ENV.fetch("POSTAL_SIGNING_KEY_PATH") { config_root.join("signing.key") }
|
||||
end
|
||||
|
||||
def self.signing_key
|
||||
@signing_key ||= OpenSSL::PKey::RSA.new(File.read(signing_key_path))
|
||||
end
|
||||
|
||||
def self.rp_dkim_dns_record
|
||||
def rp_dkim_dns_record
|
||||
public_key = signing_key.public_key.to_s.gsub(/-+[A-Z ]+-+\n/, "").gsub(/\n/, "")
|
||||
"v=DKIM1; t=s; h=sha256; p=#{public_key};"
|
||||
end
|
||||
|
||||
class ConfigError < Postal::Error
|
||||
def ip_pools?
|
||||
Config.postal.use_ip_pools?
|
||||
end
|
||||
|
||||
def self.check_config!
|
||||
return if ENV["POSTAL_SKIP_CONFIG_CHECK"].to_i == 1
|
||||
|
||||
unless File.exist?(config_file_path)
|
||||
raise ConfigError, "No config found at #{config_file_path}"
|
||||
end
|
||||
|
||||
return if File.exist?(signing_key_path)
|
||||
|
||||
raise ConfigError, "No signing key found at #{signing_key_path}"
|
||||
end
|
||||
|
||||
def self.ip_pools?
|
||||
config.general.use_ip_pools?
|
||||
end
|
||||
|
||||
def self.graylog_logging_destination
|
||||
def graylog_logging_destination
|
||||
@graylog_logging_destination ||= begin
|
||||
notifier = GELF::Notifier.new(config.logging.graylog.host, config.logging.graylog.port, "WAN")
|
||||
notifier = GELF::Notifier.new(Config.gelf.host, Config.gelf.port, "WAN")
|
||||
proc do |_logger, payload, group_ids|
|
||||
short_message = payload.delete(:message) || "[message missing]"
|
||||
notifier.notify!(short_message: short_message, **{
|
||||
facility: config.logging.graylog.facility,
|
||||
_environment: Rails.env.to_s,
|
||||
facility: Config.gelf.facility,
|
||||
_environment: Config.rails.environment,
|
||||
_version: Postal::VERSION.to_s,
|
||||
_group_ids: group_ids.join(" ")
|
||||
}.merge(payload.transform_keys { |k| "_#{k}".to_sym }.transform_values(&:to_s)))
|
||||
@@ -198,3 +127,7 @@ module Postal
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
Config = initialize_config
|
||||
|
||||
end
|
||||
|
||||
464
lib/postal/config_schema.rb
Normal file
464
lib/postal/config_schema.rb
Normal file
@@ -0,0 +1,464 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "uri"
|
||||
|
||||
module Postal
|
||||
|
||||
# REMEMBER: If you change the schema, remember to regenerate the configuration docs
|
||||
# using the rake command below:
|
||||
#
|
||||
# rake postal:generate_config_docs
|
||||
|
||||
ConfigSchema = Konfig::Schema.draw do
|
||||
group :postal do
|
||||
string :web_hostname do
|
||||
description "The hostname that the Postal web interface runs on"
|
||||
default "postal.example.com"
|
||||
end
|
||||
|
||||
string :web_protocol do
|
||||
description "The HTTP protocol to use for the Postal web interface"
|
||||
default "https"
|
||||
end
|
||||
|
||||
string :smtp_hostname do
|
||||
description "The hostname that the Postal SMTP server runs on"
|
||||
default "postal.example.com"
|
||||
end
|
||||
|
||||
boolean :use_ip_pools do
|
||||
description "Should IP pools be enabled for this installation?"
|
||||
default false
|
||||
end
|
||||
|
||||
integer :default_maximum_delivery_attempts do
|
||||
description "The maximum number of delivery attempts"
|
||||
default 18
|
||||
end
|
||||
|
||||
integer :default_maximum_hold_expiry_days do
|
||||
description "The number of days to hold a message before they will be expired"
|
||||
default 7
|
||||
end
|
||||
|
||||
integer :default_suppression_list_automatic_removal_days do
|
||||
description "The number of days an address will remain in a suppression list before being removed"
|
||||
default 30
|
||||
end
|
||||
|
||||
integer :default_spam_threshold do
|
||||
description "The default threshold at which a message should be treated as spam"
|
||||
default 5
|
||||
end
|
||||
|
||||
integer :default_spam_failure_threshold do
|
||||
description "The default threshold at which a message should be treated as spam failure"
|
||||
default 20
|
||||
end
|
||||
|
||||
boolean :use_local_ns_for_domain_verification do
|
||||
description "Domain verification and checking usually checks with a domain's nameserver. Enable this to check with the server's local nameservers."
|
||||
default false
|
||||
end
|
||||
|
||||
boolean :use_resent_sender_header do
|
||||
description "Append a Resend-Sender header to all outgoing e-mails"
|
||||
default true
|
||||
end
|
||||
|
||||
string :signing_key_path do
|
||||
description "Path to the private key used for signing"
|
||||
default "config/postal/signing.key"
|
||||
end
|
||||
|
||||
string :smtp_relays do
|
||||
array
|
||||
description "An array of SMTP relays in the format of smtp://host:port"
|
||||
transform do |value|
|
||||
uri = URI.parse(value)
|
||||
query = uri.query ? CGI.parse(uri.query) : {}
|
||||
{
|
||||
host: uri.host,
|
||||
port: uri.port || 25,
|
||||
ssl_mode: query["ssl_mode"]&.first || "Auto"
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
group :web_server do
|
||||
integer :default_port do
|
||||
description "The default port the web server should listen on unless overriden by the PORT environment variable"
|
||||
default 5000
|
||||
end
|
||||
|
||||
string :default_bind_address do
|
||||
description "The default bind address the web server should listen on unless overriden by the BIND_ADDRESS environment variable"
|
||||
default "127.0.0.1"
|
||||
end
|
||||
|
||||
integer :max_threads do
|
||||
description "The maximum number of threads which can be used by the web server"
|
||||
default 5
|
||||
end
|
||||
end
|
||||
|
||||
group :worker do
|
||||
integer :default_health_server_port do
|
||||
description "The default port for the worker health server to listen on"
|
||||
default 9090
|
||||
end
|
||||
|
||||
string :default_health_server_bind_address do
|
||||
description "The default bind address for the worker health server to listen on"
|
||||
default "127.0.0.1"
|
||||
end
|
||||
end
|
||||
|
||||
group :main_db do
|
||||
string :host do
|
||||
description "Hostname for the main MariaDB server"
|
||||
default "localhost"
|
||||
end
|
||||
|
||||
integer :port do
|
||||
description "The MariaDB port to connect to"
|
||||
default 3306
|
||||
end
|
||||
|
||||
string :username do
|
||||
description "The MariaDB username"
|
||||
default "postal"
|
||||
end
|
||||
|
||||
string :password do
|
||||
description "The MariaDB password"
|
||||
end
|
||||
|
||||
string :database do
|
||||
description "The MariaDB database name"
|
||||
default "postal"
|
||||
end
|
||||
|
||||
integer :pool_size do
|
||||
description "The maximum size of the MariaDB connection pool"
|
||||
default 5
|
||||
end
|
||||
|
||||
string :encoding do
|
||||
description "The encoding to use when connecting to the MariaDB database"
|
||||
default "utf8mb4"
|
||||
end
|
||||
end
|
||||
|
||||
group :message_db do
|
||||
string :host do
|
||||
description "Hostname for the MariaDB server which stores the mail server databases"
|
||||
default "localhost"
|
||||
end
|
||||
|
||||
integer :port do
|
||||
description "The MariaDB port to connect to"
|
||||
default 3306
|
||||
end
|
||||
|
||||
string :username do
|
||||
description "The MariaDB username"
|
||||
default "postal"
|
||||
end
|
||||
|
||||
string :password do
|
||||
description "The MariaDB password"
|
||||
end
|
||||
|
||||
string :database_name_prefix do
|
||||
description "The MariaDB prefix to add to database names"
|
||||
default "postal"
|
||||
end
|
||||
end
|
||||
|
||||
group :logging do
|
||||
boolean :rails_log_enabled do
|
||||
description "Enable the default Rails logger"
|
||||
default false
|
||||
end
|
||||
|
||||
string :sentry_dsn do
|
||||
description "A DSN which should be used to report exceptions to Sentry"
|
||||
end
|
||||
|
||||
boolean :enabled do
|
||||
description "Enable the Postal logger to log to STDOUT"
|
||||
default true
|
||||
end
|
||||
|
||||
boolean :highlighting_enabled do
|
||||
description "Enable highlighting of log lines"
|
||||
default false
|
||||
end
|
||||
end
|
||||
|
||||
group :gelf do
|
||||
string :host do
|
||||
description "GELF-capable host to send logs to"
|
||||
end
|
||||
|
||||
integer :port do
|
||||
description "GELF port to send logs to"
|
||||
default 12_201
|
||||
end
|
||||
|
||||
string :facility do
|
||||
description "The facility name to add to all log entries sent to GELF"
|
||||
default "postal"
|
||||
end
|
||||
end
|
||||
|
||||
group :smtp_server do
|
||||
integer :default_port do
|
||||
description "The default port the SMTP server should listen on unless overriden by the PORT environment variable"
|
||||
default 25
|
||||
end
|
||||
|
||||
string :default_bind_address do
|
||||
description "The default bind address the SMTP server should listen on unless overriden by the BIND_ADDRESS environment variable"
|
||||
default "127.0.0.1"
|
||||
end
|
||||
|
||||
integer :default_health_server_port do
|
||||
description "The default port for the SMTP server health server to listen on"
|
||||
default 9091
|
||||
end
|
||||
|
||||
string :default_health_server_bind_address do
|
||||
description "The default bind address for the SMTP server health server to listen on"
|
||||
default "127.0.0.1"
|
||||
end
|
||||
|
||||
boolean :tls_enabled do
|
||||
description "Enable TLS for the SMTP server (requires certificate)"
|
||||
default false
|
||||
end
|
||||
|
||||
string :tls_certificate_path do
|
||||
description "The path to the SMTP server's TLS certificate"
|
||||
default "config/postal/smtp.cert"
|
||||
end
|
||||
|
||||
string :tls_private_key_path do
|
||||
description "The path to the SMTP server's TLS private key"
|
||||
default "config/postal/smtp.key"
|
||||
end
|
||||
|
||||
string :tls_ciphers do
|
||||
description "Override ciphers to use for SSL"
|
||||
end
|
||||
|
||||
string :ssl_version do
|
||||
description "The SSL versions which are supported"
|
||||
default "SSLv23"
|
||||
end
|
||||
|
||||
boolean :proxy_protocol do
|
||||
description "Enable proxy protocol for use behind some load balancers"
|
||||
default false
|
||||
end
|
||||
|
||||
boolean :log_connections do
|
||||
description "Enable connection logging"
|
||||
default false
|
||||
end
|
||||
|
||||
integer :max_message_size do
|
||||
description "The maximum message size to accept from the SMTP server (in MB)"
|
||||
default 14
|
||||
end
|
||||
|
||||
string :log_ip_address_exclusion_matcher do
|
||||
description "A regular expression to use to exclude connections from logging"
|
||||
end
|
||||
end
|
||||
|
||||
group :dns do
|
||||
string :mx_records do
|
||||
description "The names of the default MX records"
|
||||
array
|
||||
default ["mx1.postal.example.com", "mx2.postal.example.com"]
|
||||
end
|
||||
|
||||
string :spf_include do
|
||||
description "The location of the SPF record"
|
||||
default "spf.postal.example.com"
|
||||
end
|
||||
|
||||
string :return_path_domain do
|
||||
description "The return path hostname"
|
||||
default "rp.postal.example.com"
|
||||
end
|
||||
|
||||
string :route_domain do
|
||||
description "The domain to use for hosting route-specific addresses"
|
||||
default "routes.postal.example.com"
|
||||
end
|
||||
|
||||
string :track_domain do
|
||||
description "The CNAME which tracking domains should be pointed to"
|
||||
default "track.postal.example.com"
|
||||
end
|
||||
|
||||
string :helo_hostname do
|
||||
description "The hostname to use in HELO/EHLO when connecting to external SMTP servers"
|
||||
end
|
||||
|
||||
string :dkim_identifier do
|
||||
description "The identifier to use for DKIM keys in DNS records"
|
||||
default "postal"
|
||||
end
|
||||
|
||||
string :domain_verify_prefix do
|
||||
description "The prefix to add before TXT record verification string"
|
||||
default "postal-verification"
|
||||
end
|
||||
|
||||
string :custom_return_path_prefix do
|
||||
description "The domain to use on external domains which points to the Postal return path domain"
|
||||
default "psrp"
|
||||
end
|
||||
end
|
||||
|
||||
group :smtp do
|
||||
string :host do
|
||||
description "The hostname to send application-level e-mails to"
|
||||
default "127.0.0.1"
|
||||
end
|
||||
|
||||
integer :port do
|
||||
description "The port number to send application-level e-mails to"
|
||||
default 25
|
||||
end
|
||||
|
||||
string :username do
|
||||
description "The username to use when authentication to the SMTP server"
|
||||
end
|
||||
|
||||
string :password do
|
||||
description "The password to use when authentication to the SMTP server"
|
||||
end
|
||||
|
||||
string :from_name do
|
||||
description "The name to use as the from name outgoing emails from Postal"
|
||||
default "Postal"
|
||||
end
|
||||
|
||||
string :from_address do
|
||||
description "The e-mail to use as the from address outgoing emails from Postal"
|
||||
default "postal@example.com"
|
||||
end
|
||||
end
|
||||
|
||||
group :rails do
|
||||
string :environment do
|
||||
description "The Rails environment to run the application in"
|
||||
default "production"
|
||||
end
|
||||
|
||||
string :secret_key do
|
||||
description "The secret key used to sign and encrypt cookies and session data in the application"
|
||||
end
|
||||
end
|
||||
|
||||
group :rspamd do
|
||||
boolean :enabled do
|
||||
description "Enable rspamd for message inspection"
|
||||
default false
|
||||
end
|
||||
|
||||
string :host do
|
||||
description "The hostname of the rspamd server"
|
||||
default "127.0.0.1"
|
||||
end
|
||||
|
||||
integer :port do
|
||||
description "The port of the rspamd server"
|
||||
default 11_334
|
||||
end
|
||||
|
||||
boolean :ssl do
|
||||
description "Enable SSL for the rspamd connection"
|
||||
default false
|
||||
end
|
||||
|
||||
string :password do
|
||||
description "The password for the rspamd server"
|
||||
end
|
||||
|
||||
string :flags do
|
||||
description "Any flags for the rspamd server"
|
||||
end
|
||||
end
|
||||
|
||||
group :spamd do
|
||||
boolean :enabled do
|
||||
description "Enable SpamAssassin for message inspection"
|
||||
default false
|
||||
end
|
||||
|
||||
string :host do
|
||||
description "The hostname for the SpamAssassin server"
|
||||
default "127.0.0.1"
|
||||
end
|
||||
|
||||
integer :port do
|
||||
description "The port of the SpamAssassin server"
|
||||
default 783
|
||||
end
|
||||
end
|
||||
|
||||
group :clamav do
|
||||
boolean :enabled do
|
||||
description "Enable ClamAV for message inspection"
|
||||
default false
|
||||
end
|
||||
|
||||
string :host do
|
||||
description "The host of the ClamAV server"
|
||||
default "127.0.0.1"
|
||||
end
|
||||
|
||||
integer :port do
|
||||
description "The port of the ClamAV server"
|
||||
default 2000
|
||||
end
|
||||
end
|
||||
|
||||
group :smtp_client do
|
||||
integer :open_timeout do
|
||||
description "The open timeout for outgoing SMTP connections"
|
||||
default 30
|
||||
end
|
||||
|
||||
integer :read_timeout do
|
||||
description "The read timeout for outgoing SMTP connections"
|
||||
default 30
|
||||
end
|
||||
end
|
||||
|
||||
group :migration_waiter do
|
||||
boolean :enabled do
|
||||
description "Wait for all migrations to run before starting a process"
|
||||
default false
|
||||
end
|
||||
|
||||
integer :attempts do
|
||||
description "The number of attempts to try waiting for migrations to complete before start"
|
||||
default 120
|
||||
end
|
||||
|
||||
integer :sleep_time do
|
||||
description "The number of seconds to wait between each migration check"
|
||||
default 2
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
122
lib/postal/legacy_config_source.rb
Normal file
122
lib/postal/legacy_config_source.rb
Normal file
@@ -0,0 +1,122 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "konfig/sources/abstract"
|
||||
require "konfig/error"
|
||||
|
||||
module Postal
|
||||
class LegacyConfigSource < Konfig::Sources::Abstract
|
||||
|
||||
# This maps all the new configuration values to where they
|
||||
# exist in the old YAML file. The source will load any YAML
|
||||
# file that has been provided to this source in order. A
|
||||
# warning will be generated to the console for configuration
|
||||
# loaded from this format.
|
||||
MAPPING = {
|
||||
"postal.web_hostname" => -> (c) { c.dig("web", "host") },
|
||||
"postal.web_protocol" => -> (c) { c.dig("web", "protocol") },
|
||||
"postal.smtp_hostname" => -> (c) { c.dig("dns", "smtp_server_hostname") },
|
||||
"postal.use_ip_pools" => -> (c) { c.dig("general", "use_ip_pools") },
|
||||
"logging.sentry_dsn" => -> (c) { c.dig("general", "exception_url") },
|
||||
"postal.default_maximum_delivery_attempts" => -> (c) { c.dig("general", "maximum_delivery_attempts") },
|
||||
"postal.default_maximum_hold_expiry_days" => -> (c) { c.dig("general", "maximum_hold_expiry_days") },
|
||||
"postal.default_suppression_list_automatic_removal_days" => -> (c) { c.dig("general", "suppression_list_removal_delay") },
|
||||
"postal.use_local_ns_for_domain_verification" => -> (c) { c.dig("general", "use_local_ns_for_domains") },
|
||||
"postal.default_spam_threshold" => -> (c) { c.dig("general", "default_spam_threshold") },
|
||||
"postal.default_spam_failure_threshold" => -> (c) { c.dig("general", "default_spam_failure_threshold") },
|
||||
"postal.use_resent_sender_header" => -> (c) { c.dig("general", "use_resent_sender_header") },
|
||||
# SMTP relays must be converted to the new URI style format and they'll
|
||||
# then be transformed back to a hash by the schema transform.
|
||||
"postal.smtp_relays" => -> (c) { c["smtp_relays"]&.map { |r| "smtp://#{r['hostname']}:#{r['port']}?ssl_mode=#{r['ssl_mode']}" } },
|
||||
|
||||
"web_server.default_bind_address" => -> (c) { c.dig("web_server", "bind_address") },
|
||||
"web_server.default_port" => -> (c) { c.dig("web_server", "port") },
|
||||
"web_server.max_threads" => -> (c) { c.dig("web_server", "max_threads") },
|
||||
|
||||
"main_db.host" => -> (c) { c.dig("main_db", "host") },
|
||||
"main_db.port" => -> (c) { c.dig("main_db", "port") },
|
||||
"main_db.username" => -> (c) { c.dig("main_db", "username") },
|
||||
"main_db.password" => -> (c) { c.dig("main_db", "password") },
|
||||
"main_db.database" => -> (c) { c.dig("main_db", "database") },
|
||||
"main_db.pool_size" => -> (c) { c.dig("main_db", "pool_size") },
|
||||
"main_db.encoding" => -> (c) { c.dig("main_db", "encoding") },
|
||||
|
||||
"message_db.host" => -> (c) { c.dig("message_db", "host") },
|
||||
"message_db.port" => -> (c) { c.dig("message_db", "port") },
|
||||
"message_db.username" => -> (c) { c.dig("message_db", "username") },
|
||||
"message_db.password" => -> (c) { c.dig("message_db", "password") },
|
||||
"message_db.database_name_prefix" => -> (c) { c.dig("message_db", "prefix") },
|
||||
|
||||
"logging.rails_log_enabled" => -> (c) { c.dig("logging", "rails_log") },
|
||||
|
||||
"gelf.host" => -> (c) { c.dig("logging", "graylog", "host") },
|
||||
"gelf.port" => -> (c) { c.dig("logging", "graylog", "port") },
|
||||
"gelf.facility" => -> (c) { c.dig("logging", "graylog", "facility") },
|
||||
|
||||
"smtp_server.default_port" => -> (c) { c.dig("smtp_server", "port") },
|
||||
"smtp_server.default_bind_address" => -> (c) { c.dig("smtp_server", "bind_address") },
|
||||
"smtp_server.tls_enabled" => -> (c) { c.dig("smtp_server", "tls_enabled") },
|
||||
"smtp_server.tls_certificate_path" => -> (c) { c.dig("smtp_server", "tls_certificate_path") },
|
||||
"smtp_server.tls_private_key_path" => -> (c) { c.dig("smtp_server", "tls_private_key_path") },
|
||||
"smtp_server.tls_ciphers" => -> (c) { c.dig("smtp_server", "tls_ciphers") },
|
||||
"smtp_server.ssl_version" => -> (c) { c.dig("smtp_server", "ssl_version") },
|
||||
"smtp_server.proxy_protocol" => -> (c) { c.dig("smtp_server", "proxy_protocol") },
|
||||
"smtp_server.log_connections" => -> (c) { c.dig("smtp_server", "log_connect") },
|
||||
"smtp_server.max_message_size" => -> (c) { c.dig("smtp_server", "max_message_size") },
|
||||
|
||||
"dns.mx_records" => -> (c) { c.dig("dns", "mx_records") },
|
||||
"dns.spf_include" => -> (c) { c.dig("dns", "spf_include") },
|
||||
"dns.return_path_domain" => -> (c) { c.dig("dns", "return_path") },
|
||||
"dns.route_domain" => -> (c) { c.dig("dns", "route_domain") },
|
||||
"dns.track_domain" => -> (c) { c.dig("dns", "track_domain") },
|
||||
"dns.helo_hostname" => -> (c) { c.dig("dns", "helo_hostname") },
|
||||
"dns.dkim_identifier" => -> (c) { c.dig("dns", "dkim_identifier") },
|
||||
"dns.domain_verify_prefix" => -> (c) { c.dig("dns", "domain_verify_prefix") },
|
||||
"dns.custom_return_path_prefix" => -> (c) { c.dig("dns", "custom_return_path_prefix") },
|
||||
|
||||
"smtp.host" => -> (c) { c.dig("smtp", "host") },
|
||||
"smtp.port" => -> (c) { c.dig("smtp", "port") },
|
||||
"smtp.username" => -> (c) { c.dig("smtp", "username") },
|
||||
"smtp.password" => -> (c) { c.dig("smtp", "password") },
|
||||
"smtp.from_name" => -> (c) { c.dig("smtp", "from_name") },
|
||||
"smtp.from_address" => -> (c) { c.dig("smtp", "from_address") },
|
||||
|
||||
"rails.environment" => -> (c) { c.dig("rails", "environment") },
|
||||
"rails.secret_key" => -> (c) { c.dig("rails", "secret_key") },
|
||||
|
||||
"rspamd.enabled" => -> (c) { c.dig("rspamd", "enabled") },
|
||||
"rspamd.host" => -> (c) { c.dig("rspamd", "host") },
|
||||
"rspamd.port" => -> (c) { c.dig("rspamd", "port") },
|
||||
"rspamd.ssl" => -> (c) { c.dig("rspamd", "ssl") },
|
||||
"rspamd.password" => -> (c) { c.dig("rspamd", "password") },
|
||||
"rspamd.flags" => -> (c) { c.dig("rspamd", "flags") },
|
||||
|
||||
"spamd.enabled" => -> (c) { c.dig("spamd", "enabled") },
|
||||
"spamd.host" => -> (c) { c.dig("spamd", "host") },
|
||||
"spamd.port" => -> (c) { c.dig("spamd", "port") },
|
||||
|
||||
"clamav.enabled" => -> (c) { c.dig("clamav", "enabled") },
|
||||
"clamav.host" => -> (c) { c.dig("clamav", "host") },
|
||||
"clamav.port" => -> (c) { c.dig("clamav", "port") },
|
||||
|
||||
"smtp_client.open_timeout" => -> (c) { c.dig("smtp_client", "open_timeout") },
|
||||
"smtp_client.read_timeout" => -> (c) { c.dig("smtp_client", "read_timeout") }
|
||||
|
||||
}.freeze
|
||||
|
||||
def initialize(config)
|
||||
super()
|
||||
@config = config
|
||||
end
|
||||
|
||||
def get(path, attribute: nil)
|
||||
path_string = path.join(".")
|
||||
raise Konfig::ValueNotPresentError unless MAPPING.key?(path_string)
|
||||
|
||||
legacy_value = MAPPING[path_string].call(@config)
|
||||
raise Konfig::ValueNotPresentError if legacy_value.nil?
|
||||
|
||||
legacy_value
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
@@ -63,11 +63,11 @@ module Postal
|
||||
|
||||
def establish_connection
|
||||
Mysql2::Client.new(
|
||||
host: Postal.config.message_db.host,
|
||||
username: Postal.config.message_db.username,
|
||||
password: Postal.config.message_db.password,
|
||||
port: Postal.config.message_db.port,
|
||||
encoding: Postal.config.message_db.encoding || "utf8mb4"
|
||||
host: Postal::Config.message_db.host,
|
||||
username: Postal::Config.message_db.username,
|
||||
password: Postal::Config.message_db.password,
|
||||
port: Postal::Config.message_db.port,
|
||||
encoding: Postal::Config.message_db.encoding
|
||||
)
|
||||
end
|
||||
|
||||
|
||||
@@ -272,7 +272,7 @@ module Postal
|
||||
# Return the correct database name
|
||||
#
|
||||
def database_name
|
||||
@database_name ||= "#{Postal.config.message_db.prefix}-server-#{@server_id}"
|
||||
@database_name ||= "#{Postal::Config.message_db.database_name_prefix}-server-#{@server_id}"
|
||||
end
|
||||
|
||||
#
|
||||
|
||||
@@ -128,7 +128,7 @@ module Postal
|
||||
#
|
||||
def create_delivery(status, options = {})
|
||||
delivery = Delivery.create(self, options.merge(status: status))
|
||||
hold_expiry = status == "Held" ? Postal.config.general.maximum_hold_expiry_days.days.from_now.to_f : nil
|
||||
hold_expiry = status == "Held" ? Postal::Config.postal.default_maximum_hold_expiry_days.days.from_now.to_f : nil
|
||||
update(status: status, last_delivery_attempt: delivery.timestamp.to_f, held: status == "Held", hold_expiry: hold_expiry)
|
||||
delivery
|
||||
end
|
||||
@@ -511,7 +511,7 @@ module Postal
|
||||
# Was thsi message sent to a return path?
|
||||
#
|
||||
def rcpt_to_return_path?
|
||||
!!(rcpt_to =~ /@#{Regexp.escape(Postal.config.dns.custom_return_path_prefix)}\./)
|
||||
!!(rcpt_to =~ /@#{Regexp.escape(Postal::Config.dns.custom_return_path_prefix)}\./)
|
||||
end
|
||||
|
||||
#
|
||||
|
||||
@@ -9,7 +9,7 @@ module Postal
|
||||
end
|
||||
|
||||
def add(type, address, options = {})
|
||||
keep_until = (options[:days] || Postal.config.general.suppression_list_removal_delay).days.from_now.to_f
|
||||
keep_until = (options[:days] || Postal::Config.postal.default_suppression_list_automatic_removal_days).days.from_now.to_f
|
||||
if existing = @database.select("suppressions", where: { type: type, address: address }, limit: 1).first
|
||||
reason = options[:reason] || existing["reason"]
|
||||
@database.update("suppressions", { reason: reason, keep_until: keep_until }, where: { id: existing["id"] })
|
||||
|
||||
@@ -24,14 +24,14 @@ module Postal
|
||||
# installation.
|
||||
def inspectors
|
||||
[].tap do |inspectors|
|
||||
if Postal.config.rspamd&.enabled
|
||||
inspectors << MessageInspectors::Rspamd.new(Postal.config.rspamd)
|
||||
elsif Postal.config.spamd&.enabled
|
||||
inspectors << MessageInspectors::SpamAssassin.new(Postal.config.spamd)
|
||||
if Postal::Config.rspamd.enabled?
|
||||
inspectors << MessageInspectors::Rspamd.new(Postal::Config.rspamd)
|
||||
elsif Postal::Config.spamd.enabled?
|
||||
inspectors << MessageInspectors::SpamAssassin.new(Postal::Config.spamd)
|
||||
end
|
||||
|
||||
if Postal.config.clamav&.enabled
|
||||
inspectors << MessageInspectors::Clamav.new(Postal.config.clamav)
|
||||
if Postal::Config.clamav.enabled?
|
||||
inspectors << MessageInspectors::Clamav.new(Postal::Config.clamav)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
37
lib/postal/yaml_config_exporter.rb
Normal file
37
lib/postal/yaml_config_exporter.rb
Normal file
@@ -0,0 +1,37 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "konfig/exporters/abstract"
|
||||
|
||||
module Postal
|
||||
class YamlConfigExporter < Konfig::Exporters::Abstract
|
||||
|
||||
def export
|
||||
contents = []
|
||||
contents << "version: 2"
|
||||
contents << ""
|
||||
|
||||
@schema.groups.each do |group_name, group|
|
||||
contents << "#{group_name}:"
|
||||
group.attributes.each do |name, attr|
|
||||
contents << " # #{attr.description}"
|
||||
if attr.array?
|
||||
if attr.default.blank?
|
||||
contents << " #{name}: []"
|
||||
else
|
||||
contents << " #{name}:"
|
||||
attr.default.each do |d|
|
||||
contents << " - #{d}"
|
||||
end
|
||||
end
|
||||
else
|
||||
contents << " #{name}: #{attr.default}"
|
||||
end
|
||||
end
|
||||
contents << ""
|
||||
end
|
||||
|
||||
contents.join("\n")
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
@@ -10,6 +10,18 @@ namespace :postal do
|
||||
server.message_db.provisioner.migrate
|
||||
end
|
||||
end
|
||||
|
||||
desc "Generate configuration documentation"
|
||||
task generate_config_docs: :environment do
|
||||
require "konfig/exporters/env_vars_as_markdown"
|
||||
|
||||
FileUtils.mkdir_p("doc/config")
|
||||
output = Konfig::Exporters::EnvVarsAsMarkdown.new(Postal::ConfigSchema).export
|
||||
File.write("doc/config/environment-variables.md", output)
|
||||
|
||||
output = Postal::YamlConfigExporter.new(Postal::ConfigSchema).export
|
||||
File.write("doc/config/yaml.yml", output)
|
||||
end
|
||||
end
|
||||
|
||||
Rake::Task["db:migrate"].enhance do
|
||||
|
||||
@@ -1,24 +0,0 @@
|
||||
#!/usr/bin/env ruby
|
||||
# frozen_string_literal: true
|
||||
|
||||
require File.expand_path("../lib/postal/config", __dir__)
|
||||
require "openssl"
|
||||
require "securerandom"
|
||||
require "fileutils"
|
||||
|
||||
unless File.directory?(Postal.config_root)
|
||||
FileUtils.mkdir_p(Postal.config_root)
|
||||
end
|
||||
|
||||
unless File.exist?(Postal.config_file_path)
|
||||
content = File.read(Postal.app_root.join("config", "postal.example.yml"))
|
||||
content.gsub!("{{secretkey}}", SecureRandom.hex(128))
|
||||
File.write(Postal.config_file_path, content)
|
||||
puts "Created example config file at #{Postal.config_file_path}"
|
||||
end
|
||||
|
||||
unless File.exist?(Postal.signing_key_path)
|
||||
key = OpenSSL::PKey::RSA.new(1024).to_s
|
||||
File.write(Postal.signing_key_path, key)
|
||||
puts "Created new signing key for DKIM & HTTP requests"
|
||||
end
|
||||
@@ -3,21 +3,24 @@
|
||||
require File.expand_path("../lib/postal/config", __dir__)
|
||||
require "openssl"
|
||||
|
||||
unless File.exist?(Postal.smtp_private_key_path)
|
||||
key_path = Postal::Config.smtp_server.tls_private_key_path
|
||||
cert_path = Postal::Config.smtp_server.tls_certificate_path
|
||||
|
||||
unless File.exist?(key_path)
|
||||
key = OpenSSL::PKey::RSA.new(2048).to_s
|
||||
File.write(Postal.smtp_private_key_path, key)
|
||||
puts "Created new private key for encrypting SMTP connections"
|
||||
File.write(key_path, key)
|
||||
puts "Created new private key for encrypting SMTP connections at #{key_path}"
|
||||
end
|
||||
|
||||
unless File.exist?(Postal.smtp_certificate_path)
|
||||
unless File.exist?(cert_path)
|
||||
cert = OpenSSL::X509::Certificate.new
|
||||
cert.subject = cert.issuer = OpenSSL::X509::Name.parse("/C=GB/O=Test/OU=Test/CN=Test")
|
||||
cert.not_before = Time.now
|
||||
cert.not_after = Time.now + (365 * 24 * 60 * 60)
|
||||
cert.public_key = Postal.smtp_private_key.public_key
|
||||
cert.public_key = SMTPServer::Server.tls_private_key.public_key
|
||||
cert.serial = 0x0
|
||||
cert.version = 2
|
||||
cert.sign Postal.smtp_private_key, OpenSSL::Digest.new("SHA256")
|
||||
File.write(Postal.smtp_certificate_path, cert.to_pem)
|
||||
puts "Created new self signed certificate for encrypting SMTP connections"
|
||||
cert.sign SMTPServer::Server.tls_private_key, OpenSSL::Digest.new("SHA256")
|
||||
File.write(cert_path, cert.to_pem)
|
||||
puts "Created new self signed certificate for encrypting SMTP connections at #{cert_path}"
|
||||
end
|
||||
|
||||
@@ -28,7 +28,7 @@ end
|
||||
|
||||
message = server.message_db.new_message
|
||||
message.scope = "incoming"
|
||||
message.rcpt_to = "#{server.token}@#{Postal.config.dns.return_path}"
|
||||
message.rcpt_to = "#{server.token}@#{Postal::Config.dns.return_path_domain}"
|
||||
message.mail_from = "MAILER-DAEMON@smtp.infra.atech.io"
|
||||
message.raw_message = template
|
||||
message.bounce = true
|
||||
|
||||
@@ -5,11 +5,11 @@ require_relative "../lib/postal/config"
|
||||
require "mysql2"
|
||||
|
||||
client = Mysql2::Client.new(
|
||||
host: Postal.config.main_db.host,
|
||||
username: Postal.config.main_db.username,
|
||||
password: Postal.config.main_db.password,
|
||||
port: Postal.config.main_db.port,
|
||||
database: Postal.config.main_db.database
|
||||
host: Postal::Config.main_db.host,
|
||||
username: Postal::Config.main_db.username,
|
||||
password: Postal::Config.main_db.password,
|
||||
port: Postal::Config.main_db.port,
|
||||
database: Postal::Config.main_db.database
|
||||
)
|
||||
result = client.query("SELECT COUNT(id) as size FROM `queued_messages` WHERE retry_after IS NULL OR " \
|
||||
"retry_after <= ADDTIME(UTC_TIMESTAMP(), '30') AND locked_at IS NULL")
|
||||
|
||||
@@ -5,5 +5,9 @@ $stderr.sync = true
|
||||
|
||||
require_relative "../config/environment"
|
||||
|
||||
HealthServer.start(name: "smtp-server", default_port: 9091)
|
||||
HealthServer.start(
|
||||
name: "smtp-server",
|
||||
default_port: Postal::Config.smtp_server.default_health_server_port,
|
||||
default_bind_address: Postal::Config.smtp_server.default_health_server_bind_address
|
||||
)
|
||||
SMTPServer::Server.new(debug: true).run
|
||||
|
||||
@@ -25,9 +25,9 @@ rescue StandardError => e
|
||||
puts "\e[31mMessage was not delivered successfully to SMTP server.\e[0m"
|
||||
puts "Error: #{e.class} (#{e.message})"
|
||||
puts
|
||||
puts " SMTP Host: #{Postal.config.smtp.host}"
|
||||
puts " SMTP Port: #{Postal.config.smtp.port}"
|
||||
puts " SMTP Username: #{Postal.config.smtp.username}"
|
||||
puts " SMTP Password: #{Postal.config.smtp.password}"
|
||||
puts " SMTP Host: #{Postal::Config.smtp.host}"
|
||||
puts " SMTP Port: #{Postal::Config.smtp.port}"
|
||||
puts " SMTP Username: #{Postal::Config.smtp.username}"
|
||||
puts " SMTP Password: #{Postal::Config.smtp.password}"
|
||||
puts
|
||||
end
|
||||
|
||||
@@ -6,5 +6,10 @@ $stderr.sync = true
|
||||
|
||||
require_relative "../config/environment"
|
||||
|
||||
HealthServer.start(name: "worker", default_port: 9090)
|
||||
HealthServer.start(
|
||||
name: "worker",
|
||||
default_port: Postal::Config.worker.default_health_server_port,
|
||||
default_bind_address: Postal::Config.worker.default_health_server_bind_address
|
||||
)
|
||||
|
||||
Worker::Process.new.run
|
||||
|
||||
118
spec/examples/full_legacy_config_file.yml
Normal file
118
spec/examples/full_legacy_config_file.yml
Normal file
@@ -0,0 +1,118 @@
|
||||
# This a legacy configuration file format which was used in Postal version
|
||||
# less than v3. It remains supported in v3+ by mapping these values to their
|
||||
# correct values. Support for this file format will be removed in Postal v4.
|
||||
#
|
||||
# It exists here for reference but also to faciliate testing to ensure the
|
||||
# legacy mapping works as expected
|
||||
version: 1
|
||||
|
||||
web:
|
||||
host: postal.llamas.com
|
||||
protocol: https
|
||||
|
||||
general:
|
||||
use_ip_pools: false
|
||||
exception_url: https://sentry.llamas.com/abcdef1234
|
||||
maximum_delivery_attempts: 20
|
||||
maximum_hold_expiry_days: 10
|
||||
suppression_list_removal_delay: 60
|
||||
use_local_ns_for_domains: true
|
||||
default_spam_threshold: 10
|
||||
default_spam_failure_threshold: 25
|
||||
use_resent_sender_header: true
|
||||
|
||||
web_server:
|
||||
bind_address: 127.0.0.1
|
||||
port: 6000
|
||||
max_threads: 10
|
||||
|
||||
main_db:
|
||||
host: localhost
|
||||
port: 3306
|
||||
username: postal
|
||||
password: t35tpassword
|
||||
database: postal
|
||||
pool_size: 20
|
||||
encoding: utf8mb4
|
||||
|
||||
message_db:
|
||||
host: localhost
|
||||
port: 3306
|
||||
username: postal
|
||||
password: p05t41
|
||||
prefix: postal
|
||||
|
||||
logging:
|
||||
rails_log: true
|
||||
graylog:
|
||||
host: logs.llamas.com
|
||||
port: 12201
|
||||
facility: mailer
|
||||
|
||||
smtp_server:
|
||||
port: 25
|
||||
bind_address: 127.0.0.1
|
||||
tls_enabled: true
|
||||
tls_certificate_path: config/smtp.cert
|
||||
tls_private_key_path: config/smtp.key
|
||||
tls_ciphers: abc
|
||||
ssl_version: SSLv23
|
||||
proxy_protocol: false
|
||||
log_connect: true
|
||||
max_message_size: 10
|
||||
|
||||
smtp_relays:
|
||||
- hostname: 1.2.3.4
|
||||
port: 25
|
||||
ssl_mode: Auto
|
||||
- hostname: 2.2.2.2
|
||||
port: 2525
|
||||
ssl_mode: None
|
||||
|
||||
dns:
|
||||
mx_records:
|
||||
- mx1.postal.llamas.com
|
||||
- mx2.postal.llamas.com
|
||||
smtp_server_hostname: smtp.postal.llamas.com
|
||||
spf_include: spf.postal.llamas.com
|
||||
return_path: rp.postal.llamas.com
|
||||
route_domain: routes.postal.llamas.com
|
||||
track_domain: track.postal.llamas.com
|
||||
helo_hostname: helo.postal.llamas.com
|
||||
dkim_identifier: postal
|
||||
domain_verify_prefix: postal-verification
|
||||
custom_return_path_prefix: psrp
|
||||
|
||||
smtp:
|
||||
host: 127.0.0.1
|
||||
port: 25
|
||||
username: postalserver
|
||||
password: llama
|
||||
from_name: Postal
|
||||
from_address: postal@llamas.com
|
||||
|
||||
rails:
|
||||
environment: production
|
||||
secret_key: abcdef123123123123123
|
||||
|
||||
rspamd:
|
||||
enabled: true
|
||||
host: rspamd.llamas.com
|
||||
port: 11334
|
||||
ssl: false
|
||||
password: llama
|
||||
flags: abc
|
||||
|
||||
spamd:
|
||||
enabled: false
|
||||
host: spamd.llamas.com
|
||||
port: 783
|
||||
|
||||
clamav:
|
||||
enabled: false
|
||||
host: clamav.llamas.com
|
||||
port: 2000
|
||||
|
||||
smtp_client:
|
||||
open_timeout: 60
|
||||
read_timeout: 120
|
||||
@@ -42,7 +42,7 @@ module MessageDequeuer
|
||||
end
|
||||
|
||||
context "when the number of attempts is more than the maximum" do
|
||||
let(:queued_message) { create(:queued_message, :locked, message: message, attempts: Postal.config.general.maximum_delivery_attempts + 1) }
|
||||
let(:queued_message) { create(:queued_message, :locked, message: message, attempts: Postal::Config.postal.default_maximum_delivery_attempts + 1) }
|
||||
|
||||
it "logs" do
|
||||
processor.process
|
||||
|
||||
346
spec/lib/postal/legacy_config_source_spec.rb
Normal file
346
spec/lib/postal/legacy_config_source_spec.rb
Normal file
@@ -0,0 +1,346 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "rails_helper"
|
||||
|
||||
module Postal
|
||||
|
||||
SOURCE_CONFIG = YAML.safe_load(File.read(Rails.root.join("spec/examples/full_legacy_config_file.yml")))
|
||||
|
||||
# Rather than actuall test the LegacyConfigSource directly, I have decided
|
||||
# to test this source via. the Konfig::Config system to ensure it works as
|
||||
# expected in practice rather than just in theory. Testing '#get' would be
|
||||
# fairly easy (and mostly pointless) where as testing the values we actually
|
||||
# want are correct is preferred.
|
||||
RSpec.describe LegacyConfigSource do
|
||||
before do
|
||||
# For the purposes of testing, we want to ignore any defaults provided
|
||||
# by the schema itself. Otherwise, we might see a value returned that
|
||||
# looks correct but is actually the default rather than the value from
|
||||
# config file.
|
||||
allow_any_instance_of(Konfig::SchemaAttribute).to receive(:default).and_return(nil)
|
||||
end
|
||||
|
||||
let(:source) { described_class.new(SOURCE_CONFIG) }
|
||||
subject(:config) { Konfig::Config.build(ConfigSchema, sources: [source]) }
|
||||
|
||||
describe "the 'postal' group" do
|
||||
it "returns a value for postal.web_hostname" do
|
||||
expect(config.postal.web_hostname).to eq "postal.llamas.com"
|
||||
end
|
||||
|
||||
it "returns a value for postal.web_protocol" do
|
||||
expect(config.postal.web_protocol).to eq "https"
|
||||
end
|
||||
|
||||
it "returns a value for postal.smtp_hostname" do
|
||||
expect(config.postal.smtp_hostname).to eq "smtp.postal.llamas.com"
|
||||
end
|
||||
|
||||
it "returns a value for postal.use_ip_pools?" do
|
||||
expect(config.postal.use_ip_pools?).to eq false
|
||||
end
|
||||
|
||||
it "returns a value for postal.default_maximum_delivery_attempts" do
|
||||
expect(config.postal.default_maximum_delivery_attempts).to eq 20
|
||||
end
|
||||
|
||||
it "returns a value for postal.default_maximum_hold_expiry_days" do
|
||||
expect(config.postal.default_maximum_hold_expiry_days).to eq 10
|
||||
end
|
||||
|
||||
it "returns a value for postal.default_suppression_list_automatic_removal_days" do
|
||||
expect(config.postal.default_suppression_list_automatic_removal_days).to eq 60
|
||||
end
|
||||
|
||||
it "returns a value for postal.use_local_ns_for_domain_verification?" do
|
||||
expect(config.postal.use_local_ns_for_domain_verification?).to eq true
|
||||
end
|
||||
|
||||
it "returns a value for postal.default_spam_threshold" do
|
||||
expect(config.postal.default_spam_threshold).to eq 10
|
||||
end
|
||||
|
||||
it "returns a value for postal.default_spam_failure_threshold" do
|
||||
expect(config.postal.default_spam_failure_threshold).to eq 25
|
||||
end
|
||||
|
||||
it "returns a value for postal.use_resent_sender_header?" do
|
||||
expect(config.postal.use_resent_sender_header?).to eq true
|
||||
end
|
||||
|
||||
it "returns a value for postal.smtp_relays" do
|
||||
expect(config.postal.smtp_relays).to eq [
|
||||
{ "host" => "1.2.3.4", "port" => 25, "ssl_mode" => "Auto" },
|
||||
{ "host" => "2.2.2.2", "port" => 2525, "ssl_mode" => "None" }
|
||||
]
|
||||
end
|
||||
end
|
||||
|
||||
describe "the 'web_server' group" do
|
||||
it "returns a value for web_server.default_bind_address" do
|
||||
expect(config.web_server.default_bind_address).to eq "127.0.0.1"
|
||||
end
|
||||
|
||||
it "returns a value for web_server.default_port" do
|
||||
expect(config.web_server.default_port).to eq 6000
|
||||
end
|
||||
|
||||
it "returns a value for web_server.max_threads" do
|
||||
expect(config.web_server.max_threads).to eq 10
|
||||
end
|
||||
end
|
||||
|
||||
describe "the 'main_db' group" do
|
||||
it "returns a value for main_db.host" do
|
||||
expect(config.main_db.host).to eq "localhost"
|
||||
end
|
||||
|
||||
it "returns a value for main_db.port" do
|
||||
expect(config.main_db.port).to eq 3306
|
||||
end
|
||||
|
||||
it "returns a value for main_db.username" do
|
||||
expect(config.main_db.username).to eq "postal"
|
||||
end
|
||||
|
||||
it "returns a value for main_db.password" do
|
||||
expect(config.main_db.password).to eq "t35tpassword"
|
||||
end
|
||||
|
||||
it "returns a value for main_db.database" do
|
||||
expect(config.main_db.database).to eq "postal"
|
||||
end
|
||||
|
||||
it "returns a value for main_db.pool_size" do
|
||||
expect(config.main_db.pool_size).to eq 20
|
||||
end
|
||||
|
||||
it "returns a value for main_db.encoding" do
|
||||
expect(config.main_db.encoding).to eq "utf8mb4"
|
||||
end
|
||||
end
|
||||
|
||||
describe "the 'message_db' group" do
|
||||
it "returns a value for message_db.host" do
|
||||
expect(config.message_db.host).to eq "localhost"
|
||||
end
|
||||
|
||||
it "returns a value for message_db.port" do
|
||||
expect(config.message_db.port).to eq 3306
|
||||
end
|
||||
|
||||
it "returns a value for message_db.username" do
|
||||
expect(config.message_db.username).to eq "postal"
|
||||
end
|
||||
|
||||
it "returns a value for message_db.password" do
|
||||
expect(config.message_db.password).to eq "p05t41"
|
||||
end
|
||||
|
||||
it "returns a value for message_db.database_name_prefix" do
|
||||
expect(config.message_db.database_name_prefix).to eq "postal"
|
||||
end
|
||||
end
|
||||
|
||||
describe "the 'logging' group" do
|
||||
it "returns a value for logging.rails_log_enabled" do
|
||||
expect(config.logging.rails_log_enabled).to eq true
|
||||
end
|
||||
end
|
||||
|
||||
describe "the 'gelf' group" do
|
||||
it "returns a value for gelf.host" do
|
||||
expect(config.gelf.host).to eq "logs.llamas.com"
|
||||
end
|
||||
|
||||
it "returns a value for gelf.port" do
|
||||
expect(config.gelf.port).to eq 12_201
|
||||
end
|
||||
|
||||
it "returns a value for gelf.facility" do
|
||||
expect(config.gelf.facility).to eq "mailer"
|
||||
end
|
||||
end
|
||||
|
||||
describe "the 'smtp_server' group" do
|
||||
it "returns a value for smtp_server.default_port" do
|
||||
expect(config.smtp_server.default_port).to eq 25
|
||||
end
|
||||
|
||||
it "returns a value for smtp_server.default_bind_address" do
|
||||
expect(config.smtp_server.default_bind_address).to eq "127.0.0.1"
|
||||
end
|
||||
|
||||
it "returns a value for smtp_server.tls_enabled" do
|
||||
expect(config.smtp_server.tls_enabled).to eq true
|
||||
end
|
||||
|
||||
it "returns a value for smtp_server.tls_certificate_path" do
|
||||
expect(config.smtp_server.tls_certificate_path).to eq "config/smtp.cert"
|
||||
end
|
||||
|
||||
it "returns a value for smtp_server.tls_private_key_path" do
|
||||
expect(config.smtp_server.tls_private_key_path).to eq "config/smtp.key"
|
||||
end
|
||||
|
||||
it "returns a value for smtp_server.tls_ciphers" do
|
||||
expect(config.smtp_server.tls_ciphers).to eq "abc"
|
||||
end
|
||||
|
||||
it "returns a value for smtp_server.ssl_version" do
|
||||
expect(config.smtp_server.ssl_version).to eq "SSLv23"
|
||||
end
|
||||
|
||||
it "returns a value for smtp_server.proxy_protocol" do
|
||||
expect(config.smtp_server.proxy_protocol).to eq false
|
||||
end
|
||||
|
||||
it "returns a value for smtp_server.log_connections" do
|
||||
expect(config.smtp_server.log_connections).to eq true
|
||||
end
|
||||
|
||||
it "returns a value for smtp_server.max_message_size" do
|
||||
expect(config.smtp_server.max_message_size).to eq 10
|
||||
end
|
||||
end
|
||||
|
||||
describe "the 'dns' group" do
|
||||
it "returns a value for dns.mx_records" do
|
||||
expect(config.dns.mx_records).to eq ["mx1.postal.llamas.com", "mx2.postal.llamas.com"]
|
||||
end
|
||||
|
||||
it "returns a value for dns.spf_include" do
|
||||
expect(config.dns.spf_include).to eq "spf.postal.llamas.com"
|
||||
end
|
||||
|
||||
it "returns a value for dns.return_path_domain" do
|
||||
expect(config.dns.return_path_domain).to eq "rp.postal.llamas.com"
|
||||
end
|
||||
|
||||
it "returns a value for dns.route_domain" do
|
||||
expect(config.dns.route_domain).to eq "routes.postal.llamas.com"
|
||||
end
|
||||
|
||||
it "returns a value for dns.track_domain" do
|
||||
expect(config.dns.track_domain).to eq "track.postal.llamas.com"
|
||||
end
|
||||
|
||||
it "returns a value for dns.helo_hostname" do
|
||||
expect(config.dns.helo_hostname).to eq "helo.postal.llamas.com"
|
||||
end
|
||||
|
||||
it "returns a value for dns.dkim_identifier" do
|
||||
expect(config.dns.dkim_identifier).to eq "postal"
|
||||
end
|
||||
|
||||
it "returns a value for dns.domain_verify_prefix" do
|
||||
expect(config.dns.domain_verify_prefix).to eq "postal-verification"
|
||||
end
|
||||
|
||||
it "returns a value for dns.custom_return_path_prefix" do
|
||||
expect(config.dns.custom_return_path_prefix).to eq "psrp"
|
||||
end
|
||||
end
|
||||
|
||||
describe "the 'smtp' group" do
|
||||
it "returns a value for smtp.host" do
|
||||
expect(config.smtp.host).to eq "127.0.0.1"
|
||||
end
|
||||
|
||||
it "returns a value for smtp.port" do
|
||||
expect(config.smtp.port).to eq 25
|
||||
end
|
||||
|
||||
it "returns a value for smtp.username" do
|
||||
expect(config.smtp.username).to eq "postalserver"
|
||||
end
|
||||
|
||||
it "returns a value for smtp.password" do
|
||||
expect(config.smtp.password).to eq "llama"
|
||||
end
|
||||
|
||||
it "returns a value for smtp.from_name" do
|
||||
expect(config.smtp.from_name).to eq "Postal"
|
||||
end
|
||||
|
||||
it "returns a value for smtp.from_address" do
|
||||
expect(config.smtp.from_address).to eq "postal@llamas.com"
|
||||
end
|
||||
end
|
||||
|
||||
describe "the 'rails' group" do
|
||||
it "returns a value for rails.environment" do
|
||||
expect(config.rails.environment).to eq "production"
|
||||
end
|
||||
|
||||
it "returns a value for rails.secret_key" do
|
||||
expect(config.rails.secret_key).to eq "abcdef123123123123123"
|
||||
end
|
||||
end
|
||||
|
||||
describe "the 'rspamd' group" do
|
||||
it "returns a value for rspamd.enabled" do
|
||||
expect(config.rspamd.enabled).to eq true
|
||||
end
|
||||
|
||||
it "returns a value for rspamd.host" do
|
||||
expect(config.rspamd.host).to eq "rspamd.llamas.com"
|
||||
end
|
||||
|
||||
it "returns a value for rspamd.port" do
|
||||
expect(config.rspamd.port).to eq 11_334
|
||||
end
|
||||
|
||||
it "returns a value for rspamd.ssl?" do
|
||||
expect(config.rspamd.ssl?).to eq false
|
||||
end
|
||||
|
||||
it "returns a value for rspamd.password" do
|
||||
expect(config.rspamd.password).to eq "llama"
|
||||
end
|
||||
|
||||
it "returns a value for rspamd.flags" do
|
||||
expect(config.rspamd.flags).to eq "abc"
|
||||
end
|
||||
end
|
||||
|
||||
describe "the 'spamd' group" do
|
||||
it "returns a value for spamd.enabled" do
|
||||
expect(config.spamd.enabled).to eq false
|
||||
end
|
||||
|
||||
it "returns a value for spamd.host" do
|
||||
expect(config.spamd.host).to eq "spamd.llamas.com"
|
||||
end
|
||||
|
||||
it "returns a value for spamd.port" do
|
||||
expect(config.spamd.port).to eq 783
|
||||
end
|
||||
end
|
||||
|
||||
describe "the 'clamav' group" do
|
||||
it "returns a value for clamav.enabled" do
|
||||
expect(config.clamav.enabled).to eq false
|
||||
end
|
||||
|
||||
it "returns a value for clamav.host" do
|
||||
expect(config.clamav.host).to eq "clamav.llamas.com"
|
||||
end
|
||||
|
||||
it "returns a value for clamav.port" do
|
||||
expect(config.clamav.port).to eq 2000
|
||||
end
|
||||
end
|
||||
|
||||
describe "the 'smtp_client' group" do
|
||||
it "returns a value for smtp_client.open_timeout" do
|
||||
expect(config.smtp_client.open_timeout).to eq 60
|
||||
end
|
||||
|
||||
it "returns a value for smtp_client.read_timeout" do
|
||||
expect(config.smtp_client.read_timeout).to eq 120
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
@@ -12,7 +12,7 @@ describe ReceivedHeader do
|
||||
it "returns the correct string" do
|
||||
result = described_class.generate(nil, "testhelo", "1.1.1.1", :smtp)
|
||||
expect(result).to eq "from testhelo (hostname.com [1.1.1.1]) " \
|
||||
"by #{Postal.config.dns.smtp_server_hostname} " \
|
||||
"by #{Postal::Config.postal.smtp_hostname} " \
|
||||
"with SMTP; #{Time.now.utc.rfc2822}"
|
||||
end
|
||||
end
|
||||
@@ -21,7 +21,7 @@ describe ReceivedHeader do
|
||||
it "returns the correct string" do
|
||||
server = Server.new(privacy_mode: true)
|
||||
result = described_class.generate(server, "testhelo", "1.1.1.1", :smtp)
|
||||
expect(result).to eq "by #{Postal.config.dns.smtp_server_hostname} " \
|
||||
expect(result).to eq "by #{Postal::Config.postal.smtp_hostname} " \
|
||||
"with SMTP; #{Time.now.utc.rfc2822}"
|
||||
end
|
||||
end
|
||||
@@ -31,7 +31,7 @@ describe ReceivedHeader do
|
||||
server = Server.new(privacy_mode: false)
|
||||
result = described_class.generate(server, "testhelo", "1.1.1.1", :smtp)
|
||||
expect(result).to eq "from testhelo (hostname.com [1.1.1.1]) " \
|
||||
"by #{Postal.config.dns.smtp_server_hostname} " \
|
||||
"by #{Postal::Config.postal.smtp_hostname} " \
|
||||
"with SMTP; #{Time.now.utc.rfc2822}"
|
||||
end
|
||||
end
|
||||
@@ -40,7 +40,7 @@ describe ReceivedHeader do
|
||||
it "returns the correct string" do
|
||||
result = described_class.generate(nil, "web-ui", "1.1.1.1", :http)
|
||||
expect(result).to eq "from web-ui (hostname.com [1.1.1.1]) " \
|
||||
"by #{Postal.config.web.host} " \
|
||||
"by #{Postal::Config.postal.web_hostname} " \
|
||||
"with HTTP; #{Time.now.utc.rfc2822}"
|
||||
end
|
||||
end
|
||||
|
||||
@@ -39,7 +39,7 @@ module SMTPServer
|
||||
client.handle("RCPT TO: #{route.name}@#{route.domain.name}")
|
||||
Timecop.freeze do
|
||||
client.handle("DATA")
|
||||
expect(client.headers["received"]).to include "from test.example.com (1.2.3.4 [1.2.3.4]) by postal.example.com with SMTP; #{Time.now.utc.rfc2822}"
|
||||
expect(client.headers["received"]).to include "from test.example.com (1.2.3.4 [1.2.3.4]) by #{Postal::Config.postal.smtp_hostname} with SMTP; #{Time.now.utc.rfc2822}"
|
||||
end
|
||||
end
|
||||
|
||||
@@ -72,7 +72,7 @@ module SMTPServer
|
||||
client.handle("This is some content for the message.")
|
||||
client.handle("It will keep going.")
|
||||
expect(client.instance_variable_get("@data")).to eq <<~DATA
|
||||
Received: from test.example.com (1.2.3.4 [1.2.3.4]) by #{Postal.config.dns.smtp_server_hostname} with SMTP; #{Time.now.utc.rfc2822}\r
|
||||
Received: from test.example.com (1.2.3.4 [1.2.3.4]) by #{Postal::Config.postal.smtp_hostname} with SMTP; #{Time.now.utc.rfc2822}\r
|
||||
Subject: Test\r
|
||||
\r
|
||||
This is some content for the message.\r
|
||||
|
||||
@@ -24,7 +24,7 @@ module SMTPServer
|
||||
describe "when finished sending data" do
|
||||
context "when the data is larger than the maximum message size" do
|
||||
it "returns an error and resets the state" do
|
||||
allow(Postal.config.smtp_server).to receive(:max_message_size).and_return(1)
|
||||
allow(Postal::Config.smtp_server).to receive(:max_message_size).and_return(1)
|
||||
client.handle("DATA")
|
||||
client.handle("a" * 1024 * 1024 * 10)
|
||||
expect(client.handle(".")).to eq "552 Message too large (maximum size 1MB)"
|
||||
@@ -34,10 +34,10 @@ module SMTPServer
|
||||
context "when a loop is detected" do
|
||||
it "returns an error and resets the state" do
|
||||
client.handle("DATA")
|
||||
client.handle("Received: from example1.com by #{Postal.config.dns.smtp_server_hostname}")
|
||||
client.handle("Received: from example2.com by #{Postal.config.dns.smtp_server_hostname}")
|
||||
client.handle("Received: from example1.com by #{Postal.config.dns.smtp_server_hostname}")
|
||||
client.handle("Received: from example2.com by #{Postal.config.dns.smtp_server_hostname}")
|
||||
client.handle("Received: from example1.com by #{Postal::Config.postal.smtp_hostname}")
|
||||
client.handle("Received: from example2.com by #{Postal::Config.postal.smtp_hostname}")
|
||||
client.handle("Received: from example1.com by #{Postal::Config.postal.smtp_hostname}")
|
||||
client.handle("Received: from example2.com by #{Postal::Config.postal.smtp_hostname}")
|
||||
client.handle("Subject: Test")
|
||||
client.handle("From: #{mail_from}")
|
||||
client.handle("To: #{rcpt_to}")
|
||||
@@ -93,7 +93,7 @@ module SMTPServer
|
||||
|
||||
context "when sending a bounce message" do
|
||||
let(:credential) { nil }
|
||||
let(:rcpt_to) { "#{server.token}@#{Postal.config.dns.return_path}" }
|
||||
let(:rcpt_to) { "#{server.token}@#{Postal::Config.dns.return_path_domain}" }
|
||||
|
||||
context "when there is a return path route" do
|
||||
let(:domain) { create(:domain, owner: server) }
|
||||
@@ -114,7 +114,7 @@ module SMTPServer
|
||||
|
||||
queued_message = QueuedMessage.first
|
||||
expect(queued_message).to have_attributes(
|
||||
domain: Postal.config.dns.return_path,
|
||||
domain: Postal::Config.dns.return_path_domain,
|
||||
server: server
|
||||
)
|
||||
|
||||
@@ -145,7 +145,7 @@ module SMTPServer
|
||||
|
||||
queued_message = QueuedMessage.first
|
||||
expect(queued_message).to have_attributes(
|
||||
domain: Postal.config.dns.return_path,
|
||||
domain: Postal::Config.dns.return_path_domain,
|
||||
server: server
|
||||
)
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ module SMTPServer
|
||||
describe "HELO" do
|
||||
it "returns the hostname" do
|
||||
expect(client.state).to eq :welcome
|
||||
expect(client.handle("HELO: test.example.com")).to eq "250 #{Postal.config.dns.smtp_server_hostname}"
|
||||
expect(client.handle("HELO: test.example.com")).to eq "250 #{Postal::Config.postal.smtp_hostname}"
|
||||
expect(client.state).to eq :welcomed
|
||||
end
|
||||
end
|
||||
@@ -24,7 +24,7 @@ module SMTPServer
|
||||
|
||||
context "when TLS is enabled" do
|
||||
it "returns capabilities include starttls" do
|
||||
allow(Postal.config.smtp_server).to receive(:tls_enabled?).and_return(true)
|
||||
allow(Postal::Config.smtp_server).to receive(:tls_enabled?).and_return(true)
|
||||
expect(client.handle("EHLO test.example.com")).to eq ["250-My capabilities are",
|
||||
"250-STARTTLS",
|
||||
"250 AUTH CRAM-MD5 PLAIN LOGIN"]
|
||||
|
||||
@@ -36,18 +36,18 @@ module SMTPServer
|
||||
|
||||
context "when the RCPT TO address is the system return path host" do
|
||||
it "returns an error if the server does not exist" do
|
||||
expect(client.handle("RCPT TO: nothing@#{Postal.config.dns.return_path}")).to eq "550 Invalid server token"
|
||||
expect(client.handle("RCPT TO: nothing@#{Postal::Config.dns.return_path_domain}")).to eq "550 Invalid server token"
|
||||
end
|
||||
|
||||
it "returns an error if the server is suspended" do
|
||||
server = create(:server, :suspended)
|
||||
expect(client.handle("RCPT TO: #{server.token}@#{Postal.config.dns.return_path}"))
|
||||
expect(client.handle("RCPT TO: #{server.token}@#{Postal::Config.dns.return_path_domain}"))
|
||||
.to eq "535 Mail server has been suspended"
|
||||
end
|
||||
|
||||
it "adds a recipient if all OK" do
|
||||
server = create(:server)
|
||||
address = "#{server.token}@#{Postal.config.dns.return_path}"
|
||||
address = "#{server.token}@#{Postal::Config.dns.return_path_domain}"
|
||||
expect(client.handle("RCPT TO: #{address}")).to eq "250 OK"
|
||||
expect(client.recipients).to eq [[:bounce, address, server]]
|
||||
expect(client.state).to eq :rcpt_to_received
|
||||
@@ -56,19 +56,19 @@ module SMTPServer
|
||||
|
||||
context "when the RCPT TO address is on a host using the return path prefix" do
|
||||
it "returns an error if the server does not exist" do
|
||||
address = "nothing@#{Postal.config.dns.custom_return_path_prefix}.example.com"
|
||||
address = "nothing@#{Postal::Config.dns.custom_return_path_prefix}.example.com"
|
||||
expect(client.handle("RCPT TO: #{address}")).to eq "550 Invalid server token"
|
||||
end
|
||||
|
||||
it "returns an error if the server is suspended" do
|
||||
server = create(:server, :suspended)
|
||||
address = "#{server.token}@#{Postal.config.dns.custom_return_path_prefix}.example.com"
|
||||
address = "#{server.token}@#{Postal::Config.dns.custom_return_path_prefix}.example.com"
|
||||
expect(client.handle("RCPT TO: #{address}")).to eq "535 Mail server has been suspended"
|
||||
end
|
||||
|
||||
it "adds a recipient if all OK" do
|
||||
server = create(:server)
|
||||
address = "#{server.token}@#{Postal.config.dns.custom_return_path_prefix}.example.com"
|
||||
address = "#{server.token}@#{Postal::Config.dns.custom_return_path_prefix}.example.com"
|
||||
expect(client.handle("RCPT TO: #{address}")).to eq "250 OK"
|
||||
expect(client.recipients).to eq [[:bounce, address, server]]
|
||||
expect(client.state).to eq :rcpt_to_received
|
||||
@@ -77,28 +77,28 @@ module SMTPServer
|
||||
|
||||
context "when the RCPT TO address is within the route domain" do
|
||||
it "returns an error if the route token is invalid" do
|
||||
address = "nothing@#{Postal.config.dns.route_domain}"
|
||||
address = "nothing@#{Postal::Config.dns.route_domain}"
|
||||
expect(client.handle("RCPT TO: #{address}")).to eq "550 Invalid route token"
|
||||
end
|
||||
|
||||
it "returns an error if the server is suspended" do
|
||||
server = create(:server, :suspended)
|
||||
route = create(:route, server: server)
|
||||
address = "#{route.token}@#{Postal.config.dns.route_domain}"
|
||||
address = "#{route.token}@#{Postal::Config.dns.route_domain}"
|
||||
expect(client.handle("RCPT TO: #{address}")).to eq "535 Mail server has been suspended"
|
||||
end
|
||||
|
||||
it "returns an error if the route is set to Reject mail" do
|
||||
server = create(:server)
|
||||
route = create(:route, server: server, mode: "Reject")
|
||||
address = "#{route.token}@#{Postal.config.dns.route_domain}"
|
||||
address = "#{route.token}@#{Postal::Config.dns.route_domain}"
|
||||
expect(client.handle("RCPT TO: #{address}")).to eq "550 Route does not accept incoming messages"
|
||||
end
|
||||
|
||||
it "adds a recipient if all OK" do
|
||||
server = create(:server)
|
||||
route = create(:route, server: server)
|
||||
address = "#{route.token}+tag1@#{Postal.config.dns.route_domain}"
|
||||
address = "#{route.token}+tag1@#{Postal::Config.dns.route_domain}"
|
||||
expect(client.handle("RCPT TO: #{address}")).to eq "250 OK"
|
||||
expect(client.recipients).to eq [[:route, "#{route.name}+tag1@#{route.domain.name}", server, { route: route }]]
|
||||
expect(client.state).to eq :rcpt_to_received
|
||||
|
||||
@@ -216,7 +216,7 @@ describe Domain do
|
||||
|
||||
describe "#spf_record" do
|
||||
it "returns the SPF record" do
|
||||
expect(domain.spf_record).to eq "v=spf1 a mx include:#{Postal.config.dns.spf_include} ~all"
|
||||
expect(domain.spf_record).to eq "v=spf1 a mx include:#{Postal::Config.dns.spf_include} ~all"
|
||||
end
|
||||
end
|
||||
|
||||
@@ -251,7 +251,7 @@ describe Domain do
|
||||
end
|
||||
|
||||
it "returns the DKIM identifier" do
|
||||
expect(domain.dkim_identifier).to eq "#{Postal.config.dns.dkim_identifier}-#{domain.dkim_identifier_string}"
|
||||
expect(domain.dkim_identifier).to eq "#{Postal::Config.dns.dkim_identifier}-#{domain.dkim_identifier_string}"
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -269,14 +269,14 @@ describe Domain do
|
||||
end
|
||||
|
||||
it "returns the DKIM identifier" do
|
||||
expect(domain.dkim_record_name).to eq "#{Postal.config.dns.dkim_identifier}-#{domain.dkim_identifier_string}._domainkey"
|
||||
expect(domain.dkim_record_name).to eq "#{Postal::Config.dns.dkim_identifier}-#{domain.dkim_identifier_string}._domainkey"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "#return_path_domain" do
|
||||
it "returns the return path domain" do
|
||||
expect(domain.return_path_domain).to eq "#{Postal.config.dns.custom_return_path_prefix}.#{domain.name}"
|
||||
expect(domain.return_path_domain).to eq "#{Postal::Config.dns.custom_return_path_prefix}.#{domain.name}"
|
||||
end
|
||||
end
|
||||
|
||||
@@ -284,14 +284,14 @@ describe Domain do
|
||||
let(:domain) { create(:domain, verification_method: "DNS") }
|
||||
|
||||
it "returns the DNS verification string" do
|
||||
expect(domain.dns_verification_string).to eq "#{Postal.config.dns.domain_verify_prefix} #{domain.verification_token}"
|
||||
expect(domain.dns_verification_string).to eq "#{Postal::Config.dns.domain_verify_prefix} #{domain.verification_token}"
|
||||
end
|
||||
end
|
||||
|
||||
describe "#resolver" do
|
||||
context "when the local nameservers should be used" do
|
||||
before do
|
||||
allow(Postal.config.general).to receive(:use_local_ns_for_domains?).and_return(true)
|
||||
allow(Postal::Config.postal).to receive(:use_local_ns_for_domain_verification?).and_return(true)
|
||||
end
|
||||
|
||||
it "uses the local DNS" do
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
ENV["POSTAL_ENV"] = "test"
|
||||
ENV["POSTAL_CONFIG_FILE_PATH"] ||= "config/postal/postal.test.yml"
|
||||
|
||||
require "dotenv"
|
||||
Dotenv.load(".env.test")
|
||||
|
||||
require File.expand_path("../config/environment", __dir__)
|
||||
require "rspec/rails"
|
||||
|
||||
المرجع في مشكلة جديدة
حظر مستخدم