مراية لـ
https://github.com/postalserver/postal.git
تم المزامنة 2026-03-03 14:24:06 +00:00
Compare commits
13 الالتزامات
| المؤلف | SHA1 | التاريخ | |
|---|---|---|---|
|
|
4c27baee7f | ||
|
|
9399e32234 | ||
|
|
22dcd4901f | ||
|
|
6df963651d | ||
|
|
4acfffd1d8 | ||
|
|
e2d642c0cb | ||
|
|
4e1deb2d2a | ||
|
|
d1e5b68200 | ||
|
|
33513a77c0 | ||
|
|
3785c99851 | ||
|
|
9bf6152060 | ||
|
|
0dc7359431 | ||
|
|
2c20ba65f6 |
@@ -1,3 +1,3 @@
|
||||
{
|
||||
".": "3.0.2"
|
||||
".": "3.1.1"
|
||||
}
|
||||
|
||||
34
CHANGELOG.md
34
CHANGELOG.md
@@ -2,6 +2,40 @@
|
||||
|
||||
This file contains all the latest changes and updates to Postal.
|
||||
|
||||
## [3.1.1](https://github.com/postalserver/postal/compare/3.1.0...3.1.1) (2024-03-08)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* don't override paths in dockerfile ([9399e32](https://github.com/postalserver/postal/commit/9399e3223467cdacd010e70b58ad6093e128213d))
|
||||
|
||||
|
||||
### Tests
|
||||
|
||||
* **smtp-sender:** add more tests for AUTH LOGIN ([22dcd49](https://github.com/postalserver/postal/commit/22dcd4901f188915cf4b3c758c6f2fc637a4e1e3))
|
||||
|
||||
## [3.1.0](https://github.com/postalserver/postal/compare/3.0.2...3.1.0) (2024-03-06)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* configurable trusted proxies for web requests ([3785c99](https://github.com/postalserver/postal/commit/3785c998513c634d225b489ccb43e926ce3f270a))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **message-dequeuer:** ensure SMTP endpoints are sent to SMTP sender appropriately ([e2d642c](https://github.com/postalserver/postal/commit/e2d642c0cbf443550886d90abc3a6edf3e4bc4fc)), closes [#2853](https://github.com/postalserver/postal/issues/2853)
|
||||
* **smtp-server:** listen on all interfaces by default ([d1e5b68](https://github.com/postalserver/postal/commit/d1e5b68200ea4b9710cc8714afb3271bad1f4f66)), closes [#2852](https://github.com/postalserver/postal/issues/2852)
|
||||
* **smtp-server:** remove ::ffff: from the start of ipv4 addresses ([0dc7359](https://github.com/postalserver/postal/commit/0dc7359431001c9ef1222913f8d1344093397596))
|
||||
* **smtp-server:** reset ansi sequence after logging ([9bf6152](https://github.com/postalserver/postal/commit/9bf6152060ffb8b611b66818c1d1ac7c929b7ffe))
|
||||
* **ui:** fixes typo on queue page ([33513a7](https://github.com/postalserver/postal/commit/33513a77c0df24d832ab7ed5237d68e2b1bde887))
|
||||
* **web-server:** allow for trusted proxies not be set ([4e1deb2](https://github.com/postalserver/postal/commit/4e1deb2d2aeb61d9dddb3729916411c94e73c1c6))
|
||||
|
||||
|
||||
### Styles
|
||||
|
||||
* **rubocop:** use _ when not using a variable in helm config exporter ([2c20ba6](https://github.com/postalserver/postal/commit/2c20ba65f64ccb0f8174e3f523dedb3806478782))
|
||||
|
||||
## [3.0.2](https://github.com/postalserver/postal/compare/3.0.1...3.0.2) (2024-03-05)
|
||||
|
||||
|
||||
|
||||
@@ -48,9 +48,6 @@ RUN echo $VERSION > VERSION
|
||||
|
||||
# Set paths for when running in a container
|
||||
ENV POSTAL_CONFIG_FILE_PATH=/config/postal.yml
|
||||
ENV POSTAL_SIGNING_KEY_PATH=/config/signing.key
|
||||
ENV SMTP_SERVER_TLS_CERTIFICATE_PATH=/config/smtp.cert
|
||||
ENV SMTP_SERVER_TLS_PRIVATE_KEY_PATH=/config/smtp.key
|
||||
|
||||
# Set the CMD
|
||||
ENTRYPOINT [ "/docker-entrypoint.sh" ]
|
||||
|
||||
2
Gemfile
2
Gemfile
@@ -16,7 +16,7 @@ gem "hashie"
|
||||
gem "highline", require: false
|
||||
gem "kaminari"
|
||||
gem "klogger-logger"
|
||||
gem "konfig-config", "~> 2.0"
|
||||
gem "konfig-config", "~> 3.0"
|
||||
gem "mail"
|
||||
gem "moonrope"
|
||||
gem "mysql2"
|
||||
|
||||
@@ -149,7 +149,7 @@ GEM
|
||||
concurrent-ruby (>= 1.0, < 2.0)
|
||||
json
|
||||
rouge (>= 3.30, < 5.0)
|
||||
konfig-config (2.1.1)
|
||||
konfig-config (3.0.0)
|
||||
hashie
|
||||
loofah (2.22.0)
|
||||
crass (~> 1.0.2)
|
||||
@@ -353,7 +353,7 @@ DEPENDENCIES
|
||||
jquery-rails
|
||||
kaminari
|
||||
klogger-logger
|
||||
konfig-config (~> 2.0)
|
||||
konfig-config (~> 3.0)
|
||||
mail
|
||||
moonrope
|
||||
mysql2
|
||||
|
||||
@@ -163,7 +163,7 @@ module MessageDequeuer
|
||||
|
||||
case queued_message.message.endpoint
|
||||
when SMTPEndpoint
|
||||
sender = @state.sender_for(SMTPSender, queued_message.message.recipient_domain, nil, servers: [queued_message.message.endpoint])
|
||||
sender = @state.sender_for(SMTPSender, queued_message.message.recipient_domain, nil, servers: [queued_message.message.endpoint.to_smtp_client_server])
|
||||
when HTTPEndpoint
|
||||
sender = @state.sender_for(HTTPSender, queued_message.message.endpoint)
|
||||
when AddressEndpoint
|
||||
|
||||
@@ -112,21 +112,23 @@ module SMTPServer
|
||||
# Accept the connection
|
||||
new_io = io.accept
|
||||
increment_prometheus_counter :postal_smtp_server_connections_total
|
||||
# Get the client's IP address and strip `::ffff:` for consistency.
|
||||
client_ip_address = new_io.remote_address.ip_address.sub(/\A::ffff:/, "")
|
||||
if Postal::Config.smtp_server.proxy_protocol?
|
||||
# If we are using the haproxy proxy protocol, we will be sent the
|
||||
# client's IP later. Delay the welcome process.
|
||||
client = Client.new(nil)
|
||||
if Postal::Config.smtp_server.log_connections?
|
||||
client.logger&.debug "Connection opened from #{new_io.remote_address.ip_address}"
|
||||
client.logger&.debug "Connection opened from #{client_ip_address}"
|
||||
end
|
||||
else
|
||||
# We're not using the proxy protocol so we already know the client's IP
|
||||
client = Client.new(new_io.remote_address.ip_address)
|
||||
client = Client.new(client_ip_address)
|
||||
if Postal::Config.smtp_server.log_connections?
|
||||
client.logger&.debug "Connection opened from #{new_io.remote_address.ip_address}"
|
||||
client.logger&.debug "Connection opened from #{client_ip_address}"
|
||||
end
|
||||
# We know who the client is, welcome them.
|
||||
client.logger&.debug "Client identified as #{new_io.remote_address.ip_address}"
|
||||
client.logger&.debug "Client identified as #{client_ip_address}"
|
||||
new_io.print("220 #{Postal::Config.postal.smtp_hostname} ESMTP Postal/#{client.trace_id}")
|
||||
end
|
||||
# Register the client and its socket with nio4r
|
||||
@@ -205,7 +207,7 @@ module SMTPServer
|
||||
|
||||
result = [result] unless result.is_a?(Array)
|
||||
result.compact.each do |iline|
|
||||
client.logger&.debug "\e[34m=> #{iline.strip}"
|
||||
client.logger&.debug "\e[34m=> #{iline.strip}\e[0m"
|
||||
begin
|
||||
io.write(iline.to_s + "\r\n")
|
||||
io.flush
|
||||
|
||||
@@ -47,4 +47,8 @@ class SMTPEndpoint < ApplicationRecord
|
||||
routes.each { |r| r.update(endpoint: nil, mode: "Reject") }
|
||||
end
|
||||
|
||||
def to_smtp_client_server
|
||||
SMTPClient::Server.new(hostname, port: port || 25, ssl_mode: ssl_mode)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
- if @messages.empty?
|
||||
.pageContent--compact
|
||||
.noData.noData--clean
|
||||
%h2.noData__title Your queue is current empty.
|
||||
%h2.noData__title Your queue is currently empty.
|
||||
%p.noData__text
|
||||
Messages which haven't yet been delivered successfully will appear in your queue until
|
||||
we've delivered them or we've given up trying.
|
||||
|
||||
@@ -1,15 +1,10 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Rack
|
||||
class Request
|
||||
|
||||
module Helpers
|
||||
|
||||
def trusted_proxy?(ip)
|
||||
ip =~ /^127\.0\.0\.1$|^localhost$|^unix$$/i
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
Rack::Request.ip_filter = lambda { |ip|
|
||||
if Postal::Config.postal.trusted_proxies&.any? { |net| net.include?(ip) } ||
|
||||
ip.match(/\A127\.0\.0\.1\Z|\A::1\Z|\Afd[0-9a-f]{2}:.+|\Alocalhost\Z|\Aunix\Z|\Aunix:/i)
|
||||
true
|
||||
else
|
||||
false
|
||||
end
|
||||
end
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ This document contains all the environment variables which are available for thi
|
||||
| `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 | |
|
||||
| `POSTAL_TRUSTED_PROXIES` | Array of strings | An array of IP addresses to trust for proxying requests to Postal (in addition to localhost addresses) | |
|
||||
| `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 |
|
||||
|
||||
@@ -27,6 +27,8 @@ postal:
|
||||
signing_key_path: config/postal/signing.key
|
||||
# An array of SMTP relays in the format of smtp://host:port
|
||||
smtp_relays: []
|
||||
# An array of IP addresses to trust for proxying requests to Postal (in addition to localhost addresses)
|
||||
trusted_proxies: []
|
||||
|
||||
web_server:
|
||||
# The default port the web server should listen on unless overriden by the PORT environment variable
|
||||
|
||||
@@ -68,7 +68,8 @@ module Postal
|
||||
|
||||
string :signing_key_path do
|
||||
description "Path to the private key used for signing"
|
||||
default "config/postal/signing.key"
|
||||
default "$config-file-root/signing.key"
|
||||
transform { |v| Postal.substitute_config_file_root(v) }
|
||||
end
|
||||
|
||||
string :smtp_relays do
|
||||
@@ -84,6 +85,12 @@ module Postal
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
string :trusted_proxies do
|
||||
array
|
||||
description "An array of IP addresses to trust for proxying requests to Postal (in addition to localhost addresses)"
|
||||
transform { |ip| IPAddr.new(ip) }
|
||||
end
|
||||
end
|
||||
|
||||
group :web_server do
|
||||
@@ -227,7 +234,7 @@ module Postal
|
||||
|
||||
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"
|
||||
default "::"
|
||||
end
|
||||
|
||||
integer :default_health_server_port do
|
||||
@@ -247,12 +254,14 @@ module Postal
|
||||
|
||||
string :tls_certificate_path do
|
||||
description "The path to the SMTP server's TLS certificate"
|
||||
default "config/postal/smtp.cert"
|
||||
default "$config-file-root/smtp.cert"
|
||||
transform { |v| Postal.substitute_config_file_root(v) }
|
||||
end
|
||||
|
||||
string :tls_private_key_path do
|
||||
description "The path to the SMTP server's TLS private key"
|
||||
default "config/postal/smtp.key"
|
||||
default "$config-file-root/smtp.key"
|
||||
transform { |v| Postal.substitute_config_file_root(v) }
|
||||
end
|
||||
|
||||
string :tls_ciphers do
|
||||
@@ -496,4 +505,14 @@ module Postal
|
||||
end
|
||||
end
|
||||
|
||||
class << self
|
||||
|
||||
def substitute_config_file_root(string)
|
||||
return if string.nil?
|
||||
|
||||
string.gsub(/\$config-file-root/i, File.dirname(Postal.config_file_path))
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -12,7 +12,7 @@ module Postal
|
||||
|
||||
@schema.groups.each do |group_name, group|
|
||||
path << group_name
|
||||
group.attributes.each do |name, attr|
|
||||
group.attributes.each do |name, _|
|
||||
env_var = Konfig::Sources::Environment.path_to_env_var(path + [name])
|
||||
contents << <<~VAR.strip
|
||||
{{ include "app.envVar" (dict "name" "#{env_var}" "spec" .Values.postal.#{path.join('.')}.#{name} "root" . ) }}
|
||||
|
||||
@@ -422,7 +422,7 @@ module MessageDequeuer
|
||||
it "gets a sender from the state and sends the message to it" do
|
||||
smtp_sender_double = double("SMTPSender")
|
||||
expect(smtp_sender_double).to receive(:send_message).with(queued_message.message).and_return(SendResult.new)
|
||||
expect(state).to receive(:sender_for).with(SMTPSender, message.recipient_domain, nil, { servers: [endpoint] }).and_return(smtp_sender_double)
|
||||
expect(state).to receive(:sender_for).with(SMTPSender, message.recipient_domain, nil, { servers: [kind_of(SMTPClient::Server)] }).and_return(smtp_sender_double)
|
||||
processor.process
|
||||
end
|
||||
end
|
||||
|
||||
@@ -17,7 +17,9 @@ module Postal
|
||||
# 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)
|
||||
allow_any_instance_of(Konfig::SchemaAttribute).to receive(:default) do |a|
|
||||
a.array? ? [] : nil
|
||||
end
|
||||
end
|
||||
|
||||
let(:source) { described_class.new(SOURCE_CONFIG) }
|
||||
|
||||
@@ -55,6 +55,26 @@ module SMTPServer
|
||||
it "requests the username" do
|
||||
expect(client.handle("AUTH LOGIN")).to eq("334 VXNlcm5hbWU6")
|
||||
end
|
||||
|
||||
it "requests a password after a username" do
|
||||
client.handle("AUTH LOGIN")
|
||||
expect(client.handle("xx")).to eq("334 UGFzc3dvcmQ6")
|
||||
end
|
||||
|
||||
it "authenticates and returns a response if the password is correct" do
|
||||
client.handle("AUTH LOGIN")
|
||||
client.handle("xx")
|
||||
credential = create(:credential, type: "SMTP")
|
||||
password = Base64.encode64(credential.key)
|
||||
expect(client.handle(password)).to match(/235 Granted for/)
|
||||
end
|
||||
|
||||
it "returns an error when an invalid credential is provided" do
|
||||
client.handle("AUTH LOGIN")
|
||||
client.handle("xx")
|
||||
password = Base64.encode64("xx")
|
||||
expect(client.handle(password)).to eq("535 Invalid credential")
|
||||
end
|
||||
end
|
||||
|
||||
context "when a username is provided on the first line" do
|
||||
@@ -71,9 +91,7 @@ module SMTPServer
|
||||
expect(client.handle(password)).to match(/235 Granted for/)
|
||||
expect(client.credential).to eq credential
|
||||
end
|
||||
end
|
||||
|
||||
context "when invalid credentials are provided" do
|
||||
it "returns an error and resets the state" do
|
||||
username = Base64.encode64("xx")
|
||||
password = Base64.encode64("xx")
|
||||
|
||||
المرجع في مشكلة جديدة
حظر مستخدم