mirror of
https://github.com/postalserver/postal.git
synced 2026-10-19 10:46:32 +00:00
feat: new configuration system (and schema) (#2819)
This commit is contained in:
@@ -31,3 +31,5 @@ Procfile.local
|
|||||||
VERSION
|
VERSION
|
||||||
|
|
||||||
.rubocop-https*
|
.rubocop-https*
|
||||||
|
.env*
|
||||||
|
|
||||||
|
|||||||
+5
-1
@@ -24,7 +24,7 @@ bundle install
|
|||||||
|
|
||||||
## Configuration
|
## 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:
|
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
|
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
|
## Running
|
||||||
|
|
||||||
The neatest way to run postal is to ensure that `./bin` is your `$PATH` and then use one of the following commands.
|
The neatest way to run postal is to ensure that `./bin` is your `$PATH` and then use one of the following commands.
|
||||||
|
|||||||
+6
-3
@@ -46,8 +46,11 @@ COPY --chown=postal . .
|
|||||||
ARG VERSION=unspecified
|
ARG VERSION=unspecified
|
||||||
RUN echo $VERSION > VERSION
|
RUN echo $VERSION > VERSION
|
||||||
|
|
||||||
# Set the path to the config
|
# Set paths for when running in a container
|
||||||
ENV POSTAL_CONFIG_ROOT=/config
|
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
|
# Set the CMD
|
||||||
ENTRYPOINT [ "/docker-entrypoint.sh" ]
|
ENTRYPOINT [ "/docker-entrypoint.sh" ]
|
||||||
@@ -59,5 +62,5 @@ FROM base AS ci
|
|||||||
# full target - default if no --target option is given
|
# full target - default if no --target option is given
|
||||||
FROM base AS full
|
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
|
RUN touch /opt/postal/app/public/assets/.prebuilt
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ gem "authie"
|
|||||||
gem "autoprefixer-rails"
|
gem "autoprefixer-rails"
|
||||||
gem "bcrypt"
|
gem "bcrypt"
|
||||||
gem "chronic"
|
gem "chronic"
|
||||||
gem "dotenv-rails"
|
gem "dotenv"
|
||||||
gem "dynamic_form"
|
gem "dynamic_form"
|
||||||
gem "encrypto_signo"
|
gem "encrypto_signo"
|
||||||
gem "execjs", "~> 2.7", "< 2.8"
|
gem "execjs", "~> 2.7", "< 2.8"
|
||||||
@@ -15,6 +15,7 @@ gem "hashie"
|
|||||||
gem "highline", require: false
|
gem "highline", require: false
|
||||||
gem "kaminari"
|
gem "kaminari"
|
||||||
gem "klogger-logger"
|
gem "klogger-logger"
|
||||||
|
gem "konfig-config", "~> 2.0"
|
||||||
gem "mail"
|
gem "mail"
|
||||||
gem "moonrope"
|
gem "moonrope"
|
||||||
gem "mysql2"
|
gem "mysql2"
|
||||||
|
|||||||
+4
-4
@@ -102,9 +102,6 @@ GEM
|
|||||||
deep_merge (1.2.2)
|
deep_merge (1.2.2)
|
||||||
diff-lcs (1.5.0)
|
diff-lcs (1.5.0)
|
||||||
dotenv (3.0.2)
|
dotenv (3.0.2)
|
||||||
dotenv-rails (3.0.2)
|
|
||||||
dotenv (= 3.0.2)
|
|
||||||
railties (>= 6.1)
|
|
||||||
dynamic_form (1.3.1)
|
dynamic_form (1.3.1)
|
||||||
actionview (> 5.2.0)
|
actionview (> 5.2.0)
|
||||||
activemodel (> 5.2.0)
|
activemodel (> 5.2.0)
|
||||||
@@ -151,6 +148,8 @@ GEM
|
|||||||
concurrent-ruby (>= 1.0, < 2.0)
|
concurrent-ruby (>= 1.0, < 2.0)
|
||||||
json
|
json
|
||||||
rouge (>= 3.30, < 5.0)
|
rouge (>= 3.30, < 5.0)
|
||||||
|
konfig-config (2.1.1)
|
||||||
|
hashie
|
||||||
loofah (2.22.0)
|
loofah (2.22.0)
|
||||||
crass (~> 1.0.2)
|
crass (~> 1.0.2)
|
||||||
nokogiri (>= 1.12.0)
|
nokogiri (>= 1.12.0)
|
||||||
@@ -335,7 +334,7 @@ DEPENDENCIES
|
|||||||
chronic
|
chronic
|
||||||
coffee-rails (~> 5.0)
|
coffee-rails (~> 5.0)
|
||||||
database_cleaner
|
database_cleaner
|
||||||
dotenv-rails
|
dotenv
|
||||||
dynamic_form
|
dynamic_form
|
||||||
encrypto_signo
|
encrypto_signo
|
||||||
execjs (~> 2.7, < 2.8)
|
execjs (~> 2.7, < 2.8)
|
||||||
@@ -347,6 +346,7 @@ DEPENDENCIES
|
|||||||
jquery-rails
|
jquery-rails
|
||||||
kaminari
|
kaminari
|
||||||
klogger-logger
|
klogger-logger
|
||||||
|
konfig-config (~> 2.0)
|
||||||
mail
|
mail
|
||||||
moonrope
|
moonrope
|
||||||
mysql2
|
mysql2
|
||||||
|
|||||||
+2
-2
@@ -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
|
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_key = domain.dkim_key
|
||||||
@dkim_identifier = domain.dkim_identifier
|
@dkim_identifier = domain.dkim_identifier
|
||||||
else
|
else
|
||||||
@domain_name = Postal.config.dns.return_path
|
@domain_name = Postal::Config.dns.return_path_domain
|
||||||
@dkim_key = Postal.signing_key
|
@dkim_key = Postal.signing_key
|
||||||
@dkim_identifier = Postal.config.dns.dkim_identifier
|
@dkim_identifier = Postal::Config.dns.dkim_identifier
|
||||||
end
|
end
|
||||||
@domain = domain
|
@domain = domain
|
||||||
@message = message
|
@message = message
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ module MessageDequeuer
|
|||||||
end
|
end
|
||||||
|
|
||||||
def check_delivery_attempts
|
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."
|
details = "Maximum number of delivery attempts (#{queued_message.attempts}) has been reached."
|
||||||
if queued_message.message.scope == "incoming"
|
if queued_message.message.scope == "incoming"
|
||||||
|
|||||||
@@ -3,8 +3,8 @@
|
|||||||
class ReceivedHeader
|
class ReceivedHeader
|
||||||
|
|
||||||
OUR_HOSTNAMES = {
|
OUR_HOSTNAMES = {
|
||||||
smtp: Postal.config.dns.smtp_server_hostname,
|
smtp: Postal::Config.postal.smtp_hostname,
|
||||||
http: Postal.config.web.host
|
http: Postal::Config.postal.web_hostname
|
||||||
}.freeze
|
}.freeze
|
||||||
|
|
||||||
class << self
|
class << self
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
module Postal
|
|
||||||
class ReplySeparator
|
class ReplySeparator
|
||||||
|
|
||||||
RULES = [
|
RULES = [
|
||||||
@@ -33,4 +32,3 @@ module Postal
|
|||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
end
|
|
||||||
|
|||||||
@@ -32,7 +32,9 @@ module SMTPServer
|
|||||||
end
|
end
|
||||||
|
|
||||||
def check_ip_address
|
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
|
@logging_enabled = false
|
||||||
end
|
end
|
||||||
@@ -109,7 +111,7 @@ module SMTPServer
|
|||||||
@state = :welcome
|
@state = :welcome
|
||||||
log "\e[35m Client identified as #{@ip_address}\e[0m"
|
log "\e[35m Client identified as #{@ip_address}\e[0m"
|
||||||
increment_command_count("PROXY")
|
increment_command_count("PROXY")
|
||||||
"220 #{Postal.config.dns.smtp_server_hostname} ESMTP Postal/#{id}"
|
"220 #{Postal::Config.postal.smtp_hostname} ESMTP Postal/#{id}"
|
||||||
else
|
else
|
||||||
@finished = true
|
@finished = true
|
||||||
increment_error_count("proxy-error")
|
increment_error_count("proxy-error")
|
||||||
@@ -123,7 +125,7 @@ module SMTPServer
|
|||||||
end
|
end
|
||||||
|
|
||||||
def starttls
|
def starttls
|
||||||
if Postal.config.smtp_server.tls_enabled?
|
if Postal::Config.smtp_server.tls_enabled?
|
||||||
@start_tls = true
|
@start_tls = true
|
||||||
@tls = true
|
@tls = true
|
||||||
increment_command_count("STARTLS")
|
increment_command_count("STARTLS")
|
||||||
@@ -141,7 +143,7 @@ module SMTPServer
|
|||||||
increment_command_count("EHLO")
|
increment_command_count("EHLO")
|
||||||
[
|
[
|
||||||
"250-My capabilities are",
|
"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"
|
"250 AUTH CRAM-MD5 PLAIN LOGIN"
|
||||||
].compact
|
].compact
|
||||||
end
|
end
|
||||||
@@ -151,7 +153,7 @@ module SMTPServer
|
|||||||
transaction_reset
|
transaction_reset
|
||||||
@state = :welcomed
|
@state = :welcomed
|
||||||
increment_command_count("HELO")
|
increment_command_count("HELO")
|
||||||
"250 #{Postal.config.dns.smtp_server_hostname}"
|
"250 #{Postal::Config.postal.smtp_hostname}"
|
||||||
end
|
end
|
||||||
|
|
||||||
def rset
|
def rset
|
||||||
@@ -231,7 +233,7 @@ module SMTPServer
|
|||||||
increment_command_count("AUTH CRAM-MD5")
|
increment_command_count("AUTH CRAM-MD5")
|
||||||
|
|
||||||
challenge = Digest::SHA1.hexdigest(Time.now.to_i.to_s + rand(100_000).to_s)
|
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|
|
handler = proc do |idata|
|
||||||
@proc = nil
|
@proc = nil
|
||||||
@@ -309,7 +311,7 @@ module SMTPServer
|
|||||||
|
|
||||||
uname, tag = uname.split("+", 2)
|
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
|
# This is a return path
|
||||||
@state = :rcpt_to_received
|
@state = :rcpt_to_received
|
||||||
if server = ::Server.where(token: uname).first
|
if server = ::Server.where(token: uname).first
|
||||||
@@ -326,7 +328,7 @@ module SMTPServer
|
|||||||
"550 Invalid server token"
|
"550 Invalid server token"
|
||||||
end
|
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.
|
# This is an email direct to a route. This isn't actually supported yet.
|
||||||
@state = :rcpt_to_received
|
@state = :rcpt_to_received
|
||||||
if route = Route.where(token: uname).first
|
if route = Route.where(token: uname).first
|
||||||
@@ -446,14 +448,14 @@ module SMTPServer
|
|||||||
end
|
end
|
||||||
|
|
||||||
def finished
|
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
|
transaction_reset
|
||||||
@state = :welcomed
|
@state = :welcomed
|
||||||
increment_error_count("message-too-large")
|
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
|
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
|
transaction_reset
|
||||||
@state = :welcomed
|
@state = :welcomed
|
||||||
increment_error_count("loop-detected")
|
increment_error_count("loop-detected")
|
||||||
|
|||||||
@@ -8,6 +8,24 @@ module SMTPServer
|
|||||||
|
|
||||||
include HasPrometheusMetrics
|
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 = {})
|
def initialize(options = {})
|
||||||
@options = options
|
@options = options
|
||||||
@options[:debug] ||= false
|
@options[:debug] ||= false
|
||||||
@@ -43,16 +61,19 @@ module SMTPServer
|
|||||||
@ssl_context ||= begin
|
@ssl_context ||= begin
|
||||||
ssl_context = OpenSSL::SSL::SSLContext.new
|
ssl_context = OpenSSL::SSL::SSLContext.new
|
||||||
ssl_context.cert = Postal.smtp_certificates[0]
|
ssl_context.cert = Postal.smtp_certificates[0]
|
||||||
ssl_context.extra_chain_cert = Postal.smtp_certificates[1..]
|
ssl_context.extra_chain_cert = self.class.tls_certificates[1..]
|
||||||
ssl_context.key = Postal.smtp_private_key
|
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.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.ciphers = Postal::Config.smtp_server.tls_ciphers if Postal::Config.smtp_server.tls_ciphers
|
||||||
ssl_context
|
ssl_context
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def listen
|
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.autoclose = false
|
||||||
@server.close_on_exec = false
|
@server.close_on_exec = false
|
||||||
if defined?(Socket::SOL_SOCKET) && defined?(Socket::SO_KEEPALIVE)
|
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_KEEPINTVL, 10)
|
||||||
@server.setsockopt(Socket::SOL_TCP, Socket::TCP_KEEPCNT, 5)
|
@server.setsockopt(Socket::SOL_TCP, Socket::TCP_KEEPCNT, 5)
|
||||||
end
|
end
|
||||||
logger.info "Listening on #{Postal.config.smtp_server.bind_address}:#{Postal.config.smtp_server.port}"
|
|
||||||
|
logger.info "Listening on #{bind_address}:#{port}"
|
||||||
end
|
end
|
||||||
|
|
||||||
def unlisten
|
def unlisten
|
||||||
@@ -90,22 +112,22 @@ module SMTPServer
|
|||||||
# Accept the connection
|
# Accept the connection
|
||||||
new_io = io.accept
|
new_io = io.accept
|
||||||
increment_prometheus_counter :postal_smtp_server_connections_total
|
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
|
# If we are using the haproxy proxy protocol, we will be sent the
|
||||||
# client's IP later. Delay the welcome process.
|
# client's IP later. Delay the welcome process.
|
||||||
client = Client.new(nil)
|
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"
|
logger.debug "[#{client.id}] \e[35m Connection opened from #{new_io.remote_address.ip_address}\e[0m"
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
# We're not using the proxy protocol so we already know the client's IP
|
# We're not using the proxy protocol so we already know the client's IP
|
||||||
client = Client.new(new_io.remote_address.ip_address)
|
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"
|
logger.debug "[#{client.id}] \e[35m Connection opened from #{new_io.remote_address.ip_address}\e[0m"
|
||||||
end
|
end
|
||||||
# We know who the client is, welcome them.
|
# We know who the client is, welcome them.
|
||||||
client.log "\e[35m Client identified as #{new_io.remote_address.ip_address}\e[0m"
|
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
|
end
|
||||||
# Register the client and its socket with nio4r
|
# Register the client and its socket with nio4r
|
||||||
monitor = @io_selector.register(new_io, :r)
|
monitor = @io_selector.register(new_io, :r)
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
class ApplicationMailer < ActionMailer::Base
|
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
|
layout false
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ class BounceMessage
|
|||||||
mail.subject = "Mail Delivery Failed (#{@message.subject})"
|
mail.subject = "Mail Delivery Failed (#{@message.subject})"
|
||||||
mail.text_part = body
|
mail.text_part = body
|
||||||
mail.attachments["Original Message.eml"] = { mime_type: "message/rfc822", encoding: "quoted-printable", content: @message.raw_message }
|
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
|
mail.to_s
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -32,7 +32,7 @@ class BounceMessage
|
|||||||
end
|
end
|
||||||
|
|
||||||
def postmaster_address
|
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
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|||||||
@@ -49,10 +49,10 @@ module HasDNSChecks
|
|||||||
self.spf_status = "Missing"
|
self.spf_status = "Missing"
|
||||||
self.spf_error = "No SPF record exists for this domain"
|
self.spf_error = "No SPF record exists for this domain"
|
||||||
else
|
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?
|
if suitable_spf_records.empty?
|
||||||
self.spf_status = "Invalid"
|
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
|
false
|
||||||
else
|
else
|
||||||
self.spf_status = "OK"
|
self.spf_status = "OK"
|
||||||
@@ -108,11 +108,11 @@ module HasDNSChecks
|
|||||||
self.mx_status = "Missing"
|
self.mx_status = "Missing"
|
||||||
self.mx_error = "There are no MX records for #{name}"
|
self.mx_error = "There are no MX records for #{name}"
|
||||||
else
|
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?
|
if missing_records.empty?
|
||||||
self.mx_status = "OK"
|
self.mx_status = "OK"
|
||||||
self.mx_error = nil
|
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_status = "Missing"
|
||||||
self.mx_error = "You have MX records but none of them point to us."
|
self.mx_error = "You have MX records but none of them point to us."
|
||||||
else
|
else
|
||||||
@@ -136,12 +136,12 @@ module HasDNSChecks
|
|||||||
if records.empty?
|
if records.empty?
|
||||||
self.return_path_status = "Missing"
|
self.return_path_status = "Missing"
|
||||||
self.return_path_error = "There is no return path record at #{return_path_domain}"
|
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_status = "OK"
|
||||||
self.return_path_error = nil
|
self.return_path_error = nil
|
||||||
else
|
else
|
||||||
self.return_path_status = "Invalid"
|
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
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -104,7 +104,7 @@ class Domain < ApplicationRecord
|
|||||||
end
|
end
|
||||||
|
|
||||||
def spf_record
|
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
|
end
|
||||||
|
|
||||||
def dkim_record
|
def dkim_record
|
||||||
@@ -117,7 +117,7 @@ class Domain < ApplicationRecord
|
|||||||
def dkim_identifier
|
def dkim_identifier
|
||||||
return nil unless dkim_identifier_string
|
return nil unless dkim_identifier_string
|
||||||
|
|
||||||
Postal.config.dns.dkim_identifier + "-#{dkim_identifier_string}"
|
Postal::Config.dns.dkim_identifier + "-#{dkim_identifier_string}"
|
||||||
end
|
end
|
||||||
|
|
||||||
def dkim_record_name
|
def dkim_record_name
|
||||||
@@ -128,7 +128,7 @@ class Domain < ApplicationRecord
|
|||||||
end
|
end
|
||||||
|
|
||||||
def return_path_domain
|
def return_path_domain
|
||||||
"#{Postal.config.dns.custom_return_path_prefix}.#{name}"
|
"#{Postal::Config.dns.custom_return_path_prefix}.#{name}"
|
||||||
end
|
end
|
||||||
|
|
||||||
# Returns a DNSResolver instance that can be used to perform DNS lookups needed for
|
# Returns a DNSResolver instance that can be used to perform DNS lookups needed for
|
||||||
@@ -136,13 +136,13 @@ class Domain < ApplicationRecord
|
|||||||
#
|
#
|
||||||
# @return [DNSResolver]
|
# @return [DNSResolver]
|
||||||
def resolver
|
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)
|
@resolver ||= DNSResolver.for_domain(name)
|
||||||
end
|
end
|
||||||
|
|
||||||
def dns_verification_string
|
def dns_verification_string
|
||||||
"#{Postal.config.dns.domain_verify_prefix} #{verification_token}"
|
"#{Postal::Config.dns.domain_verify_prefix} #{verification_token}"
|
||||||
end
|
end
|
||||||
|
|
||||||
def verify_with_dns
|
def verify_with_dns
|
||||||
|
|||||||
@@ -88,7 +88,7 @@ class IncomingMessagePrototype
|
|||||||
mail.from = @from
|
mail.from = @from
|
||||||
mail.subject = @subject
|
mail.subject = @subject
|
||||||
mail.text_part = @plain_body
|
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|
|
attachments.each do |attachment|
|
||||||
mail.attachments[attachment[:name]] = {
|
mail.attachments[attachment[:name]] = {
|
||||||
mime_type: attachment[:content_type],
|
mime_type: attachment[:content_type],
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ class OutgoingMessagePrototype
|
|||||||
@source_type = source_type
|
@source_type = source_type
|
||||||
@custom_headers = {}
|
@custom_headers = {}
|
||||||
@attachments = []
|
@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|
|
attributes.each do |key, value|
|
||||||
instance_variable_set("@#{key}", value)
|
instance_variable_set("@#{key}", value)
|
||||||
end
|
end
|
||||||
|
|||||||
+1
-1
@@ -89,7 +89,7 @@ class Route < ApplicationRecord
|
|||||||
end
|
end
|
||||||
|
|
||||||
def forward_address
|
def forward_address
|
||||||
@forward_address ||= "#{token}@#{Postal.config.dns.route_domain}"
|
@forward_address ||= "#{token}@#{Postal::Config.dns.route_domain}"
|
||||||
end
|
end
|
||||||
|
|
||||||
def wildcard?
|
def wildcard?
|
||||||
|
|||||||
@@ -71,8 +71,8 @@ class Server < ApplicationRecord
|
|||||||
default_value :raw_message_retention_days, -> { 30 }
|
default_value :raw_message_retention_days, -> { 30 }
|
||||||
default_value :raw_message_retention_size, -> { 2048 }
|
default_value :raw_message_retention_size, -> { 2048 }
|
||||||
default_value :message_retention_days, -> { 60 }
|
default_value :message_retention_days, -> { 60 }
|
||||||
default_value :spam_threshold, -> { Postal.config.general.default_spam_threshold }
|
default_value :spam_threshold, -> { Postal::Config.postal.default_spam_threshold }
|
||||||
default_value :spam_failure_threshold, -> { Postal.config.general.default_spam_failure_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 :name, presence: true, uniqueness: { scope: :organization_id, case_sensitive: false }
|
||||||
validates :mode, inclusion: { in: MODES }
|
validates :mode, inclusion: { in: MODES }
|
||||||
|
|||||||
@@ -58,12 +58,12 @@ class TrackDomain < ApplicationRecord
|
|||||||
if records.empty?
|
if records.empty?
|
||||||
self.dns_status = "Missing"
|
self.dns_status = "Missing"
|
||||||
self.dns_error = "There is no record at #{full_name}"
|
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_status = "OK"
|
||||||
self.dns_error = nil
|
self.dns_error = nil
|
||||||
else
|
else
|
||||||
self.dns_status = "Invalid"
|
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
|
end
|
||||||
self.dns_checked_at = Time.now
|
self.dns_checked_at = Time.now
|
||||||
save!
|
save!
|
||||||
|
|||||||
@@ -47,8 +47,8 @@ class SMTPSender < BaseSender
|
|||||||
end
|
end
|
||||||
|
|
||||||
smtp_client = Net::SMTP.new(@remote_ip, port)
|
smtp_client = Net::SMTP.new(@remote_ip, port)
|
||||||
smtp_client.open_timeout = Postal.config.smtp_client.open_timeout
|
smtp_client.open_timeout = Postal::Config.smtp_client.open_timeout
|
||||||
smtp_client.read_timeout = Postal.config.smtp_client.read_timeout
|
smtp_client.read_timeout = Postal::Config.smtp_client.read_timeout
|
||||||
smtp_client.tls_hostname = hostname
|
smtp_client.tls_hostname = hostname
|
||||||
|
|
||||||
if @source_ip_address
|
if @source_ip_address
|
||||||
@@ -140,9 +140,9 @@ class SMTPSender < BaseSender
|
|||||||
elsif message.domain.return_path_status == "OK"
|
elsif message.domain.return_path_status == "OK"
|
||||||
mail_from = "#{message.server.token}@#{message.domain.return_path_domain}"
|
mail_from = "#{message.server.token}@#{message.domain.return_path_domain}"
|
||||||
else
|
else
|
||||||
mail_from = "#{message.server.token}@#{Postal.config.dns.return_path}"
|
mail_from = "#{message.server.token}@#{Postal::Config.dns.return_path_domain}"
|
||||||
end
|
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
|
raw_message = "Resent-Sender: #{mail_from}\r\n" + message.raw_message
|
||||||
else
|
else
|
||||||
raw_message = message.raw_message
|
raw_message = message.raw_message
|
||||||
@@ -270,15 +270,20 @@ class SMTPSender < BaseSender
|
|||||||
end
|
end
|
||||||
|
|
||||||
def default_helo_hostname
|
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
|
end
|
||||||
|
|
||||||
def relay_hosts
|
def relay_hosts
|
||||||
hosts = Postal.config.smtp_relays.map do |relay|
|
relays = Postal::Config.postal.smtp_relays
|
||||||
next unless relay.hostname.present?
|
return nil if relays.nil?
|
||||||
|
|
||||||
|
hosts = relays.map do |relay|
|
||||||
|
next unless relay.host.present?
|
||||||
|
|
||||||
{
|
{
|
||||||
hostname: relay.hostname,
|
hostname: relay.host,
|
||||||
port: relay.port,
|
port: relay.port,
|
||||||
ssl_mode: relay.ssl_mode
|
ssl_mode: relay.ssl_mode
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -51,8 +51,9 @@ class HealthServer
|
|||||||
|
|
||||||
class << self
|
class << self
|
||||||
|
|
||||||
def run(default_port: 9090, **options)
|
def run(default_port:, default_bind_address:, **options)
|
||||||
port = ENV.fetch("HEALTH_SERVER_PORT", default_port)
|
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),
|
Rack::Handler::WEBrick.run(new(**options),
|
||||||
Port: port,
|
Port: port,
|
||||||
@@ -64,10 +65,6 @@ class HealthServer
|
|||||||
"in use, not starting health server"
|
"in use, not starting health server"
|
||||||
end
|
end
|
||||||
|
|
||||||
def bind_address
|
|
||||||
ENV.fetch("HEALTH_SERVER_BIND_ADDRESS", "127.0.0.1")
|
|
||||||
end
|
|
||||||
|
|
||||||
def start(**options)
|
def start(**options)
|
||||||
thread = Thread.new { run(**options) }
|
thread = Thread.new { run(**options) }
|
||||||
thread.abort_on_exception = false
|
thread.abort_on_exception = false
|
||||||
|
|||||||
@@ -8,5 +8,5 @@ If you didn't request this, you can ignore this e-mail.
|
|||||||
|
|
||||||
Thanks,
|
Thanks,
|
||||||
|
|
||||||
<%= Postal.smtp_from_name %>
|
<%= Postal::Config.smtp.from_name %>
|
||||||
<%= Postal.smtp_from_address %>
|
<%= Postal::Config.smtp.from_address %>
|
||||||
|
|||||||
@@ -13,5 +13,5 @@ You can view more information about this server at:
|
|||||||
|
|
||||||
Thanks,
|
Thanks,
|
||||||
|
|
||||||
<%= Postal.smtp_from_name %>
|
<%= Postal::Config.smtp.from_name %>
|
||||||
<%= Postal.smtp_from_address %>
|
<%= Postal::Config.smtp.from_address %>
|
||||||
|
|||||||
@@ -13,5 +13,5 @@ You can view more information about this server at:
|
|||||||
|
|
||||||
Thanks,
|
Thanks,
|
||||||
|
|
||||||
<%= Postal.smtp_from_name %>
|
<%= Postal::Config.smtp.from_name %>
|
||||||
<%= Postal.smtp_from_address %>
|
<%= Postal::Config.smtp.from_address %>
|
||||||
|
|||||||
@@ -8,5 +8,5 @@ Reason: <%= @server.actual_suspension_reason %>
|
|||||||
|
|
||||||
Thanks,
|
Thanks,
|
||||||
|
|
||||||
<%= Postal.smtp_from_name %>
|
<%= Postal::Config.smtp.from_name %>
|
||||||
<%= Postal.smtp_from_address %>
|
<%= Postal::Config.smtp.from_address %>
|
||||||
|
|||||||
@@ -10,5 +10,5 @@ If you don't agree, just ignore this e-mail.
|
|||||||
|
|
||||||
Thanks,
|
Thanks,
|
||||||
|
|
||||||
<%= Postal.smtp_from_name %>
|
<%= Postal::Config.smtp.from_name %>
|
||||||
<%= Postal.smtp_from_address %>
|
<%= Postal::Config.smtp.from_address %>
|
||||||
|
|||||||
@@ -43,7 +43,7 @@
|
|||||||
%p.pageContent__text
|
%p.pageContent__text
|
||||||
You need to add a TXT record at the apex/root of your domain (@) with the following
|
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
|
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
|
%pre.codeBlock.u-margin= @domain.spf_record
|
||||||
|
|
||||||
%h3.pageContent__subTitle DKIM Record
|
%h3.pageContent__subTitle DKIM Record
|
||||||
@@ -78,7 +78,7 @@
|
|||||||
%p.pageContent__text
|
%p.pageContent__text
|
||||||
This is optional but we recommend adding this to improve deliverability. You should add
|
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.
|
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
|
%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
|
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
|
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>.
|
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
|
%dl.pageContent__definitions
|
||||||
%dt MX Records
|
%dt MX Records
|
||||||
%dd
|
%dd
|
||||||
- for mx in Postal.config.dns.mx_records
|
- for mx in Postal::Config.dns.mx_records
|
||||||
%p.pageContent__definitionCode= mx
|
%p.pageContent__definitionCode= mx
|
||||||
|
|
||||||
|
|||||||
@@ -18,7 +18,7 @@
|
|||||||
Mail servers can be enabled to send mail from any domain by the administrator.
|
Mail servers can be enabled to send mail from any domain by the administrator.
|
||||||
%li
|
%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 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
|
.u-margin
|
||||||
%h2.pageContent__subTitle Sending using SMTP
|
%h2.pageContent__subTitle Sending using SMTP
|
||||||
%p.pageContent__text
|
%p.pageContent__text
|
||||||
@@ -27,10 +27,10 @@
|
|||||||
%dl.pageContent__definitions
|
%dl.pageContent__definitions
|
||||||
%dt SMTP Server Address
|
%dt SMTP Server Address
|
||||||
%dd
|
%dd
|
||||||
%p.pageContent__definitionCode= Postal.config.dns.smtp_server_hostname
|
%p.pageContent__definitionCode= Postal::Config.postal.smtp_hostname
|
||||||
%dt Port
|
%dt Port
|
||||||
%dd
|
%dd
|
||||||
%p.pageContent__definitionCode= Postal.config.smtp_server.port
|
%p.pageContent__definitionCode= Postal::Config.smtp_server.default_port
|
||||||
%p.pageContent__definitionText
|
%p.pageContent__definitionText
|
||||||
The SMTP service supports STARTTLS if you wish to send messages securely. Be aware that security
|
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.
|
cannot guaranteed all the way to their final destination.
|
||||||
|
|||||||
@@ -14,7 +14,7 @@
|
|||||||
- else
|
- else
|
||||||
%p.pageContent__intro.u-margin
|
%p.pageContent__intro.u-margin
|
||||||
When messages cannot be delivered, addresses are added to the suppression list which stops
|
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
|
%ul.suppressionList
|
||||||
- for suppression in @suppressions[:records]
|
- for suppression in @suppressions[:records]
|
||||||
%li.suppressionList__item
|
%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?
|
= 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
|
%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
|
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
|
.fieldSet__field
|
||||||
= f.label :ssl_enabled, :class => 'fieldSet__label'
|
= f.label :ssl_enabled, :class => 'fieldSet__label'
|
||||||
@@ -47,4 +47,3 @@
|
|||||||
|
|
||||||
.fieldSetSubmit.buttonSet
|
.fieldSetSubmit.buttonSet
|
||||||
= f.submit @track_domain.new_record? ? "Create Track Domain" : "Save Track Domain", :class => 'button button--positive js-form-submit'
|
= 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.
|
# This file is used by Rack-based servers to start the application.
|
||||||
|
|
||||||
require_relative "config/environment"
|
require_relative "config/environment"
|
||||||
$0 = "[postal] #{ENV.fetch('PROC_NAME', nil)}"
|
|
||||||
run Rails.application
|
run Rails.application
|
||||||
|
|||||||
@@ -38,9 +38,9 @@ module Postal
|
|||||||
require "tracking_middleware"
|
require "tracking_middleware"
|
||||||
config.middleware.insert_before ActionDispatch::HostAuthorization, TrackingMiddleware
|
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")
|
config.logger = Logger.new("/dev/null")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
+1
-2
@@ -5,6 +5,5 @@ ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__)
|
|||||||
require "bundler/setup" # Set up gems listed in the Gemfile.
|
require "bundler/setup" # Set up gems listed in the Gemfile.
|
||||||
|
|
||||||
require_relative "../lib/postal/config"
|
require_relative "../lib/postal/config"
|
||||||
Postal.check_config!
|
|
||||||
|
|
||||||
ENV["RAILS_ENV"] = Postal.config.rails&.environment || "development"
|
ENV["RAILS_ENV"] = Postal::Config.rails.environment || "development"
|
||||||
|
|||||||
+7
-7
@@ -1,13 +1,13 @@
|
|||||||
default: &default
|
default: &default
|
||||||
adapter: mysql2
|
adapter: mysql2
|
||||||
reconnect: true
|
reconnect: true
|
||||||
encoding: <%= Postal.config.main_db.encoding %>
|
encoding: "<%= Postal::Config.main_db.encoding %>"
|
||||||
pool: <%= Postal.config.main_db.pool_size %>
|
pool: <%= Postal::Config.main_db.pool_size %>
|
||||||
username: <%= Postal.config.main_db.username %>
|
username: "<%= Postal::Config.main_db.username %>"
|
||||||
password: <%= Postal.config.main_db.password %>
|
password: "<%= Postal::Config.main_db.password %>"
|
||||||
host: <%= Postal.config.main_db.host %>
|
host: "<%= Postal::Config.main_db.host %>"
|
||||||
port: <%= Postal.config.main_db.port %>
|
port: <%= Postal::Config.main_db.port %>
|
||||||
database: <%= Postal.config.main_db.database %>
|
database: "<%= Postal::Config.main_db.database %>"
|
||||||
|
|
||||||
development:
|
development:
|
||||||
<<: *default
|
<<: *default
|
||||||
|
|||||||
@@ -1,17 +1,13 @@
|
|||||||
web:
|
# This is an example Postal configuration file for use in
|
||||||
host: postal.example.dev
|
# development environments. For a production example, see
|
||||||
protocol: https
|
# the https://github.com/postalserver/install repository.
|
||||||
|
|
||||||
web_server:
|
version: 2
|
||||||
bind_address: 0.0.0.0
|
|
||||||
port: 4010
|
|
||||||
|
|
||||||
smtp_server:
|
postal:
|
||||||
port: 2525
|
web_hostname: postal.example.com
|
||||||
|
web_protocol: https
|
||||||
logging:
|
smtp_hostname: postal.example.com
|
||||||
rails_log: true
|
|
||||||
stdout: true
|
|
||||||
|
|
||||||
main_db:
|
main_db:
|
||||||
host: 127.0.0.1
|
host: 127.0.0.1
|
||||||
@@ -25,13 +21,9 @@ message_db:
|
|||||||
password:
|
password:
|
||||||
prefix: postal
|
prefix: postal
|
||||||
|
|
||||||
smtp:
|
logging:
|
||||||
host: 127.0.0.1
|
rails_log_enabled: true
|
||||||
port: 2525
|
highlighting_enabled: true
|
||||||
username:
|
|
||||||
password:
|
|
||||||
from_name: Postal
|
|
||||||
from_address: postal@yourdomain.com
|
|
||||||
|
|
||||||
rails:
|
rails:
|
||||||
environment: development
|
environment: development
|
||||||
@@ -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
|
# frozen_string_literal: true
|
||||||
|
|
||||||
if Postal.config.rails&.secret_key
|
if Postal::Config.rails.secret_key
|
||||||
Rails.application.secrets.secret_key_base = Postal.config.rails.secret_key
|
Rails.application.secrets.secret_key_base = Postal::Config.rails.secret_key
|
||||||
else
|
else
|
||||||
warn "No secret key was specified in the Postal config file. Using one for just this session"
|
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)
|
Rails.application.secrets.secret_key_base = SecureRandom.hex(128)
|
||||||
|
|||||||
@@ -2,8 +2,8 @@
|
|||||||
|
|
||||||
require "postal/config"
|
require "postal/config"
|
||||||
|
|
||||||
if Postal.config.general&.exception_url
|
if Postal::Config.logging.sentry_dsn
|
||||||
Sentry.init do |config|
|
Sentry.init do |config|
|
||||||
config.dsn = Postal.config.general.exception_url
|
config.dsn = Postal::Config.logging.sentry_dsn
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -2,9 +2,10 @@
|
|||||||
|
|
||||||
require "postal/config"
|
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.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 }
|
ActionMailer::Base.smtp_settings = {
|
||||||
end
|
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') %>
|
|
||||||
+5
-4
@@ -1,11 +1,12 @@
|
|||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require_relative "../lib/postal/config"
|
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
|
threads threads_count, threads_count
|
||||||
bind_address = Postal.config.web_server&.bind_address || "127.0.0.1"
|
bind_address = ENV.fetch("BIND_ADDRESS", Postal::Config.web_server.default_bind_address)
|
||||||
bind_port = Postal.config.web_server&.port&.to_i || ENV["PORT"] || 5000
|
bind_port = ENV.fetch("PORT", Postal::Config.web_server.default_port)
|
||||||
bind "tcp://#{bind_address}:#{bind_port}"
|
bind "tcp://#{bind_address}:#{bind_port}"
|
||||||
environment Postal.config.rails&.environment || "development"
|
environment Postal::Config.rails.environment || "development"
|
||||||
prune_bundler
|
prune_bundler
|
||||||
quiet false
|
quiet false
|
||||||
|
|||||||
@@ -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.
|
||||||
@@ -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 |
|
||||||
@@ -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
-2
@@ -8,8 +8,14 @@ services:
|
|||||||
volumes:
|
volumes:
|
||||||
- "./docker/ci-config:/config"
|
- "./docker/ci-config:/config"
|
||||||
environment:
|
environment:
|
||||||
POSTAL_CONFIG_ROOT: /config
|
POSTAL_SIGNING_KEY_PATH: /config/signing.key
|
||||||
KATAPULT_CONFIG_FILE: /ci-config.yml
|
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_TIMEOUT: 90
|
||||||
WAIT_FOR_TARGETS: |-
|
WAIT_FOR_TARGETS: |-
|
||||||
mariadb:3306
|
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
|
class MigrationWaiter
|
||||||
|
|
||||||
ATTEMPTS = ENV.fetch("MIGRATION_WAITER_ATTEMPTS", 120)
|
ATTEMPTS = Postal::Config.migration_waiter.attempts
|
||||||
SLEEP_TIME = ENV.fetch("MIGRATION_WAITER_SLEEP_TIME", 2)
|
SLEEP_TIME = Postal::Config.migration_waiter.sleep_time
|
||||||
|
|
||||||
class << self
|
class << self
|
||||||
|
|
||||||
@@ -35,7 +35,7 @@ class MigrationWaiter
|
|||||||
|
|
||||||
def wait_if_appropriate
|
def wait_if_appropriate
|
||||||
# Don't wait if not configured
|
# 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
|
# Don't wait in the console, rake tasks or rails commands
|
||||||
return if console? || rake_task? || rails_command?
|
return if console? || rake_task? || rails_command?
|
||||||
|
|||||||
+78
-145
@@ -6,190 +6,119 @@ require "pathname"
|
|||||||
require "cgi"
|
require "cgi"
|
||||||
require "openssl"
|
require "openssl"
|
||||||
require "fileutils"
|
require "fileutils"
|
||||||
|
require "konfig"
|
||||||
|
require "konfig/sources/environment"
|
||||||
|
require "konfig/sources/yaml"
|
||||||
|
require "dotenv"
|
||||||
|
require "klogger"
|
||||||
|
|
||||||
require_relative "error"
|
require_relative "error"
|
||||||
require_relative "version"
|
require_relative "version"
|
||||||
|
require_relative "config_schema"
|
||||||
|
require_relative "legacy_config_source"
|
||||||
|
|
||||||
module Postal
|
module Postal
|
||||||
|
|
||||||
# rubocop:disable Lint/EmptyClass
|
class << self
|
||||||
class Config
|
|
||||||
end
|
|
||||||
# rubocop:enable Lint/EmptyClass
|
|
||||||
|
|
||||||
def self.host
|
# Return the path to the config file
|
||||||
@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 [Klogger::Logger] A logger instance
|
# @return [String]
|
||||||
def self.logger
|
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
|
@logger ||= begin
|
||||||
k = Klogger.new(nil, destination: Rails.env.test? ? "/dev/null" : $stdout, highlight: Rails.env.development?)
|
k = Klogger.new(nil, destination: Config.logging.enabled? ? $stdout : "/dev/null", highlight: Config.logging.highlighting_enabled?)
|
||||||
k.add_destination(graylog_logging_destination) if config.logging&.graylog&.host.present?
|
k.add_destination(graylog_logging_destination) if Config.gelf.host.present?
|
||||||
k
|
k
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.process_name
|
def process_name
|
||||||
@process_name ||= begin
|
@process_name ||= begin
|
||||||
string = "host:#{Socket.gethostname} pid:#{Process.pid}"
|
"host:#{Socket.gethostname} pid:#{Process.pid}"
|
||||||
string += " procname:#{ENV['PROC_NAME']}" if ENV["PROC_NAME"]
|
|
||||||
string
|
|
||||||
rescue StandardError
|
rescue StandardError
|
||||||
"pid:#{Process.pid}"
|
"pid:#{Process.pid}"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.locker_name
|
def locker_name
|
||||||
string = process_name.dup
|
string = process_name.dup
|
||||||
string += " job:#{Thread.current[:job_id]}" if Thread.current[:job_id]
|
string += " job:#{Thread.current[:job_id]}" if Thread.current[:job_id]
|
||||||
string += " thread:#{Thread.current.native_thread_id}"
|
string += " thread:#{Thread.current.native_thread_id}"
|
||||||
string
|
string
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.locker_name_with_suffix(suffix)
|
def locker_name_with_suffix(suffix)
|
||||||
"#{locker_name} #{suffix}"
|
"#{locker_name} #{suffix}"
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.smtp_from_name
|
def signing_key
|
||||||
config.smtp&.from_name || "Postal"
|
@signing_key ||= OpenSSL::PKey::RSA.new(File.read(Config.postal.signing_key_path))
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.smtp_from_address
|
def rp_dkim_dns_record
|
||||||
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
|
|
||||||
public_key = signing_key.public_key.to_s.gsub(/-+[A-Z ]+-+\n/, "").gsub(/\n/, "")
|
public_key = signing_key.public_key.to_s.gsub(/-+[A-Z ]+-+\n/, "").gsub(/\n/, "")
|
||||||
"v=DKIM1; t=s; h=sha256; p=#{public_key};"
|
"v=DKIM1; t=s; h=sha256; p=#{public_key};"
|
||||||
end
|
end
|
||||||
|
|
||||||
class ConfigError < Postal::Error
|
def ip_pools?
|
||||||
|
Config.postal.use_ip_pools?
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.check_config!
|
def graylog_logging_destination
|
||||||
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
|
|
||||||
@graylog_logging_destination ||= begin
|
@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|
|
proc do |_logger, payload, group_ids|
|
||||||
short_message = payload.delete(:message) || "[message missing]"
|
short_message = payload.delete(:message) || "[message missing]"
|
||||||
notifier.notify!(short_message: short_message, **{
|
notifier.notify!(short_message: short_message, **{
|
||||||
facility: config.logging.graylog.facility,
|
facility: Config.gelf.facility,
|
||||||
_environment: Rails.env.to_s,
|
_environment: Config.rails.environment,
|
||||||
_version: Postal::VERSION.to_s,
|
_version: Postal::VERSION.to_s,
|
||||||
_group_ids: group_ids.join(" ")
|
_group_ids: group_ids.join(" ")
|
||||||
}.merge(payload.transform_keys { |k| "_#{k}".to_sym }.transform_values(&:to_s)))
|
}.merge(payload.transform_keys { |k| "_#{k}".to_sym }.transform_values(&:to_s)))
|
||||||
@@ -198,3 +127,7 @@ module Postal
|
|||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
Config = initialize_config
|
||||||
|
|
||||||
|
end
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -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
|
def establish_connection
|
||||||
Mysql2::Client.new(
|
Mysql2::Client.new(
|
||||||
host: Postal.config.message_db.host,
|
host: Postal::Config.message_db.host,
|
||||||
username: Postal.config.message_db.username,
|
username: Postal::Config.message_db.username,
|
||||||
password: Postal.config.message_db.password,
|
password: Postal::Config.message_db.password,
|
||||||
port: Postal.config.message_db.port,
|
port: Postal::Config.message_db.port,
|
||||||
encoding: Postal.config.message_db.encoding || "utf8mb4"
|
encoding: Postal::Config.message_db.encoding
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -272,7 +272,7 @@ module Postal
|
|||||||
# Return the correct database name
|
# Return the correct database name
|
||||||
#
|
#
|
||||||
def 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
|
end
|
||||||
|
|
||||||
#
|
#
|
||||||
|
|||||||
@@ -128,7 +128,7 @@ module Postal
|
|||||||
#
|
#
|
||||||
def create_delivery(status, options = {})
|
def create_delivery(status, options = {})
|
||||||
delivery = Delivery.create(self, options.merge(status: status))
|
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)
|
update(status: status, last_delivery_attempt: delivery.timestamp.to_f, held: status == "Held", hold_expiry: hold_expiry)
|
||||||
delivery
|
delivery
|
||||||
end
|
end
|
||||||
@@ -511,7 +511,7 @@ module Postal
|
|||||||
# Was thsi message sent to a return path?
|
# Was thsi message sent to a return path?
|
||||||
#
|
#
|
||||||
def rcpt_to_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
|
end
|
||||||
|
|
||||||
#
|
#
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ module Postal
|
|||||||
end
|
end
|
||||||
|
|
||||||
def add(type, address, options = {})
|
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
|
if existing = @database.select("suppressions", where: { type: type, address: address }, limit: 1).first
|
||||||
reason = options[:reason] || existing["reason"]
|
reason = options[:reason] || existing["reason"]
|
||||||
@database.update("suppressions", { reason: reason, keep_until: keep_until }, where: { id: existing["id"] })
|
@database.update("suppressions", { reason: reason, keep_until: keep_until }, where: { id: existing["id"] })
|
||||||
|
|||||||
@@ -24,14 +24,14 @@ module Postal
|
|||||||
# installation.
|
# installation.
|
||||||
def inspectors
|
def inspectors
|
||||||
[].tap do |inspectors|
|
[].tap do |inspectors|
|
||||||
if Postal.config.rspamd&.enabled
|
if Postal::Config.rspamd.enabled?
|
||||||
inspectors << MessageInspectors::Rspamd.new(Postal.config.rspamd)
|
inspectors << MessageInspectors::Rspamd.new(Postal::Config.rspamd)
|
||||||
elsif Postal.config.spamd&.enabled
|
elsif Postal::Config.spamd.enabled?
|
||||||
inspectors << MessageInspectors::SpamAssassin.new(Postal.config.spamd)
|
inspectors << MessageInspectors::SpamAssassin.new(Postal::Config.spamd)
|
||||||
end
|
end
|
||||||
|
|
||||||
if Postal.config.clamav&.enabled
|
if Postal::Config.clamav.enabled?
|
||||||
inspectors << MessageInspectors::Clamav.new(Postal.config.clamav)
|
inspectors << MessageInspectors::Clamav.new(Postal::Config.clamav)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -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
|
server.message_db.provisioner.migrate
|
||||||
end
|
end
|
||||||
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
|
end
|
||||||
|
|
||||||
Rake::Task["db:migrate"].enhance do
|
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 File.expand_path("../lib/postal/config", __dir__)
|
||||||
require "openssl"
|
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
|
key = OpenSSL::PKey::RSA.new(2048).to_s
|
||||||
File.write(Postal.smtp_private_key_path, key)
|
File.write(key_path, key)
|
||||||
puts "Created new private key for encrypting SMTP connections"
|
puts "Created new private key for encrypting SMTP connections at #{key_path}"
|
||||||
end
|
end
|
||||||
|
|
||||||
unless File.exist?(Postal.smtp_certificate_path)
|
unless File.exist?(cert_path)
|
||||||
cert = OpenSSL::X509::Certificate.new
|
cert = OpenSSL::X509::Certificate.new
|
||||||
cert.subject = cert.issuer = OpenSSL::X509::Name.parse("/C=GB/O=Test/OU=Test/CN=Test")
|
cert.subject = cert.issuer = OpenSSL::X509::Name.parse("/C=GB/O=Test/OU=Test/CN=Test")
|
||||||
cert.not_before = Time.now
|
cert.not_before = Time.now
|
||||||
cert.not_after = Time.now + (365 * 24 * 60 * 60)
|
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.serial = 0x0
|
||||||
cert.version = 2
|
cert.version = 2
|
||||||
cert.sign Postal.smtp_private_key, OpenSSL::Digest.new("SHA256")
|
cert.sign SMTPServer::Server.tls_private_key, OpenSSL::Digest.new("SHA256")
|
||||||
File.write(Postal.smtp_certificate_path, cert.to_pem)
|
File.write(cert_path, cert.to_pem)
|
||||||
puts "Created new self signed certificate for encrypting SMTP connections"
|
puts "Created new self signed certificate for encrypting SMTP connections at #{cert_path}"
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ end
|
|||||||
|
|
||||||
message = server.message_db.new_message
|
message = server.message_db.new_message
|
||||||
message.scope = "incoming"
|
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.mail_from = "MAILER-DAEMON@smtp.infra.atech.io"
|
||||||
message.raw_message = template
|
message.raw_message = template
|
||||||
message.bounce = true
|
message.bounce = true
|
||||||
|
|||||||
@@ -5,11 +5,11 @@ require_relative "../lib/postal/config"
|
|||||||
require "mysql2"
|
require "mysql2"
|
||||||
|
|
||||||
client = Mysql2::Client.new(
|
client = Mysql2::Client.new(
|
||||||
host: Postal.config.main_db.host,
|
host: Postal::Config.main_db.host,
|
||||||
username: Postal.config.main_db.username,
|
username: Postal::Config.main_db.username,
|
||||||
password: Postal.config.main_db.password,
|
password: Postal::Config.main_db.password,
|
||||||
port: Postal.config.main_db.port,
|
port: Postal::Config.main_db.port,
|
||||||
database: Postal.config.main_db.database
|
database: Postal::Config.main_db.database
|
||||||
)
|
)
|
||||||
result = client.query("SELECT COUNT(id) as size FROM `queued_messages` WHERE retry_after IS NULL OR " \
|
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")
|
"retry_after <= ADDTIME(UTC_TIMESTAMP(), '30') AND locked_at IS NULL")
|
||||||
|
|||||||
@@ -5,5 +5,9 @@ $stderr.sync = true
|
|||||||
|
|
||||||
require_relative "../config/environment"
|
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
|
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 "\e[31mMessage was not delivered successfully to SMTP server.\e[0m"
|
||||||
puts "Error: #{e.class} (#{e.message})"
|
puts "Error: #{e.class} (#{e.message})"
|
||||||
puts
|
puts
|
||||||
puts " SMTP Host: #{Postal.config.smtp.host}"
|
puts " SMTP Host: #{Postal::Config.smtp.host}"
|
||||||
puts " SMTP Port: #{Postal.config.smtp.port}"
|
puts " SMTP Port: #{Postal::Config.smtp.port}"
|
||||||
puts " SMTP Username: #{Postal.config.smtp.username}"
|
puts " SMTP Username: #{Postal::Config.smtp.username}"
|
||||||
puts " SMTP Password: #{Postal.config.smtp.password}"
|
puts " SMTP Password: #{Postal::Config.smtp.password}"
|
||||||
puts
|
puts
|
||||||
end
|
end
|
||||||
|
|||||||
+6
-1
@@ -6,5 +6,10 @@ $stderr.sync = true
|
|||||||
|
|
||||||
require_relative "../config/environment"
|
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
|
Worker::Process.new.run
|
||||||
|
|||||||
@@ -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
|
end
|
||||||
|
|
||||||
context "when the number of attempts is more than the maximum" do
|
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
|
it "logs" do
|
||||||
processor.process
|
processor.process
|
||||||
|
|||||||
@@ -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
|
it "returns the correct string" do
|
||||||
result = described_class.generate(nil, "testhelo", "1.1.1.1", :smtp)
|
result = described_class.generate(nil, "testhelo", "1.1.1.1", :smtp)
|
||||||
expect(result).to eq "from testhelo (hostname.com [1.1.1.1]) " \
|
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}"
|
"with SMTP; #{Time.now.utc.rfc2822}"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -21,7 +21,7 @@ describe ReceivedHeader do
|
|||||||
it "returns the correct string" do
|
it "returns the correct string" do
|
||||||
server = Server.new(privacy_mode: true)
|
server = Server.new(privacy_mode: true)
|
||||||
result = described_class.generate(server, "testhelo", "1.1.1.1", :smtp)
|
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}"
|
"with SMTP; #{Time.now.utc.rfc2822}"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -31,7 +31,7 @@ describe ReceivedHeader do
|
|||||||
server = Server.new(privacy_mode: false)
|
server = Server.new(privacy_mode: false)
|
||||||
result = described_class.generate(server, "testhelo", "1.1.1.1", :smtp)
|
result = described_class.generate(server, "testhelo", "1.1.1.1", :smtp)
|
||||||
expect(result).to eq "from testhelo (hostname.com [1.1.1.1]) " \
|
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}"
|
"with SMTP; #{Time.now.utc.rfc2822}"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -40,7 +40,7 @@ describe ReceivedHeader do
|
|||||||
it "returns the correct string" do
|
it "returns the correct string" do
|
||||||
result = described_class.generate(nil, "web-ui", "1.1.1.1", :http)
|
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]) " \
|
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}"
|
"with HTTP; #{Time.now.utc.rfc2822}"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ module SMTPServer
|
|||||||
client.handle("RCPT TO: #{route.name}@#{route.domain.name}")
|
client.handle("RCPT TO: #{route.name}@#{route.domain.name}")
|
||||||
Timecop.freeze do
|
Timecop.freeze do
|
||||||
client.handle("DATA")
|
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
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -72,7 +72,7 @@ module SMTPServer
|
|||||||
client.handle("This is some content for the message.")
|
client.handle("This is some content for the message.")
|
||||||
client.handle("It will keep going.")
|
client.handle("It will keep going.")
|
||||||
expect(client.instance_variable_get("@data")).to eq <<~DATA
|
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
|
Subject: Test\r
|
||||||
\r
|
\r
|
||||||
This is some content for the message.\r
|
This is some content for the message.\r
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ module SMTPServer
|
|||||||
describe "when finished sending data" do
|
describe "when finished sending data" do
|
||||||
context "when the data is larger than the maximum message size" do
|
context "when the data is larger than the maximum message size" do
|
||||||
it "returns an error and resets the state" 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("DATA")
|
||||||
client.handle("a" * 1024 * 1024 * 10)
|
client.handle("a" * 1024 * 1024 * 10)
|
||||||
expect(client.handle(".")).to eq "552 Message too large (maximum size 1MB)"
|
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
|
context "when a loop is detected" do
|
||||||
it "returns an error and resets the state" do
|
it "returns an error and resets the state" do
|
||||||
client.handle("DATA")
|
client.handle("DATA")
|
||||||
client.handle("Received: from example1.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.dns.smtp_server_hostname}")
|
client.handle("Received: from example2.com by #{Postal::Config.postal.smtp_hostname}")
|
||||||
client.handle("Received: from example1.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.dns.smtp_server_hostname}")
|
client.handle("Received: from example2.com by #{Postal::Config.postal.smtp_hostname}")
|
||||||
client.handle("Subject: Test")
|
client.handle("Subject: Test")
|
||||||
client.handle("From: #{mail_from}")
|
client.handle("From: #{mail_from}")
|
||||||
client.handle("To: #{rcpt_to}")
|
client.handle("To: #{rcpt_to}")
|
||||||
@@ -93,7 +93,7 @@ module SMTPServer
|
|||||||
|
|
||||||
context "when sending a bounce message" do
|
context "when sending a bounce message" do
|
||||||
let(:credential) { nil }
|
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
|
context "when there is a return path route" do
|
||||||
let(:domain) { create(:domain, owner: server) }
|
let(:domain) { create(:domain, owner: server) }
|
||||||
@@ -114,7 +114,7 @@ module SMTPServer
|
|||||||
|
|
||||||
queued_message = QueuedMessage.first
|
queued_message = QueuedMessage.first
|
||||||
expect(queued_message).to have_attributes(
|
expect(queued_message).to have_attributes(
|
||||||
domain: Postal.config.dns.return_path,
|
domain: Postal::Config.dns.return_path_domain,
|
||||||
server: server
|
server: server
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -145,7 +145,7 @@ module SMTPServer
|
|||||||
|
|
||||||
queued_message = QueuedMessage.first
|
queued_message = QueuedMessage.first
|
||||||
expect(queued_message).to have_attributes(
|
expect(queued_message).to have_attributes(
|
||||||
domain: Postal.config.dns.return_path,
|
domain: Postal::Config.dns.return_path_domain,
|
||||||
server: server
|
server: server
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ module SMTPServer
|
|||||||
describe "HELO" do
|
describe "HELO" do
|
||||||
it "returns the hostname" do
|
it "returns the hostname" do
|
||||||
expect(client.state).to eq :welcome
|
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
|
expect(client.state).to eq :welcomed
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -24,7 +24,7 @@ module SMTPServer
|
|||||||
|
|
||||||
context "when TLS is enabled" do
|
context "when TLS is enabled" do
|
||||||
it "returns capabilities include starttls" 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",
|
expect(client.handle("EHLO test.example.com")).to eq ["250-My capabilities are",
|
||||||
"250-STARTTLS",
|
"250-STARTTLS",
|
||||||
"250 AUTH CRAM-MD5 PLAIN LOGIN"]
|
"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
|
context "when the RCPT TO address is the system return path host" do
|
||||||
it "returns an error if the server does not exist" 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
|
end
|
||||||
|
|
||||||
it "returns an error if the server is suspended" do
|
it "returns an error if the server is suspended" do
|
||||||
server = create(:server, :suspended)
|
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"
|
.to eq "535 Mail server has been suspended"
|
||||||
end
|
end
|
||||||
|
|
||||||
it "adds a recipient if all OK" do
|
it "adds a recipient if all OK" do
|
||||||
server = create(:server)
|
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.handle("RCPT TO: #{address}")).to eq "250 OK"
|
||||||
expect(client.recipients).to eq [[:bounce, address, server]]
|
expect(client.recipients).to eq [[:bounce, address, server]]
|
||||||
expect(client.state).to eq :rcpt_to_received
|
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
|
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
|
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"
|
expect(client.handle("RCPT TO: #{address}")).to eq "550 Invalid server token"
|
||||||
end
|
end
|
||||||
|
|
||||||
it "returns an error if the server is suspended" do
|
it "returns an error if the server is suspended" do
|
||||||
server = create(:server, :suspended)
|
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"
|
expect(client.handle("RCPT TO: #{address}")).to eq "535 Mail server has been suspended"
|
||||||
end
|
end
|
||||||
|
|
||||||
it "adds a recipient if all OK" do
|
it "adds a recipient if all OK" do
|
||||||
server = create(:server)
|
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.handle("RCPT TO: #{address}")).to eq "250 OK"
|
||||||
expect(client.recipients).to eq [[:bounce, address, server]]
|
expect(client.recipients).to eq [[:bounce, address, server]]
|
||||||
expect(client.state).to eq :rcpt_to_received
|
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
|
context "when the RCPT TO address is within the route domain" do
|
||||||
it "returns an error if the route token is invalid" 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"
|
expect(client.handle("RCPT TO: #{address}")).to eq "550 Invalid route token"
|
||||||
end
|
end
|
||||||
|
|
||||||
it "returns an error if the server is suspended" do
|
it "returns an error if the server is suspended" do
|
||||||
server = create(:server, :suspended)
|
server = create(:server, :suspended)
|
||||||
route = create(:route, server: server)
|
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"
|
expect(client.handle("RCPT TO: #{address}")).to eq "535 Mail server has been suspended"
|
||||||
end
|
end
|
||||||
|
|
||||||
it "returns an error if the route is set to Reject mail" do
|
it "returns an error if the route is set to Reject mail" do
|
||||||
server = create(:server)
|
server = create(:server)
|
||||||
route = create(:route, server: server, mode: "Reject")
|
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"
|
expect(client.handle("RCPT TO: #{address}")).to eq "550 Route does not accept incoming messages"
|
||||||
end
|
end
|
||||||
|
|
||||||
it "adds a recipient if all OK" do
|
it "adds a recipient if all OK" do
|
||||||
server = create(:server)
|
server = create(:server)
|
||||||
route = create(:route, server: 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.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.recipients).to eq [[:route, "#{route.name}+tag1@#{route.domain.name}", server, { route: route }]]
|
||||||
expect(client.state).to eq :rcpt_to_received
|
expect(client.state).to eq :rcpt_to_received
|
||||||
|
|||||||
@@ -216,7 +216,7 @@ describe Domain do
|
|||||||
|
|
||||||
describe "#spf_record" do
|
describe "#spf_record" do
|
||||||
it "returns the 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
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -251,7 +251,7 @@ describe Domain do
|
|||||||
end
|
end
|
||||||
|
|
||||||
it "returns the DKIM identifier" do
|
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
|
end
|
||||||
end
|
end
|
||||||
@@ -269,14 +269,14 @@ describe Domain do
|
|||||||
end
|
end
|
||||||
|
|
||||||
it "returns the DKIM identifier" do
|
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
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "#return_path_domain" do
|
describe "#return_path_domain" do
|
||||||
it "returns the 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
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -284,14 +284,14 @@ describe Domain do
|
|||||||
let(:domain) { create(:domain, verification_method: "DNS") }
|
let(:domain) { create(:domain, verification_method: "DNS") }
|
||||||
|
|
||||||
it "returns the DNS verification string" do
|
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
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "#resolver" do
|
describe "#resolver" do
|
||||||
context "when the local nameservers should be used" do
|
context "when the local nameservers should be used" do
|
||||||
before 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
|
end
|
||||||
|
|
||||||
it "uses the local DNS" do
|
it "uses the local DNS" do
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
# frozen_string_literal: true
|
# 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 File.expand_path("../config/environment", __dir__)
|
||||||
require "rspec/rails"
|
require "rspec/rails"
|
||||||
|
|||||||
Reference in New Issue
Block a user