Compare commits

..

26 commits

Author SHA1 Message Date
PJ Eby
697e2a7039 Update push actions 2023-01-22 04:43:43 -05:00
PJ Eby
488a3d202c Update unstable tag to upstream 2.3.10 2023-01-22 04:39:31 -05:00
PJ Eby
e303e83e7b Fix Let's Encrypt renewals on 2.3.x 2023-01-22 03:47:45 -05:00
PJ Eby
0501a5c984 2.3.x compatibility (untested/unstable) 2022-02-04 20:44:40 -05:00
PJ Eby
2371f9167f Auto-update README on Docker Hub 2021-08-04 18:48:16 -04:00
PJ Eby
1f927c6300 Match entire tag during build 2021-08-04 17:22:58 -04:00
PJ Eby
2e6f102dce Fix build tag processing 2021-08-04 17:18:05 -04:00
PJ Eby
0fdc892716 Show correct redis URLs in log output 2021-08-04 17:12:30 -04:00
PJ Eby
8b82c06853 Handle tagged builds as well as unstable 2021-08-04 17:11:39 -04:00
PJ Eby
0ad79bf22e Add github actions build 2021-08-04 16:35:01 -04:00
PJ Eby
97f7da4108 Ensure cron errors can be sent 2021-08-04 13:55:20 -04:00
PJ Eby
e4c7539106 Update to 2.2.32 upstream 2021-05-31 16:06:26 -04:00
PJ Eby
825352a131 Fix #13 (ham/spam reporting issues w/sieve script) 2021-05-31 15:46:42 -04:00
PJ Eby
d20b2b481f Fix #12 - Haraka needs [] for IPv6 listen ports 2021-05-30 13:12:11 -04:00
PJ Eby
019a793c8e Update build to 2.2.31 2021-05-02 11:38:33 -04:00
PJ Eby
84cb77d234 Fix #9 - redis-cli needs socket arg 2021-05-02 11:03:09 -04:00
PJ Eby
3f79ad20bc Update build to 2.2.27 2021-01-22 12:34:51 -05:00
PJ Eby
5657070674 Fix recursive log rotation for roundcube 2020-12-07 15:59:41 -05:00
PJ Eby
5187d6272a Update build to 2.2.26 2020-12-07 15:43:06 -05:00
PJ Eby
280c3d59e1 Update build to 2.2.23 2020-10-31 13:35:02 -04:00
PJ Eby
f7312dbb06 Update build to 2.2.20 2020-06-03 13:15:30 -04:00
PJ Eby
3e3d73ef8a Patch health checks to do the right thing 2020-04-26 17:11:58 -04:00
PJ Eby
4b6a1839fb Fake 127.0.0.1 for connections from listening IPs 2020-04-15 20:24:48 -04:00
PJ Eby
333667db8c Updates for 2.2.19 2020-04-15 17:18:28 -04:00
PJ Eby
b310e9b3de Support upstream version selection w/UPSTREAM arg 2020-04-15 13:50:18 -04:00
PJ Eby
bee74835b5 Document build tagging 2020-04-06 03:29:33 -04:00
6 changed files with 161 additions and 33 deletions

53
.github/workflows/docker-hub.yml vendored Normal file
View file

@ -0,0 +1,53 @@
name: Build and Push to Docker Hub
on:
push:
jobs:
docker:
runs-on: ubuntu-latest
steps:
- name: Calculate Tags
env:
ref: ${{ github.ref }}
repo: ${{ github.repository }}
run: |
ref=${ref##*/}
if [[ $ref == master ]]; then
tags=$repo:unstable
elif [[ $ref =~ ^([0-9.]+)-((([0-9]+[.])[0-9]+[.])([.][0-9+])*)$ ]]; then
upstream=${BASH_REMATCH[1]}
minor=${BASH_REMATCH[3]}x
major=${BASH_REMATCH[4]}x
tags=$repo:latest,$repo:$ref,$repo:$upstream-$minor,$repo:$upstream-$major,$repo:$upstream
else
echo "Bad tag: $ref"
exit 1
fi
echo "$tags"
echo "build_tags=$tags" >> $GITHUB_ENV
- name: Login to DockerHub
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Checkout
uses: actions/checkout@v3
- name: Build and push
id: build
uses: docker/build-push-action@v3
with:
context: .
push: true
tags: ${{ env.build_tags }}
- name: Update repo description
uses: peter-evans/dockerhub-description@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_PASSWORD }}
repository: ${{ github.repository }}

View file

@ -1,4 +1,5 @@
FROM analogic/poste.io:2.2.2
ARG UPSTREAM=2.3.10
FROM analogic/poste.io:$UPSTREAM
RUN apt-get update && apt-get install less # 'less' is Useful for debugging
# Default to listening only on IPs bound to the container hostname

View file

@ -26,6 +26,7 @@ Specifically, it lets you:
- [Using Custom Roundcube Plugins](#using-custom-roundcube-plugins)
* [The DES_KEY Variable](#the-des_key-variable)
- [Can I use these changes with poste.io's PRO version?](#can-i-use-these-changes-with-posteios-pro-version)
- [Docker Tags](#docker-tags)
<!-- tocstop -->
@ -93,6 +94,7 @@ Take note of the following, however:
* The listening IPs must *not* have any other services listening on ports 25, 80, 110, 143, 443, 466, 587, 993, 995, or 4190. (Though you can change or disable some of those ports using poste.io's environment variables.)
* You should be using **host-mode networking** (`network_mode: host` as shown above), since in any other networking mode, this image will behave roughly the same as the original `analogic/poste.io` image, and have the same limitations and caveats. (Specifically, using any other networking mode means putting specific IP addresses into `LISTEN_ON`, `SEND_ON`, or `outbound-hosts.yml` will not do anything useful!)
* By default, outgoing email to other mail servers will be sent via the first IP address found in `LISTEN_ON` or returned by running `hostname -i` in the container. If you need to override this behavior, configure the container with `SEND_ON` set to the specific IP address to be used, OR create a `/data/outbound-hosts.yml` file as described in [Managing Sender IPs](#managing-sender-ips) below.
* Connections *from* a listening IP will be treated as if they are connections from 127.0.0.1 (because they are from the local host) unless you're using `LISTEN_ON=*` mode. This disables certain host-specific spam checks (e.g. asn, fcrdns, karma/history, etc.), that would otherwise apply. This special behavior is *not* enabled for IPs that are used only for outgoing mail transmission; such IPs will be treated as normal unless you explicitly add them to your relay networks list.
Notice, by the way, that there are **no port mappings** used in this example, because the container uses host-mode networking and thus has direct access to all of the server's network interfaces. This means that the IP addresses to be used by the container must be explicitly defined (either by the DNS address(es) of the hostname, or by setting the `LISTEN_ON` variable to the exact IP addresses) so that the container doesn't take over every IP address on the server. (Unless that's what you *want*, in which case you can set `LISTEN_ON` to `*`.)
@ -173,4 +175,9 @@ Some plugins (such as [ident_switch](https://bitbucket.org/BoresExpress/ident_sw
I don't know, but you can find out by cloning this repo, changing the `FROM` in the Dockerfile, and trying to run the resulting build. It *might* work, since the main difference between the two versions is some admin interface code left out of the free version. But if that left-out code contains hardcoded or implicit references to localhost or 127.0.0.1, then those admin features will probably break, as they won't have been patched to use unix-domain sockets (or the container's hostname) instead.
If they do break, and you can figure out what to patch (most likely, PHP code in `/opt/admin/src/ProBundle/`), let me know. (Or if it works fine, I'd love to know that, too!)
If they do break, and you can figure out what to patch (most likely, PHP code in `/opt/admin/src/ProBundle/`), let me know. (Or if it works fine, I'd love to know that, too!)
### Docker Tags
Apart from `latest`, and `unstable`, current versions of this image on docker hub are tagged as a combination of the upstream version and a version number for this image's additions. For example, `2.2.2-0.3.1` is the `0.3.1` revision of upstream poste's `2.2.2` tag, if you need to pin a specific revision. You can also just use the upstream version (e.g. `2.2.2`) to get the latest patches for that upstream version, or `latest` to get the most-recent stable version. The `unstable` tag always refers to the current `master` branch from github.

View file

@ -2,13 +2,21 @@
# Given a variable name and setting, get the matching IP addresses as a comma-delimited list
function ip_list() {
local -n ips=$1
local -n ips=$1 v6=${1}_b
case $2 in
host) ips=$(hostname -i) ;;
'*') ips='* ::' ;;
*) read -ra ips <<<"$2"; ips=("${ips[*]}") ;; # trim/normalize whitespace
esac
ips="${ips// /,}"; ips=${ips:-*,::} # handle empty list
# Create a bracketed version for configs that need [host]:port for IPv6 addrs
local addr i
IFS=, read -ra addr <<<"$ips"
for i in "${!addr[@]}"; do
case ${addr[i]} in *:*) addr[i]="[${addr[i]}]" ;; esac
done
v6=("${addr[*]}"); v6="${v6// /,}"
}
# Expand LISTEN_ON and SEND_ON into comma-delimited IP lists in `listen` and `send`
@ -27,6 +35,11 @@ bindhost=$(hostname)
# We only care about the hostname for connnecting to the submission port
sub 'submission_host = .*:587$' "submission_host = $bindhost:587" /etc/dovecot/conf.d/15-lda.conf
sub '^host.*' "host $bindhost" /etc/msmtprc
# Admin emails should go to the bindhost as well
sub "%env(MAILER_DSN)%" "smtp://$bindhost:25?verify_peer=0" /opt/admin/config/packages/mailer.yaml
sub "MAILER_DSN=.*" "MAILER_DSN=smtp://$bindhost:25?verify_peer=0" /opt/admin/.env
if [[ "$LISTEN_ON" == host ]]; then
# No IPs given, just use the hostname
@ -36,9 +49,8 @@ else
# We have explicit listening IPs (or wildcards): give them to dovecot and nginx
sub '^#\?listen = .*' "listen = ${listen}" /etc/dovecot/dovecot.conf
IFS=, read -ra ipaddrs <<<"$listen"
IFS=, read -ra ipaddrs <<<"$listen_b"
for addr in "${ipaddrs[@]}"; do
if [[ "$addr" == *:* ]]; then addr="[${addr}]"; fi # nginx needs IPv6 addresses to be in '[]'
# Add listen lines above the default ones, for the specified address, port and options
ins "__HOST__:$HTTP_PORT" " listen $addr:$HTTP_PORT;" /etc/nginx/sites-enabled/administration
ins "__HOST__:$HTTPS_PORT" " listen $addr:$HTTPS_PORT ssl;" /etc/nginx/sites-enabled/administration
@ -52,8 +64,10 @@ fi
# === Haraka needs each IP address to be listed explicitly, unless you're using wildcards ===
if [[ $listen != *'*'* ]]; then
sub '^listen=.*:25$' "listen=${listen//,/:25,}:25" /opt/haraka-smtp/config/smtp.ini
sub '^listen=.*:587,.*:465$' "listen=${listen//,/:587,}:587,${listen//,/:465,}:465" /opt/haraka-submission/config/smtp.ini
sub '^listen=.*:25$' "listen=${listen_b//,/:25,}:25" /opt/haraka-smtp/config/smtp.ini
sub '^listen=.*:587,.*:465$' "listen=${listen_b//,/:587,}:587,${listen_b//,/:465,}:465" /opt/haraka-submission/config/smtp.ini
else
listen=::0
fi
# Our Haraka sender-ip control plugin will validate outgoing IPs against the
@ -62,3 +76,8 @@ fi
echo "$send" >/opt/haraka-submission/config/my-ips
echo "$send" >/opt/haraka-smtp/config/my-ips
# Our inbound IP plugin will translate local connections to 127.0.0.1
echo "$listen" >/opt/haraka-submission/config/listen-ips
echo "$listen" >/opt/haraka-smtp/config/listen-ips

View file

@ -0,0 +1,21 @@
'use strict';
/*****
This plugin detects local connections from listening IPs, and changes the remote
IP to 127.0.0.1, simulating a local connection. Many of poste.io's plugins
have 127.0.0.1 hardcoded for special handling that otherwise might not be applied
when using this image.
*****/
const listening_ips = require("haraka-config").get("listen-ips").trim().split(/\s*,\s*/);
const is_local = listening_ips.reduce((map, addr)=>{map[addr]=true; return map;}, {});
exports.hook_connect_init = function(next, connection) {
if ( is_local[connection.remote.ip] ) {
this.logdebug(`localhost connection from ${connection.remote.ip}`);
connection.remote.ip = '127.0.0.1';
}
return next();
}

View file

@ -14,9 +14,10 @@
# loopback interface.
set -eu # fail on any errors or undefined variables
shopt -s nullglob
# A tiny DSL for editing files with sed: `~ edit files...; {{ commands }}`
edit() { local sed; ::block sed-dsl; sed -i -e "$sed" "$@"; }
edit() { local sed; ::block sed-dsl; if (($#)); then sed -i -e "$sed" "$@"; fi; }
sed-dsl() { sed."$@"; }
sed.sub() { sed+="s~$1~$2~${3-}"$'\n'; }
sed.del() { sed+="${1+/$1/}d"$'\n'; }
@ -29,6 +30,15 @@ __sedline() { sed+="${*/#/\\$'\n'}"; }
shopt -q expand_aliases||{ unalias -a;shopt -s expand_aliases;};builtin alias +='{ ::__;::(){ ((!$#))||{ shift;"${__dsl__[@]-::no-dsl}" ' ~='{ ::__;::(){ ((!$#))||{ shift; ' -='"${__dsl__[@]-::no-dsl}" ' '{{=return;return;};__blk__=;set -- "${__blarg__[@]:1}"; ' '}}=};__:: 0 "$@";}';::block(){ ((!$#))||local __dsl__=("$@");${__blk__:+::};};__bsp__=0;::__(){ __bstk__[__bsp__++]="${__blk__:+__blk__=1;$(declare -f ::)}";};__::(){ local __blarg__=("$@");__blk__=1;:: "$@"||set -- $?;__blk__=;local REPLY;${__bstk__[--__bsp__]:+eval "${__bstk__[__bsp__]}"}||:;return $1;}
# === Upstream bug fixes ===
# Remove this when 2.3.x is stable
~ edit opt/admin/src/AppBundle/Resources/views/Box/edit.html[.]twig; {{
# Fix typo
- sub "refereneId" "referenceId"
}}
# === Restrict public ports to the container hostname IP ===
~ edit /opt/www/webmail/config/config.inc.php; {{
@ -42,7 +52,15 @@ shopt -q expand_aliases||{ unalias -a;shopt -s expand_aliases;};builtin alias +=
}}
}}
~ edit /opt/admin/src/AppBundle/CommandInternal/DeliverQuarantineCommand.php; {{
~ edit /healthcheck/nginx.sh; {{
- sub "http://127.0.0.1" '"http://$(hostname)"'
}}
~ edit \
/opt/admin/src/AppBundle/CommandInternal/DeliverQuarantineCommand[.]php \
/opt/admin/src/Base/CommandInternal/DeliverQuarantineCommand[.]php ;
{{
# Quarantine "deliver" / deliver:quarantine should send to host, not localhost
- sub "\['msmtp', '-f'.*" "['msmtp', '--host', gethostname(), '-f', \$meta['from']];"
}}
@ -58,6 +76,9 @@ shopt -q expand_aliases||{ unalias -a;shopt -s expand_aliases;};builtin alias +=
}}
~ edit /opt/haraka-{smtp,submission}/config/plugins; {{
# Fake remote IP to 127.0.0.1 when connection is from localhost
- after "status_http" \
"inbound_ips"
# Add our outbound IP routing plugin
- append 'outbound_ips'
}}
@ -96,13 +117,13 @@ haraka_sub_web=$sockdir/haraka/web-11381.sock
# The rspamc command needs to reference the web socket explicitly
~ edit /opt/admin/src/AppBundle/Server/System.php; {{
~ edit /opt/admin/src/AppBundle/Server/System[.]php /opt/admin/src/Base/Server/System[.]php; {{
- sub "rspamc stat" \
"rspamc -h $rspam_web stat"
}}
~ edit /etc/dovecot/sieve/report-{spam,ham}.sieve; {{
- sub '"rspamc" \[' \
'"rspamc" ["-h" "'"$rspam_web"'" '
'"rspamc" ["--connect='"$rspam_web"'", '
}}
# Disable dovecot quota service on localhost
@ -120,41 +141,33 @@ haraka_sub_web=$sockdir/haraka/web-11381.sock
"socket.connect('$quota');"
}}
# Haraka web servers need to listen on unix sockets
~ edit /usr/lib/node_modules/Haraka/server.js; {{
- sub 'Server.get_listen_addrs(Server.http.cfg, 80)' \
'[Server.http.cfg.listen]'
+ range '^Server.setup_http_listeners' '^}$'; {{
- sub 'const hp = .*' \
'const hp = [null, null, host_port];'
- sub 'Server.http.server.listen.*$' \
'!fs.existsSync(host_port)||fs.unlinkSync(host_port); Server.http.server.listen(host_port, function(){fs.chmodSync(host_port, 0o777);});'
}}
# Haraka logs should show the redis socket
~ edit /usr/lib/node_modules/Haraka/node_modules/haraka-plugin-redis/index.js; {{
- sub 'redis://\${opts.host}:\${opts.port}' \
'redis://${opts.path}'
}}
# Haraka web servers need to listen on unix sockets
~ edit /opt/haraka-smtp/config/http.ini; {{
- sub 'listen=127.0.0.1:11380' "listen=$haraka_smtp_web"
- sub 'listen=127.0.0.1:11380' "listen=$haraka_smtp_web:777"
}}
~ edit /opt/haraka-submission/config/http.ini; {{
- sub 'listen=127.0.0.1:11381' "listen=$haraka_sub_web"
- sub 'listen=127.0.0.1:11381' "listen=$haraka_sub_web:777"
}}
# Have haraka talk to rspamd via unix socket
~ edit /usr/lib/node_modules/Haraka/node_modules/haraka-plugin-rspamd/index.js; {{
- del 'port: plugin'
- sub 'host: plugin.*,' \
"socketPath: '$rspam',"
~ edit /opt/haraka-{smtp,submission}/config/rspamd.ini; {{
- sub '^host.*=.*$' "unix_socket = $rspam"
}}
# Configure redis to listen on a unix socket, and rspamd+admin to connect there
~ edit /etc/redis/redis.conf; {{
- sub "^port 6379" "port 0" # disable the localhost port
- append "" "unixsocket $redis" "unixsocketperm 777"
- append "" "unixsocket $redis" "unixsocketperm 777" # can be removed as of 2.3.7
}}
~ edit /etc/rspamd/local.d/{redis,statistic}.conf; {{
@ -162,7 +175,19 @@ haraka_sub_web=$sockdir/haraka/web-11381.sock
'servers = "'"$redis"'";'
}}
~ edit /opt/admin/src/AppBundle/Resources/config/services.yml; {{
~ edit /healthcheck/redis.sh; {{
- sub '-h "127.0.0.1"' "-s '$redis'";
}}
~ edit /bin/clear[-]idle-connections; {{ # can be removed as of 2.3.7
- sub "redis-cli'" "redis-cli', '-s', '$redis'"
}}
~ edit /bin/poste-redis-statistics; {{
- sub "redis-cli" "redis-cli -s '$redis'"
}}
~ edit /opt/admin/src/AppBundle/Resources/config/services[.]yml /opt/admin/config/services_base[.]yaml; {{
- sub '^ Predis\\Client: .*$' \
' Predis\\Client: { arguments: [ "unix:'"$redis"'" ] }'
@ -173,8 +198,10 @@ haraka_sub_web=$sockdir/haraka/web-11381.sock
# === Support Roundcube plugins and persistent encryption key
# Load 48-digit hex des_key from DES_KEY
~ edit /etc/cont-init.d/20-apply-server-config; {{
- sub '[$]key = bin2hex' '$key = getenv("DES_KEY") ?: bin2hex'
~ edit /etc/cont-init.d/{20-apply-server-config,97[-]randoms}; {{
+ range 'roundcube' 'preg_replace'; {{
- sub '[$]key = bin2hex' '$key = getenv("DES_KEY") ?: bin2hex'
}}
}}
# Autoload roundcube plugins from /data/roundcube/installed-plugins