From bae25e4edce890d731319f5c0e0896db7bfde8ee Mon Sep 17 00:00:00 2001 From: Dominic Ricottone Date: Wed, 21 Jun 2023 13:48:55 -0500 Subject: [PATCH] Postfix redesign Now there are three images tags for `postfix`. `:latest` uses encryption and authentication for inbound and outbound mail. It listens on posts 25 and 465. Because it authenticates, I dropped the requirement for senders to have a LAN IP. `:tls-in` drops outbound encryption and authentication. This is generally going to be useful for receiving mail and handing it to a local service. `:tls-out` drops inbound encryption and authentication and listining on port 465. This is useful for relaying mail off of a trusted host. --- README.md | 2 +- postfix/Dockerfile | 7 +++-- postfix/Dockerfile.tls-in | 24 +++++++++++++++ postfix/Dockerfile.tls-out | 22 ++++++++++++++ postfix/Makefile | 22 ++++++++++++-- postfix/README.md | 25 +++++++++++++-- postfix/entrypoint.sh | 3 ++ postfix/main.cf | 11 ++++++- postfix/main.cf.tls-in | 62 ++++++++++++++++++++++++++++++++++++++ postfix/main.cf.tls-out | 58 +++++++++++++++++++++++++++++++++++ postfix/smtpd.conf | 4 +++ 11 files changed, 229 insertions(+), 11 deletions(-) create mode 100644 postfix/Dockerfile.tls-in create mode 100644 postfix/Dockerfile.tls-out create mode 100644 postfix/main.cf.tls-in create mode 100644 postfix/main.cf.tls-out create mode 100644 postfix/smtpd.conf diff --git a/README.md b/README.md index 16c903a..2deed61 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ It should be easy to get things working on another build system. |[nginx](/~dricottone/container-images/tree/dev/item/nginx/README.md)|latest|`registry.intra.dominic-ricottone.com/nginx:latest`| |[nitter](/~dricottone/container-images/tree/dev/item/nitter/README.md)|amd64,arm64|`registry.intra.dominic-ricottone.com/nitter:amd64`| |[php](/~dricottone/container-images/tree/dev/item/php/README.md)|latest,readwrite,fpm,development,fpm-development|`registry.intra.dominic-ricottone.com/php:latest`| -|[postfix](/~dricottone/container-images/tree/dev/item/postfix/README.md)|latest|`registry.intra.dominic-ricottone.com/postfix:latest`| +|[postfix](/~dricottone/container-images/tree/dev/item/postfix/README.md)|latest,tls-in,tls-out|`registry.intra.dominic-ricottone.com/postfix:latest`| |[srht-core](/~dricottone/container-images/tree/dev/item/srht-core/README.md)|latest|`registry.intra.dominic-ricottone.com/srht-core:latest`| |[srht-git-api](/~dricottone/container-images/tree/dev/item/srht-git-api/README.md)|latest|`registry.intra.dominic-ricottone.com/srht-git-api:latest`| |[srht-git-core](/~dricottone/container-images/tree/dev/item/srht-git-core/README.md)|latest|`registry.intra.dominic-ricottone.com/srht-git-core:latest`| diff --git a/postfix/Dockerfile b/postfix/Dockerfile index c485d17..26ab8d8 100644 --- a/postfix/Dockerfile +++ b/postfix/Dockerfile @@ -1,21 +1,22 @@ FROM docker.io/library/alpine:latest COPY aliases /etc/postfix/aliases -COPY *.cf /etc/postfix/ +COPY master.cf /etc/postfix/master.cf +COPY main.cf /etc/postfix/main.cf COPY entrypoint.sh /entrypoint.sh RUN chmod +x /entrypoint.sh RUN mkdir /etc/postfix/sasl && chmod 700 /etc/postfix/sasl +COPY smtpd.conf /etc/postfix/sasl/smtpd.conf RUN addgroup -S mailer RUN adduser -SD -s /bin/sh -G mailer -g mailer mailer -RUN apk add --no-cache postfix +RUN apk add --no-cache postfix cyrus-sasl EXPOSE 25 EXPOSE 465 -EXPOSE 587 ENTRYPOINT ["/entrypoint.sh"] diff --git a/postfix/Dockerfile.tls-in b/postfix/Dockerfile.tls-in new file mode 100644 index 0000000..e6f1c47 --- /dev/null +++ b/postfix/Dockerfile.tls-in @@ -0,0 +1,24 @@ +FROM docker.io/library/alpine:latest + +COPY aliases /etc/postfix/aliases +COPY master.cf /etc/postfix/master.cf +COPY main.cf.tls-in /etc/postfix/main.cf + +COPY entrypoint.sh /entrypoint.sh +RUN chmod +x /entrypoint.sh + +RUN mkdir /etc/postfix/sasl && chmod 700 /etc/postfix/sasl +COPY smtpd.conf /etc/postfix/sasl/smtpd.conf + +RUN addgroup -S mailer +RUN adduser -SD -s /bin/sh -G mailer -g mailer mailer + +RUN apk add --no-cache postfix cyrus-sasl + +EXPOSE 25 +EXPOSE 465 + +ENTRYPOINT ["/entrypoint.sh"] + +CMD ["postfix", "start-fg"] + diff --git a/postfix/Dockerfile.tls-out b/postfix/Dockerfile.tls-out new file mode 100644 index 0000000..1ab65e3 --- /dev/null +++ b/postfix/Dockerfile.tls-out @@ -0,0 +1,22 @@ +FROM docker.io/library/alpine:latest + +COPY aliases /etc/postfix/aliases +COPY master.cf /etc/postfix/master.cf +COPY main.cf.tls-out /etc/postfix/main.cf + +COPY entrypoint.sh /entrypoint.sh +RUN chmod +x /entrypoint.sh + +RUN mkdir /etc/postfix/sasl && chmod 700 /etc/postfix/sasl + +RUN addgroup -S mailer +RUN adduser -SD -s /bin/sh -G mailer -g mailer mailer + +RUN apk add --no-cache postfix + +EXPOSE 25 + +ENTRYPOINT ["/entrypoint.sh"] + +CMD ["postfix", "start-fg"] + diff --git a/postfix/Makefile b/postfix/Makefile index 7cdefc8..b19382c 100644 --- a/postfix/Makefile +++ b/postfix/Makefile @@ -2,12 +2,28 @@ CONMAN=sudo docker REGISTRY=registry.intra.dominic-ricottone.com IMAGE=postfix -TAG=latest +TAG_TLS_ALL=latest +TAG_TLS_IN=tls-in +TAG_TLS_OUT=tls-out -image: +image: image-tls-all image-tls-in image-tls-out + +image-tls-all: $(CONMAN) buildx build --push \ --platform linux/arm64,linux/amd64 \ - --tag $(REGISTRY)/$(IMAGE):$(TAG) \ + --tag $(REGISTRY)/$(IMAGE):$(TAG_TLS_ALL) \ . +image-tls-in: + $(CONMAN) buildx build --push \ + --platform linux/arm64,linux/amd64 \ + --tag $(REGISTRY)/$(IMAGE):$(TAG_TLS_IN) \ + . -f Dockerfile.tls-in + +image-tls-out: + $(CONMAN) buildx build --push \ + --platform linux/arm64,linux/amd64 \ + --tag $(REGISTRY)/$(IMAGE):$(TAG_TLS_OUT) \ + . -f Dockerfile.tls-out + .PHONY: image diff --git a/postfix/README.md b/postfix/README.md index f1b9721..6bbca96 100644 --- a/postfix/README.md +++ b/postfix/README.md @@ -11,6 +11,8 @@ make image ### Tags + `latest` + + `tls-in` (listens on the SMTPS port, but sends without encryption or authentication) + + `tls-out` (sends with encryption and authentication, but only listens on SMTP port) ---- @@ -41,7 +43,7 @@ example.com local * relay:[smtp.gmail.com]:587 ``` -Create an authentication file in `$saslfile`. +Create an outbound authentication file in `$saslfile`. This is required for relaying mail to major email providers, including GMail. It must also be owned (on the host system) by the user that will create the container (i.e. `root` for conventional `docker(1)` deployments). @@ -51,7 +53,17 @@ It should look like: [smtp.gmail.com]:587 example@gmail.com:wwwwxxxxyyyyzzzz ``` -Create a configuration file in `$conffile`. +Create an inbound authentication file in `$sasldb`. +It must also be owned (on the host system) by the user that will create the +container (i.e. `root` for conventional `docker(1)` deployments). +It should be created like: + +``` +docker run --rm --interactive --tty \ + --mount type=bind,src=$(pwd)/sasldb2,dst=/etc/sasldb2 \ + registry.intra.dominic-ricottone.com/postfix:latest \ + /usr/sbin/saslpasswd2 -c -f /etc/sasldb2 -u example.com username +``` Try: @@ -60,7 +72,14 @@ $conman run --detach --name postfix --restart always \ --mount type=bind,src=$genericfile,dst=/etc/postfix/generic,readonly \ --mount type=bind,src=$transportfile,dst=/etc/postfix/transport,readonly \ --mount type=bind,src=$saslfile,dst=/etc/postfix/sasl/sasl_passwd,readonly \ - --mount type=bind,src=$conffile,dst=/etc/postfix/main.cf,readonly \ + --mount type=bind,src=$sasldb,dst=/etc/sasldb2,readonly \ + --env DOMAIN=example.com --env DESTINATION="mail.example.com" \ + --publish 0.0.0.0:25:25 --publish 0.0.0.0:465:465 \ registry.intra.dominic-ricottone.com/postfix:latest ``` +If using the `tls-out` image, skip `$sasldb`. +Similarly, if using the `tls-in` image, skip `$saslfile`. + +If using the `tls-in` image, do not publish port 465. + diff --git a/postfix/entrypoint.sh b/postfix/entrypoint.sh index 85af13d..2e7b7b4 100644 --- a/postfix/entrypoint.sh +++ b/postfix/entrypoint.sh @@ -4,5 +4,8 @@ postmap /etc/postfix/transport postmap /etc/postfix/generic postmap /etc/postfix/sasl/sasl_passwd +sed -i /etc/postfix/main.cf -e "s/^mydomain.*/mydomain = ${DOMAIN}/" +sed -i /etc/postfix/main.cf -e "s/^mydestination.*/mydestination = \$myhostname, ${DESTINATION}, localhost, localhost.localdomain/" + exec "$@" diff --git a/postfix/main.cf b/postfix/main.cf index 9c6a86b..f5f0c05 100644 --- a/postfix/main.cf +++ b/postfix/main.cf @@ -5,7 +5,7 @@ compatibility_level = 3.6 # Allowed interfaces and addresses inet_protocols = ipv4 -mynetworks = 127.0.0.0/8, 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16 +inet_interfaces = all # Values for default settings mydomain = dominic-ricottone.com @@ -23,13 +23,22 @@ smtp_generic_maps = lmdb:/etc/postfix/generic transport_maps = lmdb:/etc/postfix/transport # Authentication +cyrus_sasl_config_path = /etc/postfix/sasl/ smtp_sasl_auth_enable = yes smtp_sasl_password_maps = lmdb:/etc/postfix/sasl/sasl_passwd smtp_sasl_security_options = noanonymous +smtpd_sasl_auth_enable = yes +smtpd_sasl_security_options = noanonymous, noplaintext +smtpd_sasl_tls_security_options = noanonymous +smtpd_tls_auth_only = yes # Encryption smtp_tls_CAfile = /etc/ssl/certs/ca-certificates.crt smtp_tls_security_level = encrypt +smtpd_tls_chain_files = /var/letsencrypt/chain.pem +smtpd_tls_mandatory_ciphers = high +smtpd_tls_mandatory_protocols = >=TLSv1.2 +smtpd_tls_security_level = may # If set, mail destined for any member of `$mydestination` would be rejected if user lookup failed local_recipient_maps = diff --git a/postfix/main.cf.tls-in b/postfix/main.cf.tls-in new file mode 100644 index 0000000..805bbcd --- /dev/null +++ b/postfix/main.cf.tls-in @@ -0,0 +1,62 @@ +# postfix main configuration file +# see `postconf(5)` or https://www.postfix.org/BASIC_CONFIGURATION_README.html + +compatibility_level = 3.6 + +# Allowed interfaces and addresses +inet_protocols = ipv4 +inet_interfaces = all + +# Values for default settings +mydomain = dominic-ricottone.com +myhostname = fedora3.$mydomain +mydestination = $myhostname, todo.$mydomain, lists.$mydomain, localhost, localhost.localdomain +myorigin = $mydomain + +# Advertise host name after SMTP 200 +smtpd_banner = $myhostname ESMTP $mail_name ($mail_version) + +# Address rewriting +smtp_generic_maps = lmdb:/etc/postfix/generic + +# Transport map +transport_maps = lmdb:/etc/postfix/transport + +# Authentication +cyrus_sasl_config_path = /etc/postfix/sasl/ +smtpd_sasl_auth_enable = yes +smtpd_sasl_security_options = noanonymous, noplaintext +smtpd_sasl_tls_security_options = noanonymous +smtpd_tls_auth_only = yes + +# Encryption +smtpd_tls_chain_files = /var/letsencrypt/chain.pem +smtpd_tls_mandatory_ciphers = high +smtpd_tls_mandatory_protocols = >=TLSv1.2 +smtpd_tls_security_level = may + +# If set, mail destined for any member of `$mydestination` would be rejected if user lookup failed +local_recipient_maps = + +# Local management +mail_owner = postfix +setgid_group = postdrop + +home_mailbox = Maildir/ + +sendmail_path = /usr/sbin/sendmail +newaliases_path = /usr/bin/newaliases +mailq_path = /usr/bin/mailq + +command_directory = /usr/sbin +daemon_directory = /usr/libexec/postfix +data_directory = /var/lib/postfix +html_directory = no +manpage_directory = /usr/share/man +mail_spool_directory = /var/spool/mail +meta_directory = /etc/postfix +queue_directory = /var/spool/postfix +readme_directory = /usr/share/doc/postfix/readme +sample_directory = /etc/postfix +shlib_directory = /usr/lib/postfix + diff --git a/postfix/main.cf.tls-out b/postfix/main.cf.tls-out new file mode 100644 index 0000000..9c6a86b --- /dev/null +++ b/postfix/main.cf.tls-out @@ -0,0 +1,58 @@ +# postfix main configuration file +# see `postconf(5)` or https://www.postfix.org/BASIC_CONFIGURATION_README.html + +compatibility_level = 3.6 + +# Allowed interfaces and addresses +inet_protocols = ipv4 +mynetworks = 127.0.0.0/8, 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16 + +# Values for default settings +mydomain = dominic-ricottone.com +myhostname = fedora3.$mydomain +mydestination = $myhostname, todo.$mydomain, lists.$mydomain, localhost, localhost.localdomain +myorigin = $mydomain + +# Advertise host name after SMTP 200 +smtpd_banner = $myhostname ESMTP $mail_name ($mail_version) + +# Address rewriting +smtp_generic_maps = lmdb:/etc/postfix/generic + +# Transport map +transport_maps = lmdb:/etc/postfix/transport + +# Authentication +smtp_sasl_auth_enable = yes +smtp_sasl_password_maps = lmdb:/etc/postfix/sasl/sasl_passwd +smtp_sasl_security_options = noanonymous + +# Encryption +smtp_tls_CAfile = /etc/ssl/certs/ca-certificates.crt +smtp_tls_security_level = encrypt + +# If set, mail destined for any member of `$mydestination` would be rejected if user lookup failed +local_recipient_maps = + +# Local management +mail_owner = postfix +setgid_group = postdrop + +home_mailbox = Maildir/ + +sendmail_path = /usr/sbin/sendmail +newaliases_path = /usr/bin/newaliases +mailq_path = /usr/bin/mailq + +command_directory = /usr/sbin +daemon_directory = /usr/libexec/postfix +data_directory = /var/lib/postfix +html_directory = no +manpage_directory = /usr/share/man +mail_spool_directory = /var/spool/mail +meta_directory = /etc/postfix +queue_directory = /var/spool/postfix +readme_directory = /usr/share/doc/postfix/readme +sample_directory = /etc/postfix +shlib_directory = /usr/lib/postfix + diff --git a/postfix/smtpd.conf b/postfix/smtpd.conf new file mode 100644 index 0000000..9910ca3 --- /dev/null +++ b/postfix/smtpd.conf @@ -0,0 +1,4 @@ +pwcheck_method: auxprop +auxprop_plugin: sasldb +mech_list: PLAIN LOGIN CRAM-MD5 DIGEST-MD5 NTLM + -- 2.45.2