Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 3 additions & 4 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -41,16 +41,15 @@ EMAIL_SERVERS_WITH_PRIORITY=[(10, "$SUBDOMAIN.$EMAIL_DOMAIN.")]
## In self-hosted, you typically trust all users. Set this variable to disable this option.
DISABLE_ALIAS_SUFFIX=1

## the DKIM private key used to compute DKIM-Signature
DKIM_PRIVATE_KEY_PATH=/dkim.key
## DKIM_PRIVATE_KEY_PATH, GNUPGHOME, TEMP_DIR and SAVE_UNSENT_DIR are all
## paths *inside* the container's sldata volume. There's no reason for you
## to configure these - they're fixed in simple-login-compose.yaml.

## DB Connection
DB_URI=postgresql://$POSTGRES_USER:$POSTGRES_PASSWORD@sl-db:5432/$POSTGRES_DB

FLASK_SECRET=paste-flask-secret-here

GNUPGHOME=/sl/pgp

LOCAL_FILE_UPLOAD=1

POSTFIX_SERVER=postfix
Expand Down
4 changes: 4 additions & 0 deletions docker-compose.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ include:
- traefik-compose.yaml
- simple-login-compose.yaml
- postfix-compose.yaml
## Migrates pre-existing ./db, ./pgp, ./upload, ./dkim.key content (if any)
## into the named volumes above. Safe to leave in for a fresh install too.
## Comment out once you've confirmed the migration succeeded.
- migrate-volumes-compose.yaml

networks:
internal:
Expand Down
59 changes: 59 additions & 0 deletions migrate-volumes-compose.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
# One-time migration helper: copies pre-existing data from the old
# bind-mount layout (./db, ./pgp, ./upload, ./dkim.key) into the named
# volumes now used by simple-login-compose.yaml, if you're upgrading an
# existing deployment.
#
# Safe to leave included even without any of the above present (e.g. a
# fresh install) - it just creates the sldata directory structure and
# does nothing else. Comment out the include line for this file in
# docker-compose.yaml once you've confirmed the migration succeeded.
#
# The whole project directory is bind-mounted read-only (rather than each
# legacy path individually) because the project directory always exists,
# whereas e.g. ./dkim.key commonly does not on a fresh setup - bind
# mounting a single nonexistent file path would make Docker silently
# create an empty directory there instead of leaving it absent.
services:
volume-migrate:
image: alpine:3.22
volumes:
- .:/legacy:ro
- db:/new/db
- sldata:/new/sldata
- upload:/new/upload
entrypoint:
- /bin/sh
- -c
- |
set -eu
migrate() {
if [ -n "$$(ls -A "$2" 2>/dev/null)" ]; then
echo "[volume-migrate] $2 already has data, skipping"
return
fi
if [ -d "$1" ] && [ -n "$$(ls -A "$1" 2>/dev/null)" ]; then
echo "[volume-migrate] copying existing $1 -> $2"
cp -a "$1"/. "$2"/
else
echo "[volume-migrate] no existing data at $1, starting fresh"
fi
}
mkdir -p /new/sldata/pgp /new/sldata/dkim /new/sldata/tmp /new/sldata/unsent
migrate /legacy/db /new/db
migrate /legacy/pgp /new/sldata/pgp
migrate /legacy/upload /new/upload
chmod 700 /new/sldata/pgp
if [ -n "$$(ls -A /new/sldata/dkim 2>/dev/null)" ]; then
echo "[volume-migrate] /new/sldata/dkim already has data, skipping"
elif [ -f /legacy/dkim.key ]; then
echo "[volume-migrate] copying existing dkim.key"
cp -a /legacy/dkim.key /new/sldata/dkim/dkim.key
chmod 600 /new/sldata/dkim/dkim.key
else
echo "[volume-migrate] no existing dkim.key"
fi

postgres:
depends_on:
volume-migrate:
condition: service_completed_successfully
27 changes: 25 additions & 2 deletions postfix-compose.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,36 @@ services:
networks:
internal:
ipv4_address: 10.0.0.53
volumes:
- ./unbound/conf.d/:/config/:ro
restart: unless-stopped
# Listener config is written at container start instead of bind-mounted
# from ./unbound/conf.d/, so this has no host-file dependency at all.
# Picked up via the image's own `include: /config/*.conf`, additively
# alongside its default listener on 5053 - so the image's built-in
# healthcheck (which probes 5053) is unaffected.
# Recipe: https://github.com/springcomp/self-hosted-simplelogin/pull/46#issuecomment-3707216659
#
# module-config/identity/root-hints match what the previous
# bind-mounted unbound/conf.d/00-unbound.conf set, to not weaken
# anything the upstream maintainer deliberately configured:
# - module-config trims the cachedb module the image loads by default
# (unused without a configured cache backend)
# - root-hints pins the image's own dns-root-hints package file, which
# the image refreshes monthly via its own cron
# (/etc/periodic/monthly/dns-root-hints) - the compiled-in fallback
# Unbound would otherwise use only updates when the image itself does
entrypoint:
- /bin/sh
- -ec
- |
cat > /config/00-listen-port.conf <<'EOF'
server:
interface: 0.0.0.0@53
log-queries: yes
verbosity: 2
module-config: "validator iterator"
identity: "DNS"
root-hints: "/usr/share/dns-root-hints/named.root"
EOF
unbound-anchor -a /var/run/unbound/root.key || true
exec su -s /bin/sh unbound -c "sh /entrypoint.sh"

Expand Down
59 changes: 54 additions & 5 deletions simple-login-compose.yaml
Original file line number Diff line number Diff line change
@@ -1,10 +1,18 @@
x-sl-defaults: &sl-defaults
image: simplelogin/$SL_IMAGE:$SL_VERSION
env_file: .env
# Paths inside the container - not meant to be end-user configurable
# (we control the whole image/volume layout), so these are fixed here
# rather than left for .env to set. Overrides any stale value a .env
# might still carry from before this change.
environment:
DKIM_PRIVATE_KEY_PATH: /sl/dkim/dkim.key
GNUPGHOME: /sl/pgp
TEMP_DIR: /sl/tmp
SAVE_UNSENT_DIR: /sl/unsent
volumes:
- ./pgp:/sl/pgp
- ./upload:/code/static/upload
- ./dkim.key:/dkim.key
- sldata:/sl
- upload:/code/static/upload
networks:
- internal

Expand All @@ -28,7 +36,7 @@ services:
networks:
- internal
volumes:
- ./db:/var/lib/postgresql/data
- db:/var/lib/postgresql/data
restart: unless-stopped

migration:
Expand All @@ -41,15 +49,51 @@ services:

init:
<<: *sl-defaults
command: ["python", "init_app.py"]
container_name: sl-init
# Ensures the sldata volume has the directory structure the app needs,
# and generates a DKIM key if none exists yet - runs on every start,
# but only ever acts once (mkdir -p and the missing-key check are both
# no-ops after the first successful run). This also covers the case
# where migrate-volumes-compose.yaml (which does the same mkdir -p, to
# have somewhere to copy pre-existing bind-mounted data into) has been
# commented out after migration, or was never used at all.
entrypoint:
- /bin/sh
- -c
- |
set -eu
umask 077
mkdir -p /sl/pgp /sl/dkim /sl/tmp /sl/unsent
if [ ! -s "$$DKIM_PRIVATE_KEY_PATH" ]; then
echo "[dkim-init] No key at $$DKIM_PRIVATE_KEY_PATH — generating 1024-bit RSA key..."
openssl genrsa -out "$$DKIM_PRIVATE_KEY_PATH" 1024
fi
chmod 600 "$$DKIM_PRIVATE_KEY_PATH" 2>/dev/null || true
exec python init_app.py
depends_on:
migration:
condition: service_completed_successfully

app:
<<: *sl-defaults
container_name: sl-app
# Prints the DKIM public key on every start, for convenience when
# setting up the DNS TXT record. No set -eu here: by the time this
# container starts, `init` above has already guaranteed a valid key
# exists, and the openssl pipe below wouldn't propagate a failure from
# its first stage anyway (no pipefail in /bin/sh), so -e would only
# look like protection without actually providing any.
#
# --timeout 45 (not the Dockerfile default of 15): a fix from an
# earlier deployment that never made it back into the app repo.
entrypoint:
- /bin/sh
- -c
- |
PUB="$(openssl rsa -in "$$DKIM_PRIVATE_KEY_PATH" -pubout -outform DER 2>/dev/null | openssl base64 -A)"
echo "[dkim-init] DKIM public key (p=) — add this to your DNS TXT record:"
echo "v=DKIM1; k=rsa; p=$$PUB"
exec gunicorn wsgi:app -b 0.0.0.0:7777 -w 2 --timeout 45
networks:
- traefik
- internal
Expand Down Expand Up @@ -110,3 +154,8 @@ services:
condition: service_completed_successfully
email:
condition: service_started

volumes:
db:
sldata:
upload:
14 changes: 0 additions & 14 deletions unbound/conf.d/00-unbound.conf

This file was deleted.

2 changes: 0 additions & 2 deletions unbound/conf.d/10-logging.conf

This file was deleted.