diff --git a/Containerfile b/Containerfile index 8debbfd..2db4075 100644 --- a/Containerfile +++ b/Containerfile @@ -1,93 +1,84 @@ -FROM docker.io/alpine:3.19 as builder - -RUN apk --no-cache add \ - rpcgen \ - g++ \ - make \ - openssl \ - openssl-dev \ - lua5.1-dev \ - libsodium-dev \ - linux-pam-dev \ - zlib-dev \ - bzip2-dev \ - xz-dev \ - lz4-dev \ - icu-dev \ - inotify-tools-dev - -ENV SHA256_SUM_DOVECOT=05b11093a71c237c2ef309ad587510721cc93bbee6828251549fc1586c36502d -ENV DOVECOT_FILENAME=dovecot-2.3.21.tar.gz -RUN wget https://www.dovecot.org/releases/2.3/$DOVECOT_FILENAME -RUN echo "$SHA256_SUM_DOVECOT $DOVECOT_FILENAME" | sha256sum -c - || exit 1 -RUN mkdir /tmp/dovecot -RUN tar xzf $DOVECOT_FILENAME -C /tmp/dovecot --strip 1 - -ENV SHA256_SUM_PIGEONHOLE=1ca71d2659076712058a72030288f150b2b076b0306453471c5261498d3ded27 -ENV PIGEONHOLE_FILENAME=dovecot-2.3-pigeonhole-0.5.21.tar.gz -RUN wget https://pigeonhole.dovecot.org/releases/2.3/$PIGEONHOLE_FILENAME -RUN echo "$SHA256_SUM_PIGEONHOLE $PIGEONHOLE_FILENAME" | sha256sum -c - || exit 1 -RUN mkdir /tmp/pigeonhole -RUN tar xzf $PIGEONHOLE_FILENAME -C /tmp/pigeonhole --strip 1 - -RUN cd /tmp/dovecot && \ - ./configure --prefix '' \ - --with-notify=inotify \ - --with-lua \ - --with-zlib \ - --with-bzlib \ - --with-pam \ - --with-ssl=openssl \ - --with-sodium \ - --without-sql \ - --with-lzma \ - --with-lz4 \ - --with-icu \ - --without-shadow \ - --with-ssldir=/etc/ssl/mail \ - --with-rundir=/run/dovecot \ - --disable-static && \ - make && \ - make install - -RUN cd /tmp/pigeonhole && \ - ./configure --prefix '' \ - --with-dovecot=/lib/dovecot \ - --disable-static && \ - make && make install +# FROM docker.io/alpine:3.19 as builder +# +# RUN apk --no-cache add \ +# rpcgen \ +# g++ \ +# make \ +# openssl \ +# openssl-dev \ +# lua5.1-dev \ +# libsodium-dev \ +# linux-pam-dev \ +# zlib-dev \ +# bzip2-dev \ +# xz-dev \ +# lz4-dev \ +# icu-dev \ +# inotify-tools-dev +# +# ENV SHA256_SUM_DOVECOT=05b11093a71c237c2ef309ad587510721cc93bbee6828251549fc1586c36502d +# ENV DOVECOT_FILENAME=dovecot-2.3.21.tar.gz +# RUN wget https://www.dovecot.org/releases/2.3/$DOVECOT_FILENAME +# RUN echo "$SHA256_SUM_DOVECOT $DOVECOT_FILENAME" | sha256sum -c - || exit 1 +# RUN mkdir /tmp/dovecot +# RUN tar xzf $DOVECOT_FILENAME -C /tmp/dovecot --strip 1 +# +# ENV SHA256_SUM_PIGEONHOLE=1ca71d2659076712058a72030288f150b2b076b0306453471c5261498d3ded27 +# ENV PIGEONHOLE_FILENAME=dovecot-2.3-pigeonhole-0.5.21.tar.gz +# RUN wget https://pigeonhole.dovecot.org/releases/2.3/$PIGEONHOLE_FILENAME +# RUN echo "$SHA256_SUM_PIGEONHOLE $PIGEONHOLE_FILENAME" | sha256sum -c - || exit 1 +# RUN mkdir /tmp/pigeonhole +# RUN tar xzf $PIGEONHOLE_FILENAME -C /tmp/pigeonhole --strip 1 +# +# RUN cd /tmp/dovecot && \ +# ./configure --prefix '' \ +# --with-notify=inotify \ +# --with-lua \ +# --with-zlib \ +# --with-bzlib \ +# --with-pam \ +# --with-ssl=openssl \ +# --with-sodium \ +# --without-sql \ +# --with-lzma \ +# --with-lz4 \ +# --with-icu \ +# --without-shadow \ +# --with-ssldir=/etc/ssl/mail \ +# --with-rundir=/run/dovecot \ +# --disable-static && \ +# make && \ +# make install +# +# RUN cd /tmp/pigeonhole && \ +# ./configure --prefix '' \ +# --with-dovecot=/lib/dovecot \ +# --disable-static && \ +# make && make install FROM docker.io/thallian/confd-env:3.19-3.1.6.2 -COPY --from=builder /lib/dovecot/ /lib/dovecot/ -COPY --from=builder /libexec/dovecot/ /libexec/dovecot/ -COPY --from=builder /bin/doveadm /bin/doveadm -COPY --from=builder /bin/doveconf /bin/doveconf -COPY --from=builder /bin/dsync /bin/dsync -COPY --from=builder /sbin/dovecot /sbin/dovecot -COPY --from=builder /bin/sieve* /bin/ +# COPY --from=builder /lib/dovecot/ /lib/dovecot/ +# COPY --from=builder /libexec/dovecot/ /libexec/dovecot/ +# COPY --from=builder /bin/doveadm /bin/doveadm +# COPY --from=builder /bin/doveconf /bin/doveconf +# COPY --from=builder /bin/dsync /bin/dsync +# COPY --from=builder /sbin/dovecot /sbin/dovecot +# COPY --from=builder /bin/sieve* /bin/ RUN apk --no-cache add \ - libsodium \ - libbz2 \ - zlib \ - xz-libs \ - lz4-libs \ - lz4 \ - linux-pam \ - openssl \ ssmtp \ - ca-certificates \ - lua5.1-libs \ - lua5.1-rapidjson \ - curl \ - inotify-tools \ - libssl3 + dovecot \ + dovecot-pgsql \ + dovecot-lmtpd \ + dovecot-pigeonhole-plugin -RUN addgroup -g 150 dovecot -RUN adduser -u 140 -h /dev/null -H -s /sbin/nologin -D -G dovecot dovecot -RUN addgroup -g 151 dovenull -RUN adduser -u 141 -h /dev/null -H -s /sbin/nologin -D -G dovenull dovenull +# RUN addgroup -g 150 dovecot +# RUN adduser -u 140 -h /dev/null -H -s /sbin/nologin -D -G dovecot dovecot +# +# RUN addgroup -g 151 dovenull +# RUN adduser -u 141 -h /dev/null -H -s /sbin/nologin -D -G dovenull dovenull RUN addgroup -g 2222 access RUN addgroup dovecot access diff --git a/README.md b/README.md index 4f4855e..2b4c0a4 100644 --- a/README.md +++ b/README.md @@ -1,75 +1,101 @@ -[Dovecot](http://www.dovecot.org/) with imap, starttls, oauth2 proxy auth and sieve rules. +[Dovecot](http://www.dovecot.org/) with imap, starttls, oauth2 proxy auth and +sieve rules. -Uses [SSMTP](https://packages.debian.org/stable/mail/ssmtp) to send mails (for example if you have a redirect sieve rule). +Uses [SSMTP](https://packages.debian.org/stable/mail/ssmtp) to send mails (for +example if you have a redirect sieve rule). +Reuses the same database schema as the {postfix container](/container/postfix). # Volumes + - `/var/lib/vmail/mail` # Environment Variables + ## HOSTNAME + Fully qualified name of the mail host. ## GRANT_URL + OAuth2 url for token grants (password grant type). ## INTROSPECTION_URL -OAuth2 url for token information. -## USER_URL -OAuth2 url for getting available users, the username will be appended to the end. +OAuth2 url for token information. Must include client id and client secret in +basic auth format. -## TOKENINFO_URL -OAuth2 url for requestion information about a token. +## TOKENINFO_URL -## CLIENT_ID -Id of the OAuth2 application. +OAuth2 url for requestion information about a token. Must include client id and +client secret in basic auth format. -## CLIENT_SECRET -Secret of the OAuth2 application. +## DB_HOST -## OAUTH_ADMIN_USER -User with which to perform user lookups (does not have to be an admin, but needs enough rights for that). +Postgre database host. -## OAUTH_ADMIN_PASSWORD -Password for the `OAUTH_ADMIN_USER`. +## DB_USER + +User to connect to the database. + +## DB_PW + +Password to use for the database user. + +## DB_NAME + +- default: email + +Name of the postgre database to connect to. ## SSMTP_MAIL_RELAY + Hostname and port for the used smtp relay (for example `mail.example.com:587`). ## SSMTP_USER + User to authenticate agains the smtp relay. ## SSMTP_PASSWORD + Password to authenticate agains the smtp relay. ## SSMTP_AUTH_METHOD + - default: LOGIN Which authentication mechanism to use for the smtp relay. ## SSMTP_USE_STARTTLS + - default: yes Whether to use starttls for the smtp relay. ## ALLOWED_USERNAME_CHARS -- default: äöüabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890.-_@ + +- default: + äöüabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890.-_@ List of characters allowed in a username. ## AUTH_MECHANISMS + - default: plain -Space seperated list of supported [authentication mechanisms](http://wiki2.dovecot.org/Authentication/Mechanisms). +Space seperated list of supported +[authentication mechanisms](http://wiki2.dovecot.org/Authentication/Mechanisms). ## SSL_MIN_PROTOCOL + - default: TLSv1.2 Ssl minimum protocol version. ## SSL_CIPHERLIST -- default: ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256 + +- default: + ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256 Colon seperated list of supported ciphers (`!`disables a cipher). @@ -77,14 +103,17 @@ Go [here](https://www.openssl.org/docs/manmaster/man1/ciphers.html) for a list of ciphers. ## IMAP_MAX_USER_CONNECTIONS + - default: 10 Maximum number of connections from the same user + ip. # Ports + - 143 # Capabilities + - CHOWN - DAC_OVERRIDE - FOWNER diff --git a/rootfs/etc/confd/conf.d/dovecot-sql.userdb.ext.toml b/rootfs/etc/confd/conf.d/dovecot-sql.userdb.ext.toml new file mode 100644 index 0000000..ba1646d --- /dev/null +++ b/rootfs/etc/confd/conf.d/dovecot-sql.userdb.ext.toml @@ -0,0 +1,3 @@ +[template] +src = "dovecot-sql.userdb.conf.ext.tmpl" +dest = "/etc/dovecot/dovecot-sql.userdb.conf.ext" diff --git a/rootfs/etc/confd/conf.d/oauth2-userdb.lua.toml b/rootfs/etc/confd/conf.d/oauth2-userdb.lua.toml deleted file mode 100644 index cfb53ac..0000000 --- a/rootfs/etc/confd/conf.d/oauth2-userdb.lua.toml +++ /dev/null @@ -1,3 +0,0 @@ -[template] -src = "oauth2-userdb.lua.tmpl" -dest = "/etc/dovecot/oauth2-userdb.lua" diff --git a/rootfs/etc/confd/templates/10-mail.conf.tmpl b/rootfs/etc/confd/templates/10-mail.conf.tmpl index 3c064c3..f15ee9c 100644 --- a/rootfs/etc/confd/templates/10-mail.conf.tmpl +++ b/rootfs/etc/confd/templates/10-mail.conf.tmpl @@ -23,4 +23,4 @@ namespace inbox { } } -mail_plugin_dir = /lib/dovecot +mail_plugin_dir = /usr/lib/dovecot diff --git a/rootfs/etc/confd/templates/auth-oauth2.conf.ext.tmpl b/rootfs/etc/confd/templates/auth-oauth2.conf.ext.tmpl index 7d1a33a..5cfcf25 100644 --- a/rootfs/etc/confd/templates/auth-oauth2.conf.ext.tmpl +++ b/rootfs/etc/confd/templates/auth-oauth2.conf.ext.tmpl @@ -11,6 +11,7 @@ passdb { } userdb { - driver = lua - args = file=/etc/dovecot/oauth2-userdb.lua blocking=yes + driver = sql + args = /etc/dovecot/dovecot-sql.userdb.conf.ext + default_fields = uid=vmail gid=vmail home=/var/lib/vmail/mail/%u } diff --git a/rootfs/etc/confd/templates/dovecot-oauth2.plain.conf.ext.tmpl b/rootfs/etc/confd/templates/dovecot-oauth2.plain.conf.ext.tmpl index 419a169..743a60e 100644 --- a/rootfs/etc/confd/templates/dovecot-oauth2.plain.conf.ext.tmpl +++ b/rootfs/etc/confd/templates/dovecot-oauth2.plain.conf.ext.tmpl @@ -1,6 +1,4 @@ grant_url = {{ getenv "GRANT_URL" }} -client_id = {{ getenv "CLIENT_ID" }} -client_secret = {{ getenv "CLIENT_SECRET" }} introspection_url = {{ getenv "INTROSPECTION_URL" }} introspection_mode = {{ getenv "INTROSPECTION_MODE" "post" }} username_attribute = username diff --git a/rootfs/etc/confd/templates/dovecot-oauth2.token.conf.ext.tmpl b/rootfs/etc/confd/templates/dovecot-oauth2.token.conf.ext.tmpl index 4940f23..cc5b90e 100644 --- a/rootfs/etc/confd/templates/dovecot-oauth2.token.conf.ext.tmpl +++ b/rootfs/etc/confd/templates/dovecot-oauth2.token.conf.ext.tmpl @@ -1,6 +1,4 @@ grant_url = {{ getenv "GRANT_URL" }} -client_id = {{ getenv "CLIENT_ID" }} -client_secret = {{ getenv "CLIENT_SECRET" }} tokeninfo_url = {{ getenv "TOKENINFO_URL" }} introspection_url = {{ getenv "INTROSPECTION_URL" }} introspection_mode = {{ getenv "INTROSPECTION_MODE" "post" }} @@ -8,4 +6,3 @@ username_attribute = username tls_ca_cert_file = /etc/ssl/certs/ca-certificates.crt use_grant_password = no pass_attrs = pass=%{oauth2:access_token} - diff --git a/rootfs/etc/confd/templates/dovecot-sql.userdb.conf.ext.tmpl b/rootfs/etc/confd/templates/dovecot-sql.userdb.conf.ext.tmpl new file mode 100644 index 0000000..e87aed7 --- /dev/null +++ b/rootfs/etc/confd/templates/dovecot-sql.userdb.conf.ext.tmpl @@ -0,0 +1,3 @@ +driver = pgsql +connect = host={{ getenv "DB_HOST" }} dbname={{ getenv "DB_NAME" }} user={{ getenv "DB_USER" }} password={{ getenv "DB_PW" }} +user_query = SELECT COUNT(email) as count FROM virtual_users WHERE email = '%n' HAVING COUNT(email) > 0; diff --git a/rootfs/etc/confd/templates/oauth2-userdb.lua.tmpl b/rootfs/etc/confd/templates/oauth2-userdb.lua.tmpl deleted file mode 100644 index cc97b81..0000000 --- a/rootfs/etc/confd/templates/oauth2-userdb.lua.tmpl +++ /dev/null @@ -1,44 +0,0 @@ -local rapidjson = require('rapidjson') - -local clientId = "{{ getenv "CLIENT_ID" }}" -local clientSecret = "{{ getenv "CLIENT_SECRET" }}" -local username = "{{ getenv "OAUTH_ADMIN_USER" }}" -local password = "{{ getenv "OAUTH_ADMIN_PASSWORD" }}" -local tokenUrl = "{{ getenv "GRANT_URL" }}" -local userUrl = "{{ getenv "USER_URL" }}" - -function os.capture(cmd, raw) - local f = assert(io.popen(cmd, 'r')) - local s = assert(f:read('*a')) - f:close() - - return s -end - -function auth_userdb_lookup(req) - local tokenCmd = "curl -L --silent -X POST -d \"grant_type=password\"" - tokenCmd = tokenCmd .. " -d \"client_id=" .. clientId .. "\"" - tokenCmd = tokenCmd .. " -d \"client_secret=" .. clientSecret .. "\"" - tokenCmd = tokenCmd .. " -d \"username=" .. username .. "\"" - tokenCmd = tokenCmd .. " -d \"password=" .. password .. "\"" - tokenCmd = tokenCmd .. " \"" .. tokenUrl .. "\"" - - local tokenRaw = os.capture(tokenCmd) - local tokenJson = rapidjson.decode(tokenRaw) - local accessToken = tokenJson.access_token - - local userCmd = "curl -L --silent -H \"Authorization: Bearer " .. accessToken .. "\" \"" .. userUrl .. req.username .. "\"" - local userRaw = os.capture(userCmd) - local userJson = rapidjson.decode(userRaw) - - if #userJson == 0 then - return dovecot.auth.USERDB_RESULT_USER_UNKNOWN, "no such user" - end - - if userJson[1].username == req.username then - return dovecot.auth.USERDB_RESULT_OK, "uid=vmail gid=vmail home=/var/lib/vmail/mail/" .. req.username - end - - return dovecot.auth.USERDB_RESULT_USER_UNKNOWN, "no such user" -end -