Comments (10)
Being able to impersonate any user on the server doesn't seem like a good/secure default to me at all, especially considering that this is an all-in-one solution.
from mail-server.
This seems to work for now:
require ["vnd.stalwart.plugins", "variables", "envelope", "reject", "environment"];
query :use "postgresql" :set ["address_owner"] "SELECT name FROM addresses WHERE address = $1" ["${envelope.from}"];
if not environment :is "authenticated_as" "${address_owner}" {
reject "Unauthorized sender";
stop;
}
I don't like the SQL query duplication from directory.postgresql.query.recipients
though, no idea how to avoid it, sieve isn't intuitive at all.
It would really make much more sense to have this built-in, at the very minimum I'd expect to be informed that anyone can send from any address in the docs, because this is NOT obvious nor expected to anyone who isn't an email nerd and can be dangerous
from mail-server.
Being able to impersonate any user on the server doesn't seem like a good/secure default to me at all
This is how email has worked for the last 40 years. I also use this daily.
Should we also go back to http, get rid of SPF, DKIM and everything else, because that's how it worked 40 years ago? This really doesn't make sense to me, there's a clear security issue here.
from mail-server.
Yes, this is an awful default. Even if it's not malicious, it can just be a deliverability issue.
Allowing forging a sender should require explicit configuration.
from mail-server.
Please attach your configuration file (removing any sensitive info).
from mail-server.
It's mostly just whatever was the default
#############################################
# Server configuration
#############################################
[server]
hostname = "uwu.kkx.one"
max-connections = 8192
#[server.run-as]
#user = "stalwart-mail"
#group = "stalwart-mail"
[server.tls]
enable = true
implicit = false
timeout = "1m"
certificate = "default"
#sni = [{subject = "", certificate = ""}]
#protocols = ["TLSv1.2", TLSv1.3"]
#ciphers = []
ignore-client-order = true
[server.socket]
nodelay = true
reuse-addr = true
#reuse-port = true
backlog = 1024
#ttl = 3600
#send-buffer-size = 65535
#recv-buffer-size = 65535
#linger = 1
#tos = 1
[global]
shared-map = {shard = 32, capacity = 10}
#thread-pool = 8
[global.tracing]
method = "stdout"
level = "trace"
#[global.tracing]
#method = "open-telemetry"
#transport = "http"
#endpoint = "https://127.0.0.1/otel"
#headers = ["Authorization: <place_auth_here>"]
#level = "debug"
#[global.tracing]
#method = "log"
#path = "/opt/stalwart-mail/logs"
#prefix = "stalwart.log"
#rotate = "daily"
#level = "info"
[certificate."default"]
cert = "file:///certs/pepega.club.crt"
private-key = "file:///certs/pepega.club.key"
#############################################
# Directory configuration
#############################################
[directory."postgres"]
type = "sql"
address = "postgresql://stalwart:[email protected]:5432/stalwart?sslmode=disable"
[directory."postgres".options]
catch-all = true
#catch-all = { map = "(.+)@(.+)$", to = "info@${2}" }
subaddressing = true
#subaddressing = { map = "^([^.]+)\.([^.]+)@(.+)$", to = "${2}@${3}" }
superuser-group = "superusers"
[directory."postgres".pool]
max-connections = 10
min-connections = 0
#idle-timeout = "10m"
[directory."postgres".cache]
entries = 500
ttl = {positive = '1h', negative = '10m'}
[directory."postgres".query]
name = "SELECT name, type, secret, description, quota FROM accounts WHERE name = $1 AND active = true"
members = "SELECT member_of FROM group_members WHERE name = $1"
recipients = "SELECT name FROM addresses WHERE address = $1"
emails = "SELECT address FROM addresses WHERE name = $1 AND type != 'list' ORDER BY type DESC, address ASC"
verify = "SELECT address FROM addresses WHERE address LIKE '%' || $1 || '%' AND type = 'primary' ORDER BY address LIMIT 5"
expand = "SELECT p.address FROM addresses AS p JOIN addresses AS l ON p.name = l.name WHERE p.type = 'primary' AND l.address = $1 AND l.type = 'list' ORDER BY p.address LIMIT 50"
domains = "SELECT 1 FROM addresses WHERE address LIKE '%@' || $1 LIMIT 1"
[directory."postgres".columns]
name = "name"
description = "description"
secret = "secret"
email = "address"
quota = "quota"
type = "type"
#############################################
# JMAP server configuration
#############################################
[server.listener."jmap"]
bind = ["[::]:8080"]
url = "https://uwu.kkx.one:8080"
protocol = "jmap"
[store.db]
path = "/opt/stalwart-mail/data/index.sqlite3"
[store.db.pool]
max-connections = 10
#workers = 8
[store.db.cache]
size = 1000
[store.blob]
type = "local"
[store.blob.local]
path = "/opt/stalwart-mail/data/blobs"
[jmap]
directory = "postgres"
[jmap.http]
#headers = ["Access-Control-Allow-Origin: *",
# "Access-Control-Allow-Methods: POST, GET, HEAD, OPTIONS",
# "Access-Control-Allow-Headers: *"]
[jmap.encryption]
enable = true
append = false
[jmap.session.cache]
ttl = "1h"
size = 100
[jmap.protocol.get]
max-objects = 500
[jmap.protocol.set]
max-objects = 500
[jmap.protocol.request]
max-concurrent = 4
max-size = 10000000
max-calls = 16
[jmap.protocol.query]
max-results = 5000
[jmap.protocol.upload]
max-size = 50000000
max-concurrent = 4
ttl = "1h"
[jmap.protocol.upload.quota]
files = 1000
size = 50000000
[jmap.protocol.changes]
max-results = 5000
[jmap.rate-limit]
account = "1000/1m"
authentication = "10/1m"
anonymous = "100/1m"
use-forwarded = false
[jmap.rate-limit.cache]
size = 1024
[jmap.mailbox]
max-depth = 10
max-name-length = 255
[jmap.email]
max-attachment-size = 50000000
max-size = 75000000
[jmap.email.parse]
max-items = 10
[jmap.principal]
allow-lookups = true
[jmap.sieve]
disable-capabilities = []
notification-uris = ["mailto"]
protected-headers = ["Original-Subject", "Original-From", "Received", "Auto-Submitted"]
[jmap.sieve.limits]
name-length = 512
max-scripts = 256
script-size = 102400
string-length = 4096
variable-name-length = 32
variable-size = 4096
nested-blocks = 15
nested-tests = 15
nested-foreverypart = 3
match-variables = 30
local-variables = 128
header-size = 1024
includes = 3
nested-includes = 3
cpu = 5000
redirects = 1
received-headers = 10
outgoing-messages = 3
[jmap.sieve.vacation]
default-subject = "Automated reply"
subject-prefix = "Auto: "
[jmap.sieve.default-expiry]
vacation = "30d"
duplicate = "7d"
[jmap.event-source]
throttle = "1s"
[jmap.web-sockets]
throttle = "1s"
timeout = "10m"
heartbeat = "1m"
[jmap.push]
max-total = 100
throttle = "1ms"
[jmap.push.attempts]
interval = "1m"
max = 3
[jmap.push.retry]
interval = "1s"
[jmap.push.timeout]
request = "10s"
verify = "1s"
[jmap.fts]
default-language = "en"
[oauth]
key = "key"
[oauth.auth]
max-attempts = 3
[oauth.expiry]
user-code = "30m"
auth-code = "10m"
token = "1h"
refresh-token = "30d"
refresh-token-renew = "4d"
[oauth.cache]
size = 128
[jmap.purge.schedule]
db = "0 3 *"
blobs = "30 3 *"
sessions = "15 * *"
#############################################
# IMAP server configuration
#############################################
[server.listener."imap"]
bind = ["[::]:143"]
protocol = "imap"
[server.listener."imaptls"]
bind = ["[::]:993"]
protocol = "imap"
tls.implicit = true
[server.listener."sieve"]
bind = ["[::]:4190"]
protocol = "managesieve"
tls.implicit = true
[imap.request]
max-size = 52428800
[imap.auth]
max-failures = 3
allow-plain-text = false
[imap.folders.name]
shared = "Shared Folders"
all = "All Mail"
[imap.timeout]
authenticated = "30m"
anonymous = "1m"
idle = "30m"
[imap.rate-limit]
requests = "2000/1m"
concurrent = 4
#############################################
# SMTP server configuration
#############################################
[server.listener."smtp"]
bind = ["[::]:25"]
greeting = "Stalwart SMTP at your service"
protocol = "smtp"
[server.listener."submission"]
bind = ["[::]:587"]
protocol = "smtp"
[server.listener."submissions"]
bind = ["[::]:465"]
protocol = "smtp"
tls.implicit = true
[server.listener."management"]
bind = ["0.0.0.0:8080"]
protocol = "http"
[session]
timeout = "5m"
transfer-limit = 262144000 # 250 MB
duration = "10m"
[session.connect]
#script = "connect.sieve"
[session.ehlo]
require = true
reject-non-fqdn = [ { if = "listener", eq = "smtp", then = true},
{ else = false } ]
#script = "ehlo"
[session.extensions]
pipelining = true
chunking = true
requiretls = true
no-soliciting = ""
dsn = [ { if = "authenticated-as", ne = "", then = true},
{ else = false } ]
expn = [ { if = "authenticated-as", ne = "", then = true},
{ else = false } ]
vrfy = [ { if = "authenticated-as", ne = "", then = true},
{ else = false } ]
future-release = [ { if = "authenticated-as", ne = "", then = "7d"},
{ else = false } ]
deliver-by = [ { if = "authenticated-as", ne = "", then = "15d"},
{ else = false } ]
mt-priority = [ { if = "authenticated-as", ne = "", then = "mixer"},
{ else = false } ]
[session.auth]
mechanisms = [ { if = "listener", ne = "smtp", then = ["plain", "login"]},
{ else = [] } ]
directory = [ { if = "listener", ne = "smtp", then = "postgres" },
{ else = false } ]
require = [ { if = "listener", ne = "smtp", then = true},
{ else = false } ]
[session.auth.errors]
total = 3
wait = "5s"
[session.mail]
#script = "mail-from"
#rewrite = [ { all-of = [ { if = "listener", ne = "smtp" },
# { if = "rcpt", matches = "^([^.]+)@([^.]+)\.(.+)$"},
# ], then = "${1}@${3}" },
# { else = false } ]
[session.rcpt]
#script = "rcpt-to"
relay = [ { if = "authenticated-as", ne = "", then = true },
{ else = false } ]
#rewrite = [ { all-of = [ { if = "rcpt-domain", in-list = "sql/domains" },
# { if = "rcpt", matches = "^([^.]+)\.([^.]+)@(.+)$"},
# ], then = "${1}+${2}@${3}" },
# { else = false } ]
max-recipients = 25
directory = "postgres"
[session.rcpt.errors]
total = 5
wait = "5s"
[session.data]
#script = "data"
#[session.data.milter."rspamd"]
#enable = [ { if = "listener", eq = "smtp", then = true },
# { else = false } ]
#hostname = "127.0.0.1"
#port = 11332
#tls = false
#allow-invalid-certs = false
#[session.data.milter."rspamd".timeout]
#connect = "30s"
#command = "30s"
#data = "60s"
#[session.data.milter."rspamd".options]
#tempfail-on-error = true
#max-response-size = 52428800 # 50mb
#version = 6
#[session.data.pipe."spam-assassin"]
#command = "spamc"
#arguments = []
#timeout = "10s"
[session.data.limits]
messages = 10
size = 104857600
received-headers = 50
[session.data.add-headers]
received = [ { if = "listener", eq = "smtp", then = true },
{ else = false } ]
received-spf = [ { if = "listener", eq = "smtp", then = true },
{ else = false } ]
auth-results = [ { if = "listener", eq = "smtp", then = true },
{ else = false } ]
message-id = [ { if = "listener", eq = "smtp", then = false },
{ else = true } ]
date = [ { if = "listener", eq = "smtp", then = false },
{ else = true } ]
return-path = false
[[session.throttle]]
#match = {if = "remote-ip", eq = "10.0.0.1"}
key = ["remote-ip"]
concurrency = 5
#rate = "5/1h"
[[session.throttle]]
key = ["sender-domain", "rcpt"]
rate = "25/1h"
[auth.dnsbl]
verify = [ { if = "listener", eq = "smtp", then = ["ip", "iprev", "ehlo", "return-path", "from"] },
{ else = [] } ]
[auth.dnsbl.lookup]
ip = ["zen.spamhaus.org", "bl.spamcop.net", "b.barracudacentral.org"]
domain = ["dbl.spamhaus.org"]
[auth.iprev]
verify = [ { if = "listener", eq = "smtp", then = "relaxed" },
{ else = "disable" } ]
[auth.dkim]
verify = "relaxed"
sign = [ { if = "listener", ne = "smtp", then = ["rsa"] },
{ else = [] } ]
[auth.spf.verify]
ehlo = [ { if = "listener", eq = "smtp", then = "relaxed" },
{ else = "disable" } ]
mail-from = [ { if = "listener", eq = "smtp", then = "relaxed" },
{ else = "disable" } ]
[auth.arc]
verify = "relaxed"
seal = ["rsa"]
[auth.dmarc]
verify = [ { if = "listener", eq = "smtp", then = "relaxed" },
{ else = "disable" } ]
[queue]
path = "/opt/stalwart-mail/queue"
hash = 64
[queue.schedule]
retry = ["2m", "5m", "10m", "15m", "30m", "1h", "2h"]
notify = ["1d", "3d"]
expire = "5d"
[queue.outbound]
#hostname = "uwu.kkx.one"
next-hop = [ { if = "rcpt-domain", in-list = "postgres/domains", then = "local" },
{ else = false } ]
ip-strategy = "ipv4-then-ipv6"
[queue.outbound.tls]
dane = "optional"
mta-sts = "optional"
starttls = "require"
#[queue.outbound.source-ip]
#v4 = ["10.0.0.10", "10.0.0.11"]
#v6 = ["a::b", "a::c"]
[queue.outbound.limits]
mx = 7
multihomed = 2
[queue.outbound.timeouts]
connect = "3m"
greeting = "3m"
tls = "2m"
ehlo = "3m"
mail-from = "3m"
rcpt-to = "3m"
data = "10m"
mta-sts = "2m"
[[queue.quota]]
#match = {if = "sender-domain", eq = "foobar.org"}
#key = ["rcpt"]
messages = 100000
size = 10737418240 # 10gb
[[queue.throttle]]
key = ["rcpt-domain"]
#rate = "100/1h"
concurrency = 5
[resolver]
type = "system"
#preserve-intermediates = true
concurrency = 2
timeout = "5s"
attempts = 2
try-tcp-on-error = true
[resolver.cache]
txt = 2048
mx = 1024
ipv4 = 1024
ipv6 = 1024
ptr = 1024
tlsa = 1024
mta-sts = 1024
[report]
path = "/opt/stalwart-mail/reports"
hash = 64
#submitter = "uwu.kkx.one"
[report.analysis]
addresses = ["dmarc@*", "abuse@*", "postmaster@*"]
forward = true
#store = "/opt/stalwart-mail/incoming"
[report.dsn]
from-name = "Mail Delivery Subsystem"
from-address = "[email protected]"
sign = ["rsa"]
[report.dkim]
from-name = "Report Subsystem"
from-address = "[email protected]"
subject = "DKIM Authentication Failure Report"
sign = ["rsa"]
send = "1/1d"
[report.spf]
from-name = "Report Subsystem"
from-address = "[email protected]"
subject = "SPF Authentication Failure Report"
send = "1/1d"
sign = ["rsa"]
[report.dmarc]
from-name = "Report Subsystem"
from-address = "[email protected]"
subject = "DMARC Authentication Failure Report"
send = "1/1d"
sign = ["rsa"]
[report.dmarc.aggregate]
from-name = "DMARC Report"
from-address = "[email protected]"
org-name = "kkx.one"
#contact-info = ""
send = "daily"
max-size = 26214400 # 25mb
sign = ["rsa"]
[report.tls.aggregate]
from-name = "TLS Report"
from-address = "[email protected]"
org-name = "kkx.one"
#contact-info = ""
send = "daily"
max-size = 26214400 # 25 mb
sign = ["rsa"]
[signature."rsa"]
#public-key = "file:///opt/stalwart-mail/etc/dkim/kkx.one.cert"
private-key = "file:///opt/stalwart-mail/etc/dkim/kkx.one.key"
domain = "kkx.one"
selector = "stalwart"
headers = ["From", "To", "Date", "Subject", "Message-ID"]
algorithm = "rsa-sha256"
canonicalization = "relaxed/relaxed"
#expire = "10d"
#third-party = ""
#third-party-algo = ""
#auid = ""
set-body-length = false
report = true
[remote."lmtp"]
address = "127.0.0.1"
port = 11200
protocol = "lmtp"
concurrency = 10
timeout = "1m"
[remote."lmtp".tls]
implicit = false
allow-invalid-certs = true
#[remote."lmtp".auth]
#username = ""
#secret = ""
[sieve]
from-name = "Automated Message"
from-addr = "[email protected]"
return-path = ""
#hostname = "uwu.kkx.one"
sign = ["rsa"]
use-directory = "postgres"
[sieve.limits]
redirects = 3
out-messages = 5
received-headers = 50
cpu = 10000
nested-includes = 5
duplicate-expiry = "7d"
[sieve.scripts]
[management]
directory = "postgres"
from mail-server.
This is not a bug.
SMTP servers in general do not validate whether a MAIL FROM
address exists or not. However,
- For messages received from the internet, you can perform IPREV, DNSBL, SPF and DMARC checks to make sure the remote domain is not a spammer.
- For authenticated senders, you can create a Sieve scripts that verifies that the
envelope.from
variable equals to theenv.authenticated_as
variable. Alternatively you could do a directory lookup from the Sieve script to make sure thatenvelope.from
is from a local domain and is a valid account.
from mail-server.
Being able to impersonate any user on the server doesn't seem like a good/secure default to me at all
This is how email has worked for the last 40 years. I also use this daily.
from mail-server.
How do you propose to check that the address specified in the MAIL FROM command is able to be used by the (presumably authenticated) person sending the mail? What happens if the user isn't authenticated?
from mail-server.
How do you propose to check that the address specified in the MAIL FROM command is able to be used by the (presumably authenticated) person sending the mail? What happens if the user isn't authenticated?
Just like relay
config option works? If authenticated as some user only then check if that user has this address assigned?
EDIT: Something like this
[session.mail]
directory = "postgresql" # already needs to be configured for session.rcpt
validate_from = [ { if = "authenticated-as", ne = "", then = true }, { else = false } ]
from mail-server.
Related Issues (20)
- [enhancement]: make reject events INFO level logging HOT 1
- [bug]: failure to parse in 0.3.9 HOT 1
- [enhancement]: OCSP Stapling support
- [enhancement]: Add encrypted message Subject support
- [enhancement]: Consider optimising systemd service file security HOT 3
- [enhancement]: Consider shipping an AppArmor profile
- [bug]: Failed to parse public suffixes from any source. HOT 1
- [enhancement]: implement a sendmail cli compatibility binary and a virtual map or map like postfix
- [enhancement]: Enforce encryption on all mails in the mailstore - retrospectively
- [bug]: 3.10 Does not have x86_64-unknown-linux-gnu support HOT 4
- [bug]: IMAP hangs for ever after AUTHENTICATE PLAIN HOT 4
- [enhancement]: Have different logging channels
- [bug]: configure.sh script exits before completing HOT 3
- [bug]: Installation of standalone JMAP configures SMTP HOT 4
- [enhancement]: propose a setting to rename folders HOT 2
- [bug]: better handle docker dns PTRs HOT 2
- [enhancement]: Allow infinite number of DKIM keys HOT 3
- [bug]: Purging bitmaps fails for S3 storage HOT 3
- [enhancement]: OAuth dynamic client registration
- [bug]: Install script fails on slow internet connection
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from mail-server.