Compare commits
26 commits
2.2.2-0.3.
...
master
Author | SHA1 | Date | |
---|---|---|---|
![]() |
697e2a7039 | ||
![]() |
488a3d202c | ||
![]() |
e303e83e7b | ||
![]() |
0501a5c984 | ||
![]() |
2371f9167f | ||
![]() |
1f927c6300 | ||
![]() |
2e6f102dce | ||
![]() |
0fdc892716 | ||
![]() |
8b82c06853 | ||
![]() |
0ad79bf22e | ||
![]() |
97f7da4108 | ||
![]() |
e4c7539106 | ||
![]() |
825352a131 | ||
![]() |
d20b2b481f | ||
![]() |
019a793c8e | ||
![]() |
84cb77d234 | ||
![]() |
3f79ad20bc | ||
![]() |
5657070674 | ||
![]() |
5187d6272a | ||
![]() |
280c3d59e1 | ||
![]() |
f7312dbb06 | ||
![]() |
3e3d73ef8a | ||
![]() |
4b6a1839fb | ||
![]() |
333667db8c | ||
![]() |
b310e9b3de | ||
![]() |
bee74835b5 |
6 changed files with 161 additions and 33 deletions
53
.github/workflows/docker-hub.yml
vendored
Normal file
53
.github/workflows/docker-hub.yml
vendored
Normal 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 }}
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
21
files/opt/haraka-smtp/plugins/inbound_ips.js
Normal file
21
files/opt/haraka-smtp/plugins/inbound_ips.js
Normal 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();
|
||||
}
|
|
@ -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
|
||||
|
|
Loading…
Add table
Reference in a new issue