diff --git a/Containerfile b/Containerfile index b5cda83..1315ee2 100644 --- a/Containerfile +++ b/Containerfile @@ -1,4 +1,5 @@ -FROM golang:1.19-alpine AS build-env +### Builds maddy-hostux-check-password +FROM docker.io/golang:1.19-alpine AS build-env # Required for mattn/go-sqlite3 ENV CGO_ENABLED=1 @@ -16,6 +17,11 @@ COPY . ./ RUN make -FROM foxcpp/maddy:0.6 +### Copies maddy-hostux-check-password and maddy.conf to the final image +FROM docker.io/foxcpp/maddy:0.7.1 -COPY --from=build-env /maddy-hostux-check-password/build/maddy-hostux-check-password /bin/maddy-hostux-check-password \ No newline at end of file +COPY --from=build-env /maddy-hostux-check-password/build/maddy-hostux-check-password /bin/maddy-hostux-check-password + +COPY ./maddy.conf.docker /etc/maddy/maddy.conf + +ENTRYPOINT ["/bin/maddy", "-config", "/etc/maddy/maddy.conf"] \ No newline at end of file diff --git a/README.md b/README.md index 13de5d8..cbe8793 100644 --- a/README.md +++ b/README.md @@ -7,6 +7,7 @@ Features: Configuration through environment variables: * `HOSTUX_EMAIL_DATABASE_SQLITE`: required, path to the database containing the email addresses * `HOSTUX_EMAIL_CHECKPW_LOGFILE`: if set, logs are written to the path indicated +* `HOSTUX_LOCAL_DOMAINS`: to complete `$(local_domains)` list (necessary for outbound DKIM signing, and w/ current `maddy.conf.docker`, for destination and source matching) ## Testing @@ -14,7 +15,7 @@ Configuration through environment variables: Build the image: ```bash -docker build -f Containerfile -t hostux/maddy:latest . +podman build -t hostux/maddy:latest . ``` For `maddy.conf`: @@ -93,20 +94,19 @@ imap tcp://0.0.0.0:143 { ``` ```bash -docker volume create maddydata -docker network create maddy-test +podman volume create maddydata -docker run --rm \ +podman run --rm \ --name maddy \ -e MADDY_HOSTNAME=mx.maddy.test \ -e MADDY_DOMAIN=maddy.test \ - -e HOSTUX_EMAIL_DATABASE_SQLITE=/email.sqlite \ + -e HOSTUX_EMAIL_DATABASE_SQLITE=/data/email-db/email.sqlite \ -v maddydata:/data \ - -v ~/maddy-hostux-check-password/email.sqlite:/email.sqlite \ + -v email-db:/data/email-db/ \ -p 143:143 \ hostux/maddy:latest -docker run --rm -it -v maddydata:/data --entrypoint ash foxcpp/maddy:0.6 +podman run --rm -it -v maddydata:/data --entrypoint ash foxcpp/maddy:0.6 ``` ### CLI Imap Client @@ -135,4 +135,4 @@ format_list = format_thread = {uid} {subject} <<< FROM {from} format_status = {directory:>20} : {count:>5} Mails - {unseen:>5} Unseen - {recent:>5} Recent limit = 10 -``` \ No newline at end of file +``` diff --git a/maddy.conf.docker b/maddy.conf.docker new file mode 100644 index 0000000..97c1228 --- /dev/null +++ b/maddy.conf.docker @@ -0,0 +1,203 @@ +### Hostux +### 2022-06-18 Original default configuration file with changes necessary to run it in Docker +### 2024-03-16 Adapted configuration file from https://raw.githubusercontent.com/foxcpp/maddy/master/maddy.conf.docker + + +# Suitable for small-scale deployments. Uses its own format for local users DB, +# should be managed via maddy subcommands. +# +# See tutorials at https://maddy.email for guidance on typical +# configuration changes. + +# ---------------------------------------------------------------------------- +# Base variables + +$(hostname) = {env:MADDY_HOSTNAME} +$(primary_domain) = {env:MADDY_DOMAIN} +$(local_domains) = $(primary_domain) {env:HOSTUX_DOMAINS_WITH_DKIM} + +tls file /certs/live/$(hostname)/fullchain.pem /certs/live/$(hostname)/privkey.pem + +# ---------------------------------------------------------------------------- +# Local storage & authentication + +# pass_table provides local hashed passwords storage for authentication of +# users. It can be configured to use any "table" module, in default +# configuration a table in SQLite DB is used. +# Table can be replaced to use e.g. a file for passwords. Or pass_table module +# can be replaced altogether to use some external source of credentials (e.g. +# PAM, /etc/shadow file). +# +# If table module supports it (sql_table does) - credentials can be managed +# using 'maddy creds' command. + +auth.external hostux_auth { + helper /bin/maddy-hostux-check-password + perdomain yes + domains $(local_domains) +} + +# imapsql module stores all indexes and metadata necessary for IMAP using a +# relational database. It is used by IMAP endpoint for mailbox access and +# also by SMTP & Submission endpoints for delivery of local messages. +# +# IMAP accounts, mailboxes and all message metadata can be inspected using +# imap-* subcommands of maddy. + +storage.imapsql local_mailboxes { + driver sqlite3 + dsn imapsql.db +} + +# ---------------------------------------------------------------------------- +# SMTP endpoints + message routing + +hostname $(hostname) + +table.chain local_rewrites { + optional_step regexp "(.+)\+(.+)@(.+)" "$1@$3" + optional_step static { + entry postmaster postmaster@$(primary_domain) + } + optional_step sql_query { + driver sqlite3 + dsn {env:HOSTUX_EMAIL_DATABASE_SQLITE} + lookup "SELECT aliasTo_emailAddress FROM email-aliases WHERE emailAlias = $1" + } +} + +#table.sql_query hostux_destinations { +# driver sqlite3 +# dsn {env:HOSTUX_EMAIL_DATABASE_SQLITE} +# lookup "SELECT emailAddress AS address FROM `email-addresses` +# WHERE address = $1 +# UNION +# SELECT emailAlias AS address FROM `email-aliases` +# WHERE address = $1" +#} + +msgpipeline local_routing { + # Insert handling for special-purpose local domains here. + # e.g. + # destination lists.example.org { + # deliver_to lmtp tcp://127.0.0.1:8024 + # } + + check { + rspamd { + api_path {env:RSPAMD_SERVER} + } + } + + destination postmaster $(local_domains) { + modify { + replace_rcpt &local_rewrites + } + + deliver_to &local_mailboxes + } + + default_destination { + reject 550 5.1.1 "User doesn't exist" + } +} + +smtp tcp://0.0.0.0:25 { + limits { + # Up to 20 msgs/sec across max. 10 SMTP connections. + all rate 20 1s + all concurrency 10 + } + + dmarc yes + check { + require_mx_record + dkim + spf + } + + source $(local_domains) { + reject 501 5.1.8 "Use Submission for outgoing SMTP" + } + default_source { + destination postmaster $(local_domains) { + deliver_to &local_routing + } + default_destination { + reject 550 5.1.1 "User doesn't exist" + } + } +} + +submission tls://0.0.0.0:465 tcp://0.0.0.0:587 { + limits { + # Up to 50 msgs/sec across any amount of SMTP connections. + all rate 50 1s + } + + auth &hostux_auth + + source $(local_domains) { + check { + authorize_sender { + prepare_email &local_rewrites + user_to_email identity + } + } + + destination postmaster $(local_domains) { + deliver_to &local_routing + } + default_destination { + modify { + dkim $(primary_domain) $(local_domains) default + } + deliver_to &remote_queue + } + } + default_source { + reject 501 5.1.8 "Non-local sender domain" + } +} + +target.remote outbound_delivery { + limits { + # Up to 20 msgs/sec across max. 10 SMTP connections + # for each recipient domain. + destination rate 20 1s + destination concurrency 10 + } + mx_auth { + dane + mtasts { + cache fs + fs_dir mtasts_cache/ + } + local_policy { + min_tls_level encrypted + min_mx_level none + } + } +} + +target.queue remote_queue { + target &outbound_delivery + + autogenerated_msg_domain $(primary_domain) + bounce { + destination postmaster $(local_domains) { + deliver_to &local_routing + } + default_destination { + reject 550 5.0.0 "Refusing to send DSNs to non-local addresses" + } + } +} + +# ---------------------------------------------------------------------------- +# IMAP endpoints + +imap tls://0.0.0.0:993 tcp://0.0.0.0:143 { + auth &hostux_auth + storage &local_mailboxes +}