Compare commits
6 Commits
f29b3fe156
...
0678d5ddd4
Author | SHA1 | Date | |
---|---|---|---|
0678d5ddd4 | |||
0b2de4c8d0 | |||
f333789ec9 | |||
53a19053ec | |||
e2af62042a | |||
d3f91889af |
43
README.md
43
README.md
@ -1,18 +1,45 @@
|
||||
# ketreweb containers
|
||||
|
||||
The cron job to update certificates isn't quite working yet.
|
||||
| Container | Purpose |
|
||||
|:----------------------|:-----------------------------------------------|
|
||||
| ketrenet-web | Main webserver entry for https://ketrenos.com |
|
||||
| ketrenet-mail | Email MTA and mailbox management |
|
||||
| ketrenet-roundcube | Email web interface. https://mail.ketrenos.com |
|
||||
| ketrenet-cron | Update letsencrypt keys, etc. |
|
||||
| ketrenet-dns | DNS server |
|
||||
| ketrenet-mailman-core | Mailman3 Core backend |
|
||||
| ketrenet-mailman-web | Mailman3 Web frontend |
|
||||
| ketrenet-database | Mailman3 Postgres DB |
|
||||
|
||||
To update certificates:
|
||||
## Disk structure
|
||||
|
||||
| Directory | Purpose |
|
||||
|:----------------------|:-----------------------------------------------|
|
||||
| $PWD/data/$SERVICE | Logs, error reporting, databases |
|
||||
| $SERVICE/etc | Configuration files for $SERVICE |
|
||||
| $PWD/www | Web content hosted by ketrenet-web directly |
|
||||
|
||||
## Update certificates
|
||||
|
||||
The cron job to update certificates isn't quite working yet. To update
|
||||
certificates:
|
||||
|
||||
```bash
|
||||
docker exec -it ketrenet-cron /bin/bash
|
||||
/usr/bin/certbot renew --no-self-upgrade --webroot -w /var/www/ketrenos.com
|
||||
/usr/bin/scp -q -i /keys/opnsense-letsencrypt /etc/letsencrypt/live/ketrenos.com/{fullchain,privkey}.pem letsencrypt@opnsense.ketrenos.com:.
|
||||
/usr/bin/ssh -i /keys/opnsense-letsencrypt letsencrypt@opnsense.ketrenos.com sudo ./update-cert.sh fullchain.pem privkey.pem
|
||||
/usr/bin/certbot renew --no-self-upgrade --webroot \
|
||||
-w /var/www/ketrenos.com
|
||||
/usr/bin/scp -q \
|
||||
-i /keys/opnsense-letsencrypt \
|
||||
/etc/letsencrypt/live/ketrenos.com/{fullchain,privkey}.pem \
|
||||
letsencrypt@opnsense.ketrenos.com:.
|
||||
/usr/bin/ssh \
|
||||
-i /keys/opnsense-letsencrypt \
|
||||
letsencrypt@opnsense.ketrenos.com \
|
||||
sudo ./update-cert.sh fullchain.pem privkey.pem
|
||||
```
|
||||
|
||||
After that completes (without errors) outside the container use `./sync-certs` to push
|
||||
the updated certificates to all the service containers and servers.
|
||||
After that completes (without errors) outside the container use `./sync-certs`
|
||||
to push the updated certificates to all the service containers and servers.
|
||||
|
||||
## ketreweb
|
||||
|
||||
@ -23,7 +50,7 @@ apache2 is only being used for mailman, which is currently offline
|
||||
|
||||
## ketreweb-roundcube
|
||||
|
||||
default container for roundcube
|
||||
default container for roundcube. Provides https://mail.ketrenos.com
|
||||
|
||||
## ketreweb-cron
|
||||
|
||||
|
@ -12,14 +12,20 @@ services:
|
||||
- /home/jketreno/docker/webserver/web/etc/nginx/sites-enabled:/etc/nginx/sites-enabled:ro
|
||||
- /home/jketreno/docker/webserver/web/etc/nginx/sites-available:/etc/nginx/sites-available:ro
|
||||
- /home/jketreno/docker/webserver/web/etc/apache2:/etc/apache2:ro
|
||||
- /home/jketreno/docker/webserver/keys/cron/etc/letsencrypt/options-ssl-apache.conf:/etc/letsencrypt/options-ssl-apache.conf:ro
|
||||
- /home/jketreno/docker/webserver/keys/cron/etc/letsencrypt/options-ssl-nginx.conf:/etc/letsencrypt/options-ssl-nginx.conf:ro
|
||||
- /home/jketreno/docker/webserver/keys/cron/etc/letsencrypt/live:/etc/letsencrypt/live:ro
|
||||
- /home/jketreno/docker/webserver/keys/cron/etc/letsencrypt/archive:/etc/letsencrypt/archive:ro
|
||||
- /home/jketreno/docker/webserver/web/entrypoint.sh:/entrypoint.sh:ro
|
||||
- /home/jketreno/docker/webserver/data/log:/var/log:rw
|
||||
- /home/jketreno/docker/webserver/www:/var/www:ro
|
||||
- /home/jketreno/docker/webserver/data/mailman/web/data/static:/opt/mailman/static:ro
|
||||
ports:
|
||||
- 80:80
|
||||
- 443:443
|
||||
networks:
|
||||
ketrenet-mailman:
|
||||
|
||||
|
||||
ketrenet-mail:
|
||||
image: ketrenet-mail
|
||||
@ -30,11 +36,13 @@ services:
|
||||
dockerfile: Dockerfile.mail
|
||||
restart: always
|
||||
ports:
|
||||
- 143:143 # dovecot imap
|
||||
- 993:993 # dovecot imaps
|
||||
- 465:465 # postfix smtps
|
||||
- 587:587 # postfix submission
|
||||
- 143:143 # dovecot imap
|
||||
- 993:993 # dovecot imaps
|
||||
- 465:465 # postfix smtps
|
||||
- 587:587 # postfix submission
|
||||
volumes:
|
||||
- /home:/home:rw # User home directories for Maildir access
|
||||
- /home/jketreno/docker/webserver/data/mailman/core/var/data/postfix_lmtp:/opt/mailman/postfix_lmtp:ro # Used for LMTP to ketrenet-mailman-core
|
||||
- /home/jketreno/docker/webserver/keys/cron/etc/letsencrypt/live:/etc/letsencrypt/live:ro
|
||||
- /home/jketreno/docker/webserver/keys/cron/etc/letsencrypt/archive:/etc/letsencrypt/archive:ro
|
||||
- /home/jketreno/docker/webserver/mail/etc/mailname:/etc/mailname:ro
|
||||
@ -48,22 +56,20 @@ services:
|
||||
- /home/jketreno/docker/webserver/mail/etc/opendkim:/etc/opendkim:ro
|
||||
- /home/jketreno/docker/webserver/mail/etc/postfix:/etc/postfix:rw
|
||||
- /home/jketreno/docker/webserver/mail/etc/milter-greylist:/etc/milter-greylist:ro
|
||||
- /home/jketreno/docker/webserver/data/mail/var/lib/milter-greylist:/var/lib/milter-greylist:rw
|
||||
- /home/jketreno/docker/webserver/mail/etc/default/milter-greylist:/etc/default/milter-greylist:ro
|
||||
- /home/jketreno/docker/webserver/mail/entrypoint.sh:/entrypoint.sh:ro
|
||||
- /home/jketreno/docker/webserver/data/log:/var/log:rw
|
||||
- /home/jketreno/docker/webserver/data/mail/var/mail:/var/mail:rw
|
||||
- /home/jketreno/docker/webserver/data/mail/var/spool/mail:/var/spool/mail:rw
|
||||
- /home/jketreno/docker/webserver/data/mail/var/lib/milter-greylist:/var/lib/milter-greylist:rw
|
||||
- /home:/home:rw
|
||||
- /home/jketreno/docker/webserver/www:/var/www:ro
|
||||
- /home/jketreno/docker/webserver/data/mail/var/lib/clamav:/var/lib/clamav:rw
|
||||
- /home/jketreno/docker/webserver/mail/etc/rsyslog.conf:/etc/rsyslog.conf:ro
|
||||
- /home/jketreno/docker/webserver/mail/etc/default/milter-greylist:/etc/default/milter-greylist:ro
|
||||
# Keys
|
||||
- /home/jketreno/docker/webserver/keys/mail/etc/dkimkeys:/etc/dkimkeys:ro
|
||||
- /home/jketreno/docker/webserver/keys/mail/etc/spamassassin/sa-update-keys/:/etc/spamassassin/sa-update-keys:rw
|
||||
- /home/jketreno/docker/webserver/keys/mail/etc/dovecot/private:/etc/dovecot-private:ro
|
||||
- /home/jketreno/docker/webserver/keys/mail/etc/opendkim:/etc/opendkim-private:rw
|
||||
|
||||
# Authentication of dovecot users via pam
|
||||
#
|
||||
#- /etc/passwd:/etc/systempasswd:ro
|
||||
@ -71,6 +77,8 @@ services:
|
||||
#- /etc/shadow:/etc/systemshadow:ro
|
||||
#- /etc/gshadow:/etc/systemgshadow:ro
|
||||
# etc directories with permissions locked down
|
||||
networks:
|
||||
ketrenet-mailman:
|
||||
|
||||
ketrenet-roundcube:
|
||||
image: ketrenet-roundcube
|
||||
@ -81,15 +89,18 @@ services:
|
||||
dockerfile: Dockerfile.roundcube
|
||||
restart: always
|
||||
environment:
|
||||
- ROUNDCUBEMAIL_DEFAULT_HOST=tls://ketrenos.com
|
||||
- ROUNDCUBEMAIL_SMTP_SERVER=tls://ketrenos.com
|
||||
- ROUNDCUBEMAIL_SMTP_PORT=587
|
||||
- ROUNDCUBEMAIL_DEFAULT_HOST=tls://ketrenos.com
|
||||
- ROUNDCUBEMAIL_SMTP_SERVER=tls://ketrenos.com
|
||||
- ROUNDCUBEMAIL_SMTP_PORT=587
|
||||
ports:
|
||||
- 8124:80
|
||||
volumes:
|
||||
- /home/jketreno/docker/webserver/roundcube/var/roundcube:/var/roundcube/config:ro
|
||||
- /home/jketreno/docker/webserver/data/roundcube/db:/var/roundcube/db:rw
|
||||
- /home/jketreno/docker/webserver/data/roundcube/html:/var/www/html:rw
|
||||
networks:
|
||||
ketrenet-mailman:
|
||||
|
||||
|
||||
ketrenet-cron:
|
||||
image: ketrenet-cron
|
||||
@ -129,3 +140,94 @@ services:
|
||||
- /home/jketreno/docker/webserver/dns/entrypoint.sh:/entrypoint.sh:ro
|
||||
- /home/jketreno/docker/webserver/data/log:/var/log:rw
|
||||
- /home/jketreno/docker/webserver/data/dns/var/lib/:/var/lib:rw
|
||||
|
||||
ketrenet-mailman-core:
|
||||
image: maxking/mailman-core:0.4 # Use a specific version tag (tag latest is not published)
|
||||
container_name: ketrenet-mailman-core
|
||||
hostname: mailman-core
|
||||
restart: unless-stopped
|
||||
volumes:
|
||||
- /home/jketreno/docker/webserver/data/mailman/core/var/etc/mailman.cfg:/etc/mailman.cfg
|
||||
- /home/jketreno/docker/webserver/data/mailman/core:/opt/mailman/
|
||||
stop_grace_period: 30s
|
||||
links:
|
||||
- ketrenet-database:ketrenet-database
|
||||
depends_on:
|
||||
- ketrenet-database
|
||||
environment:
|
||||
- DATABASE_URL=postgresql://mailman:mailmanpass@ketrenet-database/mailmandb
|
||||
- DATABASE_TYPE=postgres
|
||||
- DATABASE_CLASS=mailman.database.postgresql.PostgreSQLDatabase
|
||||
- HYPERKITTY_URL=http://ketrenet-mailman-web:8000/hyperkitty
|
||||
- HYPERKITTY_API_KEY=s0meap1k3y
|
||||
- MAILMAN_ADMIN_USER=jketreno
|
||||
- MAILMAN_ADMIN_EMAIL=james_mailman@ketrenos.com
|
||||
- SMTP_HOST=ketrenos.com
|
||||
- SMTP_PORT=587
|
||||
- MTA=postfix
|
||||
ports:
|
||||
- "127.0.0.1:8001:8001" # API
|
||||
- "127.0.0.1:8024:8024" # LMTP - incoming emails
|
||||
networks:
|
||||
ketrenet-mailman:
|
||||
|
||||
|
||||
ketrenet-mailman-web:
|
||||
image: maxking/mailman-web:0.4 # Use a specific version tag (tag latest is not published)
|
||||
container_name: ketrenet-mailman-web
|
||||
hostname: mailman-web
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
- ketrenet-database
|
||||
links:
|
||||
- ketrenet-mailman-core:ketrenet-mailman-core
|
||||
- ketrenet-database:ketrenet-database
|
||||
volumes:
|
||||
- /home/jketreno/docker/webserver/data/mailman/web/data:/opt/mailman-web-data
|
||||
- /home/jketreno/docker/webserver/data/mailman/web/config:/opt/mailman-web
|
||||
environment:
|
||||
- DATABASE_TYPE=postgres
|
||||
- DATABASE_URL=postgresql://mailman:mailmanpass@ketrenet-database/mailmandb
|
||||
- HYPERKITTY_API_KEY=s0meap1k3y
|
||||
- HYPERKITTY_URL=http://ketrenet-mailman-web:8000/hyperkitty
|
||||
- SECRET_KEY=s7p4rs3kr1t
|
||||
- SERVE_FROM_DOMAIN=ketrenos.com
|
||||
- UWSGI_STATIC_MAP=/mailman/static=/opt/mailman-web-data/static
|
||||
- MAILMAN_ADMIN_USER=jketreno
|
||||
- MAILMAN_ADMIN_EMAIL=james_mailman@ketrenos.com
|
||||
- EMAIL_HOST=ketrenos.com
|
||||
- EMAIL_PORT=587
|
||||
- EMAIL_USE_TLS=True
|
||||
- EMAIL_USE_SSL=False
|
||||
- SMTP_HOST=ketrenos.com
|
||||
- SMTP_PORT=587
|
||||
- MTA=postfix
|
||||
ports:
|
||||
- "127.0.0.1:8000:8000" # HTTP
|
||||
- "127.0.0.1:8080:8080" # uwsgi
|
||||
networks:
|
||||
ketrenet-mailman:
|
||||
|
||||
|
||||
ketrenet-database:
|
||||
container_name: ketrenet-database
|
||||
environment:
|
||||
- POSTGRES_DB=mailmandb
|
||||
- POSTGRES_USER=mailman
|
||||
- POSTGRES_PASSWORD=mailmanpass
|
||||
- MAILMAN_ADMIN_USER=jketreno
|
||||
- MAILMAN_ADMIN_EMAIL=james_mailman@ketrenos.com
|
||||
image: postgres:12-alpine
|
||||
volumes:
|
||||
- /home/jketreno/docker/webserver/data/mailman/database:/var/lib/postgresql/data
|
||||
networks:
|
||||
ketrenet-mailman:
|
||||
|
||||
|
||||
networks:
|
||||
ketrenet-mailman:
|
||||
driver: bridge
|
||||
ipam:
|
||||
driver: default
|
||||
config:
|
||||
- subnet: 172.19.199.0/24
|
||||
|
@ -32,4 +32,9 @@ RUN apt-get -q update \
|
||||
COPY ./Dockerfile.mail /Dockerfile
|
||||
COPY ./entrypoint.sh /entrypoint.sh
|
||||
|
||||
# Change ownership of /opt/mailman so that entrypoint.sh
|
||||
# can watch the postfix_lmtp file and postmap it to a DB
|
||||
# file
|
||||
RUN mkdir /opt/mailman && chown 100:65533 /opt/mailman
|
||||
|
||||
ENTRYPOINT [ "/entrypoint.sh" ]
|
||||
|
@ -1,43 +1,75 @@
|
||||
#!/bin/bash
|
||||
fail() {
|
||||
echo "FAIL: ${*}" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
# clamav needs access to read the spool files from amavis
|
||||
usermod -a -G amavis clamav
|
||||
if ! usermod -a -G amavis clamav; then
|
||||
fail "usermod -a -G amavis clamav"
|
||||
fi
|
||||
|
||||
# postfix needs access to the opendkim socket
|
||||
usermod -a -G opendkim postfix
|
||||
if ! usermod -a -G opendkim postfix; then
|
||||
fail "usermod -a -G opendkim postfix"
|
||||
fi
|
||||
|
||||
# clamd couldn't access amavis/tmp
|
||||
chmod g+rx /var/lib/amavis/tmp
|
||||
if ! chmod g+rx /var/lib/amavis/tmp; then
|
||||
fail "chmod g+rx /var/lib/amavis/tmp"
|
||||
fi
|
||||
|
||||
# directory is not being created by /etc/init.d/opendkim
|
||||
for dir in opendkim ilter-greylist; do
|
||||
if [[ ! -d "/var/spool/${dir}" ]]; then
|
||||
mkdir -p "/var/spool/postfix/${dir}"
|
||||
if ! mkdir -p "/var/spool/postfix/${dir}"; then
|
||||
fail "mkdir -p /var/spool/postfix/${dir}"
|
||||
fi
|
||||
fi
|
||||
done
|
||||
chown opendkim:opendkim /var/spool/postfix/opendkim
|
||||
if ! chown opendkim:opendkim /var/spool/postfix/opendkim; then
|
||||
fail "chown opendkim:opendkim /var/spool/postfix/opendkim"
|
||||
fi
|
||||
|
||||
# opendkim needs to read its private data
|
||||
chown -R opendkim:root /etc/opendkim-private
|
||||
if ! chown -R opendkim:root /etc/opendkim-private; then
|
||||
fail "chown -R opendkim:root /etc/opendkim-private"
|
||||
fi
|
||||
|
||||
if ! chown root:root /var/log; then
|
||||
fail "chown root:root /var/log"
|
||||
fi
|
||||
|
||||
chown root:root /var/log
|
||||
for log in syslog "mail.*" "dovecot*.log" auth.log; do
|
||||
touch /var/log/${log}
|
||||
chmod a+rwX /var/log/${log}
|
||||
if ! touch "/var/log/${log}"; then
|
||||
fail "touch /var/log/${log}"
|
||||
fi
|
||||
if ! chmod a+rwX "/var/log/${log}"; then
|
||||
fail "chmod a+rwX /var/log/${log}"
|
||||
fi
|
||||
done
|
||||
|
||||
if [[ -e /run/rsyslogd.pid ]]; then
|
||||
rm /run/rsyslogd.pid
|
||||
if ! rm /run/rsyslogd.pid; then
|
||||
fail "rm /run/rsyslogd.pid"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ -e /var/run/dovecot/master.pid ]]; then
|
||||
rm /var/run/dovecot/master.pid
|
||||
if ! rm /var/run/dovecot/master.pid; then
|
||||
fail "rm /var/run/dovecot/master.pid"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Set opendkim.sock ownership and permissions
|
||||
|
||||
find /var/log -name 'dovecot*' | while read -r file; do
|
||||
chown dovecot:postfix "${file}"
|
||||
chmod g+rwX "${file}"
|
||||
if ! chown dovecot:postfix "${file}"; then
|
||||
fail "chown dovecot:postfix ${file}"
|
||||
fi
|
||||
if ! chmod g+rwX "${file}"; then
|
||||
fail "chmod g+rwX ${file}"
|
||||
fi
|
||||
done
|
||||
|
||||
if false; then
|
||||
@ -56,9 +88,21 @@ if false; then
|
||||
sed -i -E 's,(passwd|group|shadow|gshadow):.*files$,\1: files [NOTFOUND=return] system\1: files,g' /etc/nsswitch.conf
|
||||
else
|
||||
# use ldap
|
||||
sed -i -E 's#^base dc=example.*#base dc=ketrenos,dc=net#g' /etc/ldap.conf
|
||||
sed -i -E 's#^uri ldap.*#uri ldap://192.168.1.78/#g' /etc/ldap.conf
|
||||
sed -i -E 's#(passwd|group|shadow|gshadow):.*files$#\1: files ldap#g' /etc/nsswitch.conf
|
||||
if ! sed -i -E 's#^base dc=example.*#base dc=ketrenos,dc=net#g' /etc/ldap.conf; then
|
||||
fail "sed 1"
|
||||
fi
|
||||
|
||||
if ! sed -i -E 's#^uri ldap.*#uri ldap://192.168.1.78/#g' /etc/ldap.conf; then
|
||||
fail "sed 2"
|
||||
fi
|
||||
|
||||
if ! sed -i -E 's#(passwd|group|shadow|gshadow):.*files$#\1: files ldap#g' /etc/nsswitch.conf; then
|
||||
fail "sed 3"
|
||||
fi
|
||||
fi
|
||||
|
||||
if ! postmap /opt/mailman/postfix_lmtp; then
|
||||
fail "postmap /opt/mailman/postfix_lmtp"
|
||||
fi
|
||||
|
||||
while true; do
|
||||
@ -84,6 +128,9 @@ done &
|
||||
# greylist.conf is installed into /etc/milter-greylist, however
|
||||
# /etc/init.d/milter-greylist uses the default, which looks in
|
||||
# /etc/mail/greylist.conf
|
||||
if [[ -e /etc/mail/greylist.conf ]]; then
|
||||
rm /etc/mail/greylist.conf
|
||||
fi
|
||||
ln -s ../milter-greylist/greylist.conf /etc/mail/greylist.conf
|
||||
while true; do
|
||||
/usr/sbin/milter-greylist -D -P /var/run/greylist.pid -u postfix -p /var/spool/postfix/milter-greylist/milter-greylist.sock
|
||||
@ -135,7 +182,14 @@ done &
|
||||
#
|
||||
# Watch for letsencrypt changes and if they occur, restart nginx and apache2
|
||||
#
|
||||
while inotifywait -e modify /etc/letsencrypt/archive; do
|
||||
/etc/init.d/dovecot restart
|
||||
/etc/init.d/postfix restart
|
||||
while true; do
|
||||
inotifywait -e modify /etc/letsencrypt/archive /opt/mailman/postfix_lmtp | while read -r file status; do
|
||||
if [[ "${file}" == "/opt/mailman/postfix_lmtp" ]]; then
|
||||
postmap /opt/mailman/postfix_lmtp
|
||||
/etc/init.d/postfix reload
|
||||
else
|
||||
/etc/init.d/dovecot restart
|
||||
/etc/init.d/postfix restart
|
||||
fi
|
||||
done
|
||||
done
|
||||
|
@ -49,7 +49,7 @@ auth_debug_passwords = no
|
||||
|
||||
# Enable mail process debugging. This can help you figure out why Dovecot
|
||||
# isn't finding your mails.
|
||||
mail_debug = yes
|
||||
mail_debug = no
|
||||
|
||||
# Show protocol level SSL errors.
|
||||
verbose_ssl = no
|
||||
|
1
mail/etc/postfix/canonical
Normal file
1
mail/etc/postfix/canonical
Normal file
@ -0,0 +1 @@
|
||||
/^([^@]+)\+(.*)@ketrenos\.com$/ ${1}_${2}@ketrenos.com
|
@ -1,53 +1,45 @@
|
||||
# See /usr/share/postfix/main.cf.dist for a commented, more complete version
|
||||
|
||||
# Debian specific: Specifying a file name will cause the first
|
||||
# line of that file to be used as the name. The Debian default
|
||||
# Debian specific: Specifying a file name will cause the first
|
||||
# line of that file to be used as the name. The Debian default
|
||||
# is /etc/mailname.
|
||||
#myorigin = /etc/mailname
|
||||
|
||||
# The hostname of the mail server
|
||||
myhostname = ketrenos.com
|
||||
# Alternative hostname examples
|
||||
#myhostname = static-50-126-85-202.cor02.bvtn.or.ptr.ziplyfiber.com
|
||||
#myhostname = mail.ketrenos.com
|
||||
|
||||
# Logging
|
||||
# Log file location
|
||||
maillog_file = /var/log/postfix.log
|
||||
|
||||
# SMTPD banner (what clients see when they connect)
|
||||
smtpd_banner = $myhostname ESMTP $mail_name
|
||||
|
||||
# Enable unverified_sender_verify
|
||||
# 2022-05-26
|
||||
# Configuration for unverified senders
|
||||
unverified_sender_defer_code = 250
|
||||
|
||||
# Disable the biff service (notify users of new mail)
|
||||
biff = no
|
||||
# 2020-09-16 set max size to 50M as more and more
|
||||
# messages are exceeding 32M
|
||||
#
|
||||
# 2021-05-16 set max size to 200M (2^28) as we are seeing Infuccient
|
||||
# system storage bounces.
|
||||
message_size_limit = 0
|
||||
|
||||
# appending .domain is the MUA's job.
|
||||
# Set the maximum message size to 200MB (in bytes)
|
||||
message_size_limit = 209715200
|
||||
|
||||
# Don't append the domain to usernames automatically
|
||||
append_dot_mydomain = no
|
||||
|
||||
# Uncomment the next line to generate "delayed mail" warnings
|
||||
# Uncomment to generate "delayed mail" warnings
|
||||
#delay_warning_time = 4h
|
||||
|
||||
# Disable the README directory
|
||||
readme_directory = no
|
||||
|
||||
#debug_peer_list = 127.0.0.1
|
||||
|
||||
# NOTE:
|
||||
# `smtpd` prefix is for receiving connections. `smtp` is for outbound
|
||||
# connections
|
||||
|
||||
# TLS parameters
|
||||
#smtpd_use_tls = yes
|
||||
# TLS parameters for inbound connections
|
||||
smtpd_use_tls = yes
|
||||
smtpd_tls_auth_only = no
|
||||
smtpd_tls_cert_file = /etc/letsencrypt/live/mail.ketrenos.com/fullchain.pem
|
||||
smtpd_tls_key_file = /etc/letsencrypt/live/mail.ketrenos.com/privkey.pem
|
||||
|
||||
# Trying to fix insufficient space
|
||||
smtpd_proxy_timeout = 240s
|
||||
|
||||
smtpd_tls_received_header = yes
|
||||
smtpd_tls_ask_ccert = yes
|
||||
smtpd_tls_session_cache_timeout = 3600s
|
||||
@ -55,45 +47,69 @@ smtpd_tls_loglevel = 1
|
||||
smtp_tls_loglevel = 1
|
||||
smtp_tls_note_starttls_offer = yes
|
||||
|
||||
#Disable Poodle
|
||||
# Disable old and insecure SSL/TLS protocols
|
||||
smtp_tls_security_level = may
|
||||
smtp_tls_mandatory_protocols=!SSLv2,!SSLv3
|
||||
smtp_tls_protocols=!SSLv2,!SSLv3
|
||||
smtp_tls_mandatory_protocols = !SSLv2, !SSLv3
|
||||
smtp_tls_protocols = !SSLv2, !SSLv3
|
||||
smtpd_tls_security_level = may
|
||||
smtpd_tls_mandatory_protocols=!SSLv2,!SSLv3
|
||||
smtpd_tls_protocols=!SSLv2,!SSLv3
|
||||
smtpd_tls_mandatory_protocols = !SSLv2, !SSLv3
|
||||
smtpd_tls_protocols = !SSLv2, !SSLv3
|
||||
|
||||
# Force TLS for outgoing server connection
|
||||
# Force TLS for outgoing server connections
|
||||
smtp_tls_policy_maps = hash:/etc/postfix/tls_policy
|
||||
smtp_tls_CApath = /etc/ssl/certs/
|
||||
smtpd_tls_CApath = /etc/ssl/certs/
|
||||
smtp_tls_CAfile = /etc/ssl/certs/ca-certificates.crt
|
||||
smtpd_tls_CAfile = /etc/ssl/certs/ca-certificates.crt
|
||||
|
||||
# Changes to SSL Ciphers
|
||||
# Configure SSL ciphers
|
||||
tls_preempt_cipherlist = yes
|
||||
smtpd_tls_mandatory_ciphers = high
|
||||
|
||||
# If this is used, facebook stops being able to deliver to us :(
|
||||
#tls_high_cipherlist = ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:DHE-DSS-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA256:ADH-AES256-GCM-SHA384:ADH-AES256-SHA256:ECDH-RSA-AES256-GCM-SHA384:ECDH-ECDSA-AES256-GCM-SHA384:ECDH-RSA-AES256-SHA384:ECDH-ECDSA-AES256-SHA384:AES256-GCM-SHA384:AES256-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:DHE-DSS-AES128-GCM-SHA256:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES128-SHA256:DHE-DSS-AES128-SHA256:ADH-AES128-GCM-SHA256:ADH-AES128-SHA256:ECDH-RSA-AES128-GCM-SHA256:ECDH-ECDSA-AES128-GCM-SHA256:ECDH-RSA-AES128-SHA256:ECDH-ECDSA-AES128-SHA256:AES128-GCM-SHA256:AES128-SHA256:NULL-SHA256
|
||||
|
||||
# SMTP session cache settings
|
||||
#smtpd_tls_session_cache_database = btree:${data_directory}/smtpd_scache
|
||||
#smtp_tls_session_cache_database = btree:${data_directory}/smtp_scache
|
||||
|
||||
# See /usr/share/doc/postfix/TLS_README.gz in the postfix-doc package for
|
||||
# information on enabling SSL in the smtp client.
|
||||
|
||||
# Alias maps and database
|
||||
alias_maps = hash:/etc/aliases
|
||||
alias_database = hash:/etc/aliases
|
||||
|
||||
myorigin = /etc/mailname
|
||||
mydestination = ketrenos.com, kiaoramassage.com, sketchitect.com, localhost, email.ketrenos.net, ketrenos.net
|
||||
relayhost =
|
||||
mailbox_size_limit = 0
|
||||
# Support multiple recipient delimiters (_ and +)
|
||||
canonical_maps = regexp:/etc/postfix/canonical
|
||||
recipient_delimiter = _
|
||||
# Example /etc/postfix/canonical:
|
||||
# /^([^@]+)\+(.*)@ketrenos\.com$/ ${1}_${2}@ketrenos.com
|
||||
# sudo postmap /etc/postfix/canonical
|
||||
# sudo systemctl restart postfix
|
||||
|
||||
# Mailman3 support (via ketrenet-mailman-core)
|
||||
unknown_local_recipient_reject_code = 550
|
||||
owner_request_special = no
|
||||
transport_maps = hash:/opt/mailman/postfix_lmtp
|
||||
local_recipient_maps = hash:/opt/mailman/postfix_lmtp
|
||||
# relay_domains is set for more than just mailman ketrenos.com
|
||||
# relay_domains = hash:/opt/mailman/postfix_domains
|
||||
|
||||
# Origin domain for outgoing mail
|
||||
myorigin = /etc/mailname
|
||||
|
||||
# Define destinations for which this system is responsible
|
||||
mydestination = ketrenos.com, kiaoramassage.com, sketchitect.com, localhost, email.ketrenos.net, ketrenos.net
|
||||
|
||||
# No relay host (direct delivery)
|
||||
relayhost =
|
||||
|
||||
# No mailbox size limit
|
||||
mailbox_size_limit = 0
|
||||
|
||||
# Network interfaces and protocols
|
||||
inet_interfaces = all
|
||||
inet_protocols = ipv4
|
||||
|
||||
# Mailbox format
|
||||
home_mailbox = Maildir/
|
||||
|
||||
# SASL authentication settings
|
||||
smtpd_sasl_auth_enable = yes
|
||||
smtpd_sasl_type = dovecot
|
||||
smtpd_sasl_path = private/auth_client
|
||||
@ -106,50 +122,86 @@ smtpd_sasl_local_domain =
|
||||
smtpd_helo_required = yes
|
||||
broken_sasl_auth_clients = yes
|
||||
|
||||
mynetworks = 127.0.0.0/8 192.168.0.0/16
|
||||
# Network configuration
|
||||
mynetworks = 127.0.0.0/8, 192.168.0.0/16
|
||||
|
||||
# Mailbox command (for Dovecot delivery)
|
||||
mailbox_command = /usr/lib/dovecot/deliver -c /etc/dovecot/dovecot.conf -m "${EXTENSION}" -a "${RECIPIENT}"
|
||||
|
||||
# Random number source for TLS
|
||||
tls_random_source = dev:/dev/urandom
|
||||
|
||||
# Content filter (Amavis)
|
||||
content_filter = smtp-amavis:[127.0.0.1]:10024
|
||||
|
||||
# Relay domains (repeat for clarity, should match previous definition)
|
||||
relay_domains = ketrenos.com, email.ketrenos.net, webserver.ketrenos.net
|
||||
#transport_maps = hash:/etc/postfix/transport
|
||||
|
||||
# Mailman destination recipient limit
|
||||
mailman_destination_recipient_limit = 1
|
||||
|
||||
# smtpd_client_restrictions =
|
||||
# SMTPD client restrictions
|
||||
#smtpd_client_restrictions =
|
||||
# permit_mynetworks
|
||||
# reject_plaintext_session
|
||||
|
||||
# SMTPD recipient restrictions
|
||||
smtpd_recipient_restrictions =
|
||||
permit_mynetworks
|
||||
check_recipient_access hash:/etc/postfix/recipient_restrictions
|
||||
permit_sasl_authenticated
|
||||
reject_unauth_destination
|
||||
reject_rbl_client
|
||||
zen.spamhaus.org=127.0.0.[2..11],
|
||||
check_sender_access hash:/etc/postfix/sender_checks
|
||||
check_policy_service unix:private/policy-spf
|
||||
reject_unknown_sender_domain
|
||||
warn_if_reject reject_unverified_sender
|
||||
permit_mynetworks,
|
||||
check_recipient_access hash:/etc/postfix/recipient_restrictions,
|
||||
permit_sasl_authenticated,
|
||||
reject_unauth_destination,
|
||||
reject_rbl_client zen.spamhaus.org=127.0.0.[2..11],
|
||||
check_sender_access hash:/etc/postfix/sender_checks,
|
||||
check_policy_service unix:private/policy-spf,
|
||||
reject_unknown_sender_domain,
|
||||
warn_if_reject reject_unverified_sender
|
||||
|
||||
# SMTPD sender restrictions
|
||||
smtpd_sender_restrictions =
|
||||
permit_mynetworks
|
||||
permit_mynetworks,
|
||||
reject_unknown_sender_domain
|
||||
|
||||
# Added check_sender_access to block all spoofed root@ketrenos.com email sends
|
||||
# SMTPD relay restrictions (to block spoofed root@ketrenos.com)
|
||||
smtpd_relay_restrictions =
|
||||
permit_mynetworks
|
||||
check_sender_access hash:/etc/postfix/sender_restrictions
|
||||
check_recipient_access hash:/etc/postfix/recipient_restrictions
|
||||
permit_sasl_authenticated
|
||||
permit_mynetworks,
|
||||
check_sender_access hash:/etc/postfix/sender_restrictions,
|
||||
check_recipient_access hash:/etc/postfix/recipient_restrictions,
|
||||
permit_sasl_authenticated,
|
||||
reject_unauth_destination
|
||||
milter_connect_macros = i b j _ {daemon_name} {if_name} {client_addr}
|
||||
# https://www.digitalocean.com/community/tutorials/how-to-install-and-configure-dkim-with-postfix-on-debian-wheezy
|
||||
|
||||
# Milter settings (greylisting and DKIM)
|
||||
# This macro definition helps the milters (greylisting and DKIM) by providing
|
||||
# specific connection details that can be used to make filtering decisions.
|
||||
# The provided macros include:
|
||||
# - i: Queue ID
|
||||
# - b: Blog ID
|
||||
# - j: The message's destination hostname
|
||||
# - _: The client address in numeric form
|
||||
# - {daemon_name}: The name of the daemon
|
||||
# - {if_name}: The name of the network interface
|
||||
# - {client_addr}: The client's IP address
|
||||
# This detailed information helps improve the accuracy and effectiveness of the milters.
|
||||
milter_connect_macros = i, b, j, _, {daemon_name}, {if_name}, {client_addr}
|
||||
|
||||
# Specify the milter protocol version
|
||||
milter_protocol = 2
|
||||
|
||||
# Set the default action if a milter fails (accept the mail)
|
||||
milter_default_action = accept
|
||||
|
||||
# Specify the paths to the milter sockets
|
||||
smtpd_milters = unix:milter-greylist/milter-greylist.sock, local:opendkim/opendkim.sock
|
||||
#smtpd_milters = unix:/milter-greylist/milter-greylist.sock
|
||||
non_smtpd_milters = $smtpd_milters milter_default_action = accept
|
||||
|
||||
# Apply the same milters to non-SMTPD traffic
|
||||
non_smtpd_milters = $smtpd_milters
|
||||
|
||||
# TLS usage settings
|
||||
smtpd_use_tls = yes
|
||||
smtp_use_tls = no
|
||||
|
||||
# SPF policy time limit
|
||||
policy-spf_time_limit = 3600s
|
||||
|
||||
# Compatibility level
|
||||
compatibility_level = 3.6
|
||||
|
@ -19,7 +19,7 @@
|
||||
# -o smtpd_data_restrictions=
|
||||
# -o smtpd_end_of_data_restrictions=
|
||||
submission inet n - y - - smtpd
|
||||
smtps inet n - y - - smtpd
|
||||
#smtps inet n - y - - smtpd
|
||||
# -o smtpd_tls_wrappermode=yes
|
||||
# -o smtpd_sasl_auth_enable=yes
|
||||
# -o smtpd_client_restrictions=permit_sasl_authenticated,reject
|
||||
|
81
mailman/.circleci/config.yml
Normal file
81
mailman/.circleci/config.yml
Normal file
@ -0,0 +1,81 @@
|
||||
version: '2.1'
|
||||
|
||||
jobs:
|
||||
build:
|
||||
parameters:
|
||||
rolling:
|
||||
type: string
|
||||
default: "no"
|
||||
machine:
|
||||
image: ubuntu-2004:202010-01
|
||||
environment:
|
||||
DOCKER_BUILDKIT: 1
|
||||
BUILDKIT_PROGRESS: plain
|
||||
BUILD_ROLLING: << parameters.rolling >>
|
||||
steps:
|
||||
- checkout
|
||||
- run:
|
||||
name: Install Python dependencies
|
||||
command: python3 -m pip install packaging
|
||||
- restore_cache:
|
||||
keys:
|
||||
- python-deps-cache-v1
|
||||
- run:
|
||||
name: Building Container Images
|
||||
command: ./build.sh << parameters.rolling >>
|
||||
- save_cache:
|
||||
key: python-deps-cache-v1
|
||||
paths:
|
||||
- /root/.cache
|
||||
- run:
|
||||
environment:
|
||||
DB: postgres
|
||||
name: Postgres Tests
|
||||
command: bash tests/test.sh
|
||||
- run:
|
||||
environment:
|
||||
DB: mysql
|
||||
name: MySQL Test
|
||||
command: bash tests/test.sh
|
||||
- deploy:
|
||||
name: Deploy
|
||||
command: |
|
||||
python3 --version
|
||||
python3 deploy.py
|
||||
- store_artifacts:
|
||||
path: /opt/mailman/web/logs/
|
||||
|
||||
- store_artifacts:
|
||||
path: /opt/mailman/core/var/logs
|
||||
|
||||
workflows:
|
||||
version: 2
|
||||
test-stable:
|
||||
jobs:
|
||||
- build:
|
||||
rolling: "no"
|
||||
filters:
|
||||
tags:
|
||||
only: /^v\d+\.\d+\.\d+$/
|
||||
|
||||
cron-builds:
|
||||
triggers:
|
||||
- schedule:
|
||||
cron: "0 0 * * *"
|
||||
filters:
|
||||
branches:
|
||||
only: main
|
||||
jobs:
|
||||
- build:
|
||||
rolling: "yes"
|
||||
context: org-global
|
||||
|
||||
test-rolling:
|
||||
jobs:
|
||||
- build:
|
||||
rolling: "yes"
|
||||
context: org-global
|
||||
filters:
|
||||
branches:
|
||||
# Forked pull requests have CIRCLE_BRANCH set to pull/XXX
|
||||
ignore: /pull\/[0-9]+/
|
11
mailman/.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
11
mailman/.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
@ -0,0 +1,11 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Create a report to help us improve
|
||||
title: "[BUG]"
|
||||
labels: bug
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Tag/version of Container Images**
|
||||
Choose from: rolling, 0.3, 0.3.*, 0.4.*
|
34
mailman/.github/dependabot.yml
vendored
Normal file
34
mailman/.github/dependabot.yml
vendored
Normal file
@ -0,0 +1,34 @@
|
||||
# To get started with Dependabot version updates, you'll need to specify which
|
||||
# package ecosystems to update and where the package manifests are located.
|
||||
# Please see the documentation for all configuration options:
|
||||
# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
|
||||
|
||||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: "docker" # See documentation for possible values
|
||||
directory: "/core" # Location of package manifests
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
- package-ecosystem: "docker" # See documentation for possible values
|
||||
directory: "/web" # Location of package manifests
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
- package-ecosystem: "docker" # See documentation for possible values
|
||||
directory: "/postorius" # Location of package manifests
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
# Enable version updates for Actions
|
||||
- package-ecosystem: "github-actions"
|
||||
# Look for `.github/workflows` in the `root` directory
|
||||
directory: "/"
|
||||
# Check for updates once a week
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
- package-ecosystem: "pip"
|
||||
directory: "/core"
|
||||
schedule:
|
||||
interval: "daily"
|
||||
- package-ecosystem: "pip"
|
||||
directory: "/web"
|
||||
schedule:
|
||||
interval: "daily"
|
9
mailman/.github/workflows/main.workflow
vendored
Normal file
9
mailman/.github/workflows/main.workflow
vendored
Normal file
@ -0,0 +1,9 @@
|
||||
workflow "Add PR to release notes" {
|
||||
on = "pull_request"
|
||||
resolves = ["Chronicler"]
|
||||
}
|
||||
|
||||
action "Chronicler" {
|
||||
uses = "crosscompile/chronicler-action@v1.0.0"
|
||||
secrets = ["GITHUB_TOKEN"]
|
||||
}
|
28
mailman/.github/workflows/main.yml
vendored
Normal file
28
mailman/.github/workflows/main.yml
vendored
Normal file
@ -0,0 +1,28 @@
|
||||
# This is a basic workflow to help you get started with Actions
|
||||
|
||||
name: CI
|
||||
|
||||
# Controls when the action will run.
|
||||
on:
|
||||
# Triggers the workflow on push or pull request events but only for the master branch
|
||||
push:
|
||||
branches: [ master ]
|
||||
pull_request:
|
||||
branches: [ master ]
|
||||
|
||||
# Allows you to run this workflow manually from the Actions tab
|
||||
workflow_dispatch:
|
||||
|
||||
# A workflow run is made up of one or more jobs that can run sequentially or in parallel
|
||||
jobs:
|
||||
# This workflow contains a single job called "build"
|
||||
build:
|
||||
# The type of runner that the job will run on
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
# Steps represent a sequence of tasks that will be executed as part of the job
|
||||
steps:
|
||||
- name: Chronicler Action
|
||||
# You may pin to the exact commit or the version.
|
||||
# uses: crosscompile/chronicler-action@5c25dbce26b0789724a92902c69217b46f023b51
|
||||
uses: crosscompile/chronicler-action@v1.0.1
|
24
mailman/.github/workflows/publish_docs.yml
vendored
Normal file
24
mailman/.github/workflows/publish_docs.yml
vendored
Normal file
@ -0,0 +1,24 @@
|
||||
|
||||
|
||||
name: Publish docs via GitHub Pages
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- master
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Deploy docs
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout main
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Deploy docs
|
||||
uses: mhausenblas/mkdocs-deploy-gh-pages@master
|
||||
# Or use mhausenblas/mkdocs-deploy-gh-pages@nomaterial to build without the mkdocs-material theme
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
CUSTOM_DOMAIN: asynchronous.in
|
||||
CONFIG_FILE: mkdocs.yml
|
28
mailman/.github/workflows/stale.yml
vendored
Normal file
28
mailman/.github/workflows/stale.yml
vendored
Normal file
@ -0,0 +1,28 @@
|
||||
# This workflow warns and then closes issues and PRs that have had no activity for a specified amount of time.
|
||||
#
|
||||
# You can adjust the behavior by modifying this file.
|
||||
# For more information, see:
|
||||
# https://github.com/actions/stale
|
||||
name: Mark stale issues and pull requests
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: '19 22 * * *'
|
||||
|
||||
jobs:
|
||||
stale:
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
issues: write
|
||||
pull-requests: write
|
||||
|
||||
steps:
|
||||
- uses: actions/stale@v9
|
||||
with:
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
stale-issue-message: 'This issue has not been updated for more than 1year'
|
||||
stale-pr-message: 'This Pull Request has not been updated for more than 1year'
|
||||
stale-issue-label: 'no-issue-activity'
|
||||
stale-pr-label: 'no-pr-activity'
|
||||
days-before-stale: 365
|
7
mailman/.gitignore
vendored
Normal file
7
mailman/.gitignore
vendored
Normal file
@ -0,0 +1,7 @@
|
||||
*/.sass-cache/*
|
||||
*.log
|
||||
*.log
|
||||
*.sass-cache/
|
||||
/web/mailman-web/settings_local.py
|
||||
pythonenv3.8/*
|
||||
.venv/*
|
21
mailman/LICENSE
Normal file
21
mailman/LICENSE
Normal file
@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2017 Abhilash Raj
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
132
mailman/NEWS.md
Normal file
132
mailman/NEWS.md
Normal file
@ -0,0 +1,132 @@
|
||||
# NEWS
|
||||
|
||||
## Upgrading to 0.4.0 Release
|
||||
|
||||
Some configurations in the release are backwards incompatible with
|
||||
what was working before.
|
||||
|
||||
### Web server configuration
|
||||
|
||||
With 0.4.0 version, we added Port mapping from host's Port 8000/8080
|
||||
to mailman-web container's port 8000/8080. Make sure you have this
|
||||
in your docker-compose.yaml
|
||||
|
||||
```yaml
|
||||
mailman-web:
|
||||
ports:
|
||||
- "127.0.0.1:8000:8000" # HTTP
|
||||
- "127.0.0.1:8080:8080" # uwsgi
|
||||
```
|
||||
|
||||
You should update your web server to proxy 127.0.0.1:8000.
|
||||
|
||||
#### Nginx
|
||||
|
||||
Update the Nginx configuration to look like this, notice the
|
||||
actual **URL for `proxy_pass` is the only thing that has changed**
|
||||
along with some options like `uwsgi_read_timeout` and `include uwsgi_params`
|
||||
that have been removed from the previous version.
|
||||
|
||||
```
|
||||
location / {
|
||||
proxy_pass http://127.0.0.1:8000;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Forwarded-For $remote_addr;
|
||||
}
|
||||
```
|
||||
|
||||
For other web servers like Apache2, update the URL accordingly.
|
||||
|
||||
**Note** that if you are using `uwsgi_pass` instead of `proxy_pass`
|
||||
then you should update the URL accordingly to `https://127.0.0.1:8080`.
|
||||
|
||||
### MTA configuration
|
||||
|
||||
MTA configuration needs updating to ensure that all IPs from the
|
||||
`172.19.199.0/24` subnet is added to `mynetworks` in Postfix configs.
|
||||
|
||||
Please verify that the network configuration generated by the containers
|
||||
look like this:
|
||||
|
||||
```bash
|
||||
$ docker exec mailman-core cat /etc/mailman.cfg
|
||||
# This file is autogenerated at container startup.
|
||||
[database]
|
||||
class: mailman.database.postgresql.PostgreSQLDatabase
|
||||
url: postgres://mailman:mailmanpass@database/mailmandb
|
||||
[runner.retry]
|
||||
sleep_time: 10s
|
||||
|
||||
[webservice]
|
||||
hostname: 172.19.199.3
|
||||
port: 8001
|
||||
admin_user: restadmin
|
||||
admin_pass: restpass
|
||||
configuration: /etc/gunicorn.cfg
|
||||
|
||||
[mta]
|
||||
incoming: mailman.mta.postfix.LMTP
|
||||
outgoing: mailman.mta.deliver.deliver
|
||||
lmtp_host: 172.19.199.3
|
||||
lmtp_port: 8024
|
||||
smtp_host: 172.19.199.1
|
||||
smtp_port: 25
|
||||
configuration: /etc/postfix-mailman.cfg
|
||||
|
||||
[archiver.hyperkitty]
|
||||
class: mailman_hyperkitty.Archiver
|
||||
enable: yes
|
||||
configuration: /etc/mailman-hyperkitty.cfg
|
||||
```
|
||||
|
||||
**Note that lmtp_host and webserver hostname can be different than
|
||||
before since new containers don't have static IP addresses. They
|
||||
are automatically parsed from the output of "ip route" command
|
||||
from inside mailman-core container.**
|
||||
|
||||
You can verify that the IP address of the containers by running the
|
||||
following commands, note that the **output can be different** and it is
|
||||
fine if that is the case.
|
||||
|
||||
```bash
|
||||
$ docker inspect -f '{{range.NetworkSettings.Networks}}{{.IPAddress}}{{end}}' mailman-core
|
||||
172.19.199.3
|
||||
$ docker inspect -f '{{range.NetworkSettings.Networks}}{{.IPAddress}}{{end}}' mailman-web
|
||||
172.19.199.4
|
||||
```
|
||||
|
||||
----
|
||||
## Mailman Core
|
||||
|
||||
### v1.1.1 (released Aug 9 2017)
|
||||
|
||||
- The MM_HOSTNAME now defaults to output of `hostname -i` instead of `mailman-core`. This
|
||||
is the hostname Core binds to for Webservice.
|
||||
- Added pymysql to the image to use MySQL as database.
|
||||
- The default settings for using SQLITE are now more sane.
|
||||
- Postfix's transport maps are generated at the container startup now even when
|
||||
there is no lists exist.
|
||||
|
||||
|
||||
## Mailman Web
|
||||
|
||||
### v1.1.1 (released Aug 9 2017)
|
||||
|
||||
- The default search_index for whoosh now exists on persistent storage at
|
||||
`/opt/mailman-web-data`
|
||||
- Move to using Alpine instead of Debian for this image, python2.7:alpine-3.6
|
||||
image is now the base image
|
||||
- Django compressor is now using `sassc` from alpine repo.
|
||||
- Default value of SECRET_KEY is now removed. It is MUST to set SECRET_KEY
|
||||
environment variable to run this image now.
|
||||
- If a SERVE_FROM_DOMAIN environment variable is defined, the default Django's
|
||||
example.com site is renamed to this domain. The SITE_ID remains same so there
|
||||
is no change required to serve this domain.
|
||||
- If MAILMAN_ADMIN_USER and MAILMAN_ADMIN_EMAIL environment variables are
|
||||
defined a Django Superuser is created by default. The password for this user
|
||||
would have to be reset on the first login.
|
||||
- Fix cron configuration which would run them in wrong order.
|
||||
- Removed facebook as default social auth provider in the settings.py
|
||||
- Uwsgi now listens on port 8080 for uwsgi protocol and 8000 for http protocol.
|
||||
- Threads are enabled by default in the uwsgi configuration now.
|
||||
- Hyperkitty updated to v1.1.1
|
585
mailman/README.md
Normal file
585
mailman/README.md
Normal file
@ -0,0 +1,585 @@
|
||||
---
|
||||
permalink: /
|
||||
---
|
||||
|
||||
|
||||
# GNU Mailman 3 Deployment with Docker
|
||||
|
||||
|
||||
|
||||
[](https://circleci.com/gh/maxking/docker-mailman/tree/main)
|
||||
|
||||
This repository hosts code for two docker images `maxking/mailman-core` and
|
||||
`maxking/mailman-web` both of which are meant to deploy [GNU Mailman 3][1] in
|
||||
a production environment.
|
||||
|
||||
[Docker][2] is a container ecosystem which can run containers on several
|
||||
platforms. It consists of a tool called [docker-compose][3] which can be used to
|
||||
run multi-container applications. This repository consists of a
|
||||
[`docker-compose.yaml`][19] file which is a set of
|
||||
configurations that can be used to deploy the [Mailman 3 Suite][4].
|
||||
|
||||
Please see [release page](https://github.com/maxking/docker-mailman/releases)
|
||||
for the releases and change log.
|
||||
|
||||
## Release
|
||||
|
||||
|
||||
The tags for the images are assumed to be release versions for images. This is
|
||||
going to be a somewhat common philosophy of distributing Container images where
|
||||
the images with same tags are usually updated with the new functionality.
|
||||
|
||||
Releases will follow the following rules:
|
||||
|
||||
* Images tagged like A.B.C will never change. If you want to pin down versions
|
||||
of Images, use these tags.
|
||||
|
||||
* Images tagged with A.B will correspond to the latest A.B.C version
|
||||
released. Releases in A.B series are supposed to be backwards compatible,
|
||||
i.e., any existing installation should not break when upgrading between
|
||||
subversions of A.B.C. So, if you want the latest updates and want to
|
||||
frequently update your installation without having to change the version
|
||||
numbers, you can use this.
|
||||
|
||||
* Any changes in the minor version of Mailman components of the images will
|
||||
cause a bump in the minor version, e.g., A.(B+1) can have one or more
|
||||
updated Mailman components from A.B. Also, significant change in functionality,
|
||||
that might change how Images work or how people interact with the containers
|
||||
can also cause a bump in the minor version.
|
||||
|
||||
* Major versions will change either when there are backwards incompatible
|
||||
changes or when the releases reach a certain set milestone or when there are
|
||||
bugfix releases for the internal components or both.
|
||||
|
||||
|
||||
## Container Registries
|
||||
|
||||
The container images are available from multiple container registries. Do specify an [explicit version tag](https://hub.docker.com/r/maxking/mailman-web/tags?page=1&ordering=last_updated&name=0.) (e.g. `0.4.5` , MAJOR.MINOR like `0.4` also works as floating tag pointing to latest patch version) as tag `latest` is **not** updated anymore.
|
||||
|
||||
### Mailman Core
|
||||
|
||||
- `ghcr.io/maxking/mailman-core`
|
||||
- `docker.io/maxking/mailman-core`
|
||||
|
||||
### Mailman Web
|
||||
|
||||
- `ghcr.io/maxking/mailman-web`
|
||||
- `docker.io/maxking/mailman-web`
|
||||
|
||||
### Postorius
|
||||
|
||||
- `ghcr.io/maxking/postorius`
|
||||
- `docker.io/maxking/postorius`
|
||||
|
||||
## Rolling Releases
|
||||
|
||||
Rolling releases are made up of Mailman Components installed from [git
|
||||
source](https://gitlab.com/mailman). **Note that these releases are made up of
|
||||
un-released software and should be assumed to be beta quality.**
|
||||
|
||||
Every commit is tested with Mailman's CI infrastructure and is included in
|
||||
rolling releases only if they have passed the complete test suite.
|
||||
|
||||
```bash
|
||||
$ docker pull docker.io/maxking/mailman-web:rolling
|
||||
$ docker pull docker.io/maxking/mailman-core:rolling
|
||||
```
|
||||
|
||||
Rolling releases are built with every commit and also re-generated nightly. You
|
||||
can inspect the images to get which commit it was built using:
|
||||
|
||||
```bash
|
||||
$ docker inspect --format '{{json .Config.Labels }}' mailman-core | python -m json.tool
|
||||
{
|
||||
"version.git_commit": "45a4d7805b2b3d0e7c51679f59682d64ba02f05f",
|
||||
}
|
||||
|
||||
$ docker inspect --format '{{json .Config.Labels }}' mailman-web | python -m json.tool
|
||||
{
|
||||
"version.git_commit": "45a4d7805b2b3d0e7c51679f59682d64ba02f05f",
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
- `version.git_commit` : This is the commit hash of the Dockerfile in the
|
||||
[Github repo](https://github.com/maxking/docker-mailman)
|
||||
|
||||
## Dependencies
|
||||
|
||||
- Docker
|
||||
- Docker-compose
|
||||
|
||||
To install these on Ubuntu/Debian:
|
||||
|
||||
```
|
||||
$ sudo apt install docker.io docker-compose
|
||||
```
|
||||
|
||||
For other systems, you can read the official Docker documentation to install
|
||||
[Docker from here][5] and [docker-compose from here][6].
|
||||
|
||||
|
||||
## Configuration
|
||||
|
||||
Most of the common configuration is handled through environment variables in the
|
||||
`docker-compose.yaml`. However, there is need for some extra configuration that
|
||||
interacts directly with the application. There are two configuration files on
|
||||
the host that interact directly with Mailman's settings. These files exist on
|
||||
the host running the containers and are imported at runtime in the containers.
|
||||
|
||||
* `/opt/mailman/core/mailman-extra.cfg` : This is the configuration for Mailman
|
||||
Core and anything that you add here will be added to Core's configuration. You
|
||||
need to restart your mailman-core container for the changes in this file to
|
||||
take effect.
|
||||
|
||||
* `/opt/mailman/web/settings_local.py` : This is the Django configuration that
|
||||
is imported by the [existing configuration][2]
|
||||
provided by the mailman-web container. **This file is referred to as
|
||||
`settings.py` in most of the Postorius and Django documentation.** To change
|
||||
or override any settings in Django/Postorius, you need to create/edit this file.
|
||||
A useful configuration for troubleshooting is `DEBUG = True`.
|
||||
|
||||
[2]: https://github.com/maxking/docker-mailman/blob/master/web/mailman-web/settings.py
|
||||
|
||||
Also, note that if you need any other files to be accessible from the host to
|
||||
inside the container, you can place them at certain directories which are
|
||||
mounted inside the containers.
|
||||
|
||||
|
||||
* `/opt/mailman/core` in host maps to `/opt/mailman/` in mailman-core container.
|
||||
* `/opt/mailman/web` in host maps to `/opt/mailman-web-data` in mailman-web
|
||||
container.
|
||||
|
||||
### Mailman-web
|
||||
These are the settings that you MUST change in your docker-compose.yaml before deploying:
|
||||
|
||||
- `SERVE_FROM_DOMAIN`: The domain name from which Django will be served. To be
|
||||
added to `ALLOWED_HOSTS` in django settings. Default value is not set. This
|
||||
also replaces Django's default `example.com` SITE and becomes the default SITE
|
||||
(with SITE_ID=1).
|
||||
|
||||
- `HYPERKITTY_API_KEY`: Hyperkitty's API Key, should be set to the same value
|
||||
as set for the mailman-core. (Not needed in case of Postorius-only version.)
|
||||
|
||||
- `MAILMAN_ADMIN_USER`: The username for the admin user to be created by default.
|
||||
|
||||
- `MAILMAN_ADMIN_EMAIL`: The email for the admin user to be created by default.
|
||||
|
||||
- `SECRET_KEY`: Django's secret key, mainly used for signing cookies and others.
|
||||
|
||||
Please note here that if you choose to create the admin user using the
|
||||
environment variables mentioned above (`MAILMAN_ADMIN_USER` &
|
||||
`MAILMAN_ADMIN_EMAIL`), no password is set for your admin account. To set a
|
||||
password, plese follow the "Forgot Password" link on the "Sign In" page.
|
||||
|
||||
Mailman web is already configured to send emails through `$SMTP_HOST` as the
|
||||
MTA's address. If you want to modify it, you can set the value in under
|
||||
docker-compose.yaml for mailman-web container. By default, `SMTP_HOST` points
|
||||
to the gateway of the web container, which is the host itself.
|
||||
|
||||
You can also use the environment variables `SMTP_HOST` (defaults to
|
||||
the container's gateway), `SMTP_PORT` (defaults to `25`), `SMTP_HOST_USER` (defaults to
|
||||
an empty string), `SMTP_HOST_PASSWORD` (defaults to an empty string),
|
||||
`SMTP_USE_TLS` (defaults to `False`) and `SMTP_USE_SSL` (defaults to `False`).
|
||||
|
||||
This is required in addition to the [Setup your MTA](#setting-up-your-mta)
|
||||
section below, which covers email setup for Mailman Core.
|
||||
|
||||
For more details on how to configure this image, please look at
|
||||
[Mailman-web's Readme](web/)
|
||||
|
||||
### Mailman-Core
|
||||
|
||||
These are the variables that you MUST change in your docker-compose.yaml before deploying:
|
||||
|
||||
- `HYPERKITTY_API_KEY`: Hyperkitty's API Key, should be set to the same value as
|
||||
set for the mailman-web. Skip the variable in case of non-Hyperkitty deployment.
|
||||
|
||||
- `DATABASE_URL`: URL of the type
|
||||
`driver://user:password@hostname:port/databasename` for the django to use. If
|
||||
not set, the default is set to
|
||||
`sqlite:///opt/mailman-web-data/mailmanweb.db`. The standard
|
||||
docker-compose.yaml comes with it set to a postgres database. There is no need
|
||||
to change this if you are happy with PostgreSQL.
|
||||
|
||||
- `DATABASE_TYPE`: Its value can be one of `sqlite`, `postgres` or `mysql` as
|
||||
these are the only three database types that Mailman 3 supports. Its default
|
||||
value is set to `sqlite` along with the default database class and default
|
||||
database url above.
|
||||
|
||||
- `DATABASE_CLASS`: Default value is
|
||||
`mailman.database.sqlite.SQLiteDatabase`. The values for this can be found in
|
||||
the mailman's documentation [here][11].
|
||||
- `SMTP_HOST` : outgoing host for SMTP connections
|
||||
- `SMTP_PORT` : use this port. 25, 587, whatever your host asks for.
|
||||
- `SMTP_HOST_USER`: authenticate this user
|
||||
- `SMTP_HOST_PASSWORD`: and use this password
|
||||
|
||||
For more details on how to configure this image, please look [Mailman-core's
|
||||
Readme](core/)
|
||||
|
||||
|
||||
While the above configuration will allow you to run the images and possibly view
|
||||
the Web Frontend, it won't be functional until it is fully configured to to send
|
||||
emails.
|
||||
|
||||
To configure the mailman-core container to send emails, see the [Setting your MTA
|
||||
section below](#setting-up-your-mta).
|
||||
|
||||
## Running
|
||||
|
||||
To run the containers, simply run:
|
||||
|
||||
```bash
|
||||
$ mkdir -p /opt/mailman/core
|
||||
$ mkdir -p /opt/mailman/web
|
||||
$ git clone https://github.com/maxking/docker-mailman
|
||||
$ cd docker-mailman
|
||||
# Change some configuration variables as mentioned above.
|
||||
$ docker-compose up -d
|
||||
```
|
||||
|
||||
Note that the web frontend in the mailman-web container is, by default, only
|
||||
configured to serve dynamic content. Anything static like stylesheets, etc., is
|
||||
expected to be served directly by the web server. The static content exists at
|
||||
`/opt/mailman/web/static` and should be _aliased_ to `/static/` in the web
|
||||
server configuration.
|
||||
|
||||
See [the nginx configuration][17] as an example.
|
||||
|
||||
This command will do several things, most importantly:
|
||||
|
||||
- Run a wsgi server using [`uwsgi`][7] for the Mailman's Django-based web
|
||||
frontend listening on port 8000. It will run 2 worker
|
||||
processes with 4 threads each. You may want to change the setting
|
||||
`ALLOWED_HOSTS` in the settings before deploying the application in
|
||||
production.
|
||||
|
||||
- Run a PostgreSQL server with a default database, username, and password as
|
||||
mentioned in the `docker-compose.yaml`. You will have to change configuration
|
||||
files too if you change any of these.
|
||||
|
||||
- Run mailman-core listening on port 8001 for REST API and port 8024 (LMTP
|
||||
server) for messages from your MTA. You will have to configure your MTA to
|
||||
send messages at this address.
|
||||
|
||||
Some more details about what the above system achieves is mentioned below. If you
|
||||
are only going to deploy a simple configuration, you don't need to read
|
||||
this. However, these are very easy to understand if you know how docker works.
|
||||
|
||||
- First create a bridge network called `mailman` in the
|
||||
`docker-compose.yaml`. It will probably be named something else in your
|
||||
machine. All the containers
|
||||
mentioned (mailman-core, mailman-web, database) will join this network and are
|
||||
assigned static IPs. The host operating system is the default gateway
|
||||
from within these containers.
|
||||
|
||||
- Spin off a mailman-core container attached to the mailman bridge network created above. It has
|
||||
GNU Mailman 3 core running inside it. Mailman core's REST API is available at
|
||||
port 8001 and LMTP server listens at port 8024.
|
||||
|
||||
- Spin off a mailman-web container which has a Django application running with
|
||||
both Mailman's web frontend Postorius and Mailman's web-based Archiver
|
||||
running. [Uwsgi][7] server is used to run a web server with the configuration
|
||||
provided in this repository [here][2]. You may want to
|
||||
change the setting `ALLOWED_HOSTS` in the settings before deploying the
|
||||
application in production. You can do that by adding a
|
||||
`/opt/mailman/web/settings_local.py` which is imported by the Django when
|
||||
running.
|
||||
|
||||
- Spin off a PostgreSQL database container which is used by both mailman-core
|
||||
and mailman-web as their primary database.
|
||||
|
||||
- mailman-core mounts `/opt/mailman/core` from host OS at `/opt/mailman` in the
|
||||
container. Mailman's var directory is stored there so that it is accessible
|
||||
from the host operating system. Configuration for Mailman core is generated on
|
||||
every run from the environment variables provided. Extra configuration can
|
||||
also be provided at `/opt/mailman/core/mailman-extra.cfg` (on host), and will
|
||||
be added to generated configuration file. Mailman also needs another
|
||||
configuration file called
|
||||
[mailman-hyperkitty.cfg][3] and is also
|
||||
expected to be at `/opt/mailman/core/` on the host OS.
|
||||
|
||||
[3]: https://github.com/maxking/docker-mailman/blob/master/core/assets/mailman-hyperkitty.cfg
|
||||
|
||||
- mailman-web mounts `/opt/mailman/web` from the host OS to
|
||||
`/opt/mailman-web-data` in the container. It consists of the logs and
|
||||
settings_local.py file for Django.
|
||||
|
||||
- database mounts `/opt/mailman/database` at `/var/lib/postgresql/data` so that
|
||||
PostgreSQL can persist its data even if the database containers are
|
||||
updated/changed/removed.
|
||||
|
||||
## Setting up your MTA
|
||||
|
||||
The provided docker containers do not have an MTA in-built. You can either run
|
||||
your own MTA inside a container and have them relay emails to the mailman-core
|
||||
container or just install an MTA on the host and have them relay emails.
|
||||
|
||||
### Exim4
|
||||
|
||||
To use [Exim4][8], it should be setup to relay emails from mailman-core and
|
||||
mailman-web. The mailman specific configuration is provided in the
|
||||
repository at `core/assets/exim`. There are three files
|
||||
|
||||
- [25_mm3_macros](core/assets/exim/25_mm3_macros) to be placed at
|
||||
`/etc/exim4/conf.d/main/25_mm3_macros` in a typical Debian install of
|
||||
exim4. Please change MY_DOMAIN_NAME to the domain name that will be used to
|
||||
serve mailman. Multi-domain setups will be added later.
|
||||
|
||||
- [455_mm3_router](core/assets/exim/455_mm3_router) to be placed at
|
||||
`/etc/exim4/conf.d/router/455_mm3_router` in a typical Debian install of exim4.
|
||||
|
||||
- [55_mm3_transport](core/assets/exim/55_mm3_transport) to be placed at
|
||||
`/etc/exim4/conf.d/transport/55_mm3_transport` in a typical Debian install of exim4.
|
||||
|
||||
|
||||
Also, the default configuration inside the mailman-core image has the MTA set
|
||||
to Exim, but just for reference, it looks like this:
|
||||
|
||||
```
|
||||
# mailman.cfg
|
||||
[mta]
|
||||
incoming: mailman.mta.exim4.LMTP
|
||||
outgoing: mailman.mta.deliver.deliver
|
||||
lmtp_host: $MM_HOSTNAME
|
||||
lmtp_port: 8024
|
||||
smtp_host: $SMTP_HOST
|
||||
smtp_port: $SMTP_PORT
|
||||
configuration: python:mailman.config.exim4
|
||||
```
|
||||
|
||||
### Postfix
|
||||
|
||||
To use [Postfix][12], edit the `main.cf` configuration file, which is typically
|
||||
at `/etc/postfix/main.cf` on Debian-based operating systems. Add
|
||||
mailman-core and mailman-web to `mynetworks` so it will relay emails from
|
||||
the containers and add the following configuration lines:
|
||||
|
||||
```
|
||||
# main.cf
|
||||
|
||||
# Support the default VERP delimiter.
|
||||
recipient_delimiter = +
|
||||
unknown_local_recipient_reject_code = 550
|
||||
owner_request_special = no
|
||||
|
||||
transport_maps =
|
||||
regexp:/opt/mailman/core/var/data/postfix_lmtp
|
||||
local_recipient_maps =
|
||||
regexp:/opt/mailman/core/var/data/postfix_lmtp
|
||||
relay_domains =
|
||||
regexp:/opt/mailman/core/var/data/postfix_domains
|
||||
```
|
||||
|
||||
To configure Mailman to use Postfix, add `MTA=postfix` under mailman-core's
|
||||
environment section in the `docker-compose.yaml`:
|
||||
|
||||
```
|
||||
mailman-core:
|
||||
<snip>
|
||||
environment:
|
||||
- MTA=postfix
|
||||
```
|
||||
|
||||
This will auto-generate the configuration to talk to Postfix assuming that
|
||||
Postfix is available at the gateway address for the container's bridge network
|
||||
at port 25. The final configuration can be found by executing:
|
||||
|
||||
```
|
||||
$ docker exec mailman-core cat /etc/mailman.cfg
|
||||
```
|
||||
|
||||
The postfix configuration that is generated looks like this:
|
||||
```
|
||||
[mta]
|
||||
incoming: mailman.mta.postfix.LMTP
|
||||
outgoing: mailman.mta.deliver.deliver
|
||||
lmtp_host: $MM_HOSTNAME
|
||||
lmtp_port: 8024
|
||||
smtp_host: $SMTP_HOST
|
||||
smtp_port: $SMTP_PORT
|
||||
configuration: /etc/postfix-mailman.cfg
|
||||
```
|
||||
|
||||
So, if you need to update the values, you can set `SMTP_HOST`, `SMTP_PORT`,
|
||||
`MM_HOSTNAME` environment variables in `mailman-core` container.
|
||||
|
||||
Please verify the output for `[mta]` section to ensure that it points to
|
||||
the right `smtp_host` (address to reach postfix from mailman-core container)
|
||||
and `lmtp_host` (address to reach mailman-core container from postfix).
|
||||
|
||||
The configuration file `/etc/postfix-mailman.cfg` is also generated automatically
|
||||
inside the `mailman-core` container and contains the configuration specific
|
||||
for Postfix.
|
||||
|
||||
## Site Owner
|
||||
|
||||
Setup site owner address. By default, mailman is setup with the site_owner set to 'changeme@example.com'. This should be pointing to a valid mailbox. Add the following to the '/opt/mailman/core/mailman-extra.cfg'.
|
||||
|
||||
```
|
||||
[mailman]
|
||||
# This address is the "site owner" address. Certain messages which must be
|
||||
# delivered to a human, but which can't be delivered to a list owner (e.g. a
|
||||
# bounce from a list owner), will be sent to this address. It should point to
|
||||
# a human.
|
||||
site_owner: changeme@example.com
|
||||
```
|
||||
|
||||
## Setting up search indexing
|
||||
|
||||
Hyperkitty in mailman-web image support full-text indexing. The current default
|
||||
indexing engine is [Whoosh](https://whoosh.readthedocs.io/en/latest/intro.html)
|
||||
for historical reasons. It is highly recommended that you instead use Xapian for
|
||||
production use cases. The default will change when the next major version bump
|
||||
happens.
|
||||
|
||||
To configure your Mailman-web container to use Xapian, add the following to your
|
||||
`settings_local.py`:
|
||||
|
||||
```python
|
||||
HAYSTACK_CONNECTIONS = {
|
||||
'default': {
|
||||
'ENGINE': 'xapian_backend.XapianEngine',
|
||||
'PATH': "/opt/mailman-web-data/fulltext_index",
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
If you have been using the default search indexing engine, you might have to
|
||||
re-index emails using the following command:
|
||||
|
||||
```bash
|
||||
$ docker-compose exec mailman-web ./manage.py rebuild_index
|
||||
```
|
||||
|
||||
This command can take some time if you a lot of emails, so please be patient!
|
||||
|
||||
## Setting up your web server
|
||||
|
||||
It is advisable to run your Django (interfaced through WSGI server) through an
|
||||
_actual_ webserver in production for better performance.
|
||||
|
||||
If you are using v0.1.0, the uwsgi server is configured to listen to requests at
|
||||
port `8000` using the `HTTP` protocol. Make sure that you preserve the `HOST`
|
||||
header when you proxy the requests from your Web Server. In Nginx you can do
|
||||
that by adding the following to your configuration:
|
||||
|
||||
```
|
||||
# Nginx configuration.
|
||||
location /static {
|
||||
alias /opt/mailman/web/static;
|
||||
autoindex off;
|
||||
}
|
||||
|
||||
location / {
|
||||
proxy_pass http://127.0.0.1:8000;
|
||||
include uwsgi_params;
|
||||
uwsgi_read_timeout 300;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Forwarded-For $remote_addr;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
Make sure you are using `proxy_pass` for the `HTTP` protocol.
|
||||
|
||||
### uwsgi
|
||||
|
||||
|
||||
Starting from v0.1.1, the uwsgi server is configured to listen to requests at
|
||||
port `8000` with the http protocol and port `8080` for the uwsgi
|
||||
protocol.
|
||||
|
||||
**Please make sure that you are using port 8080 for uwsgi protocol.**
|
||||
|
||||
It is advised to use the uwsgi protocol as it has better performance. Both
|
||||
Apache and Nginx have native support for the uwsgi protocol through plugins which
|
||||
are generally included in the distro packages.
|
||||
|
||||
To move to uwsgi protocol in the above nginx configuration use this
|
||||
|
||||
```
|
||||
# Nginx configuration.
|
||||
location /static {
|
||||
alias /opt/mailman/web/static;
|
||||
autoindex off;
|
||||
}
|
||||
|
||||
location / {
|
||||
uwsgi_pass localhost:8080;
|
||||
include uwsgi_params;
|
||||
uwsgi_read_timeout 300;
|
||||
}
|
||||
```
|
||||
|
||||
Please make sure that you are using v0.1.1 or greater if you use this configuration.
|
||||
|
||||
|
||||
### Serving static files
|
||||
|
||||
UWSGI by default doesn't serve static files so, when running
|
||||
`mailman-web` using the provided `docker-compose.yaml` file, you won't see any
|
||||
CSS or JS files being served.
|
||||
|
||||
To enable serving of static files using UWSGI, add the following environment
|
||||
variable to your `docker-compose.yaml` file under `mailman-web`:
|
||||
|
||||
```
|
||||
UWSGI_STATIC_MAP=/static=/opt/mailman-web-data/static
|
||||
```
|
||||
|
||||
It is recommended to use web-server to serve static files instead of UWSGI for
|
||||
better performance. You will have to add an alias rule in your web server to
|
||||
serve the static files. See [here][18] for instructions on how to configure your
|
||||
web server. The STATIC_ROOT for you would be `/opt/mailman/web/static`.
|
||||
|
||||
### SSL certificates
|
||||
|
||||
SSL Certificates from Lets Encrypt need to be renewed every 90 days. You can
|
||||
setup a cron job to do the job. I have this small shell script (certbot-renew.sh)
|
||||
that you can put up in `/etc/cron.monthly` to get the job done.
|
||||
|
||||
```
|
||||
#! /bin/bash
|
||||
|
||||
cd /opt/letsencrypt/
|
||||
./certbot-auto --config /etc/letsencrypt/renewal/MY_DOMAIN_NAME.conf certonly
|
||||
|
||||
if [ $? -ne 0 ]
|
||||
then
|
||||
ERRORLOG=`tail /var/log/letsencrypt/letsencrypt.log`
|
||||
echo -e "The Let's Encrypt cert has not been renewed! \n \n" \
|
||||
$ERRORLOG
|
||||
else
|
||||
nginx -s reload
|
||||
fi
|
||||
|
||||
exit 0
|
||||
```
|
||||
|
||||
**Please do not forget to make the script executable (`chmod +x certbot-renew.sh`).**
|
||||
|
||||
## LICENSE
|
||||
|
||||
This repository is licensed under the MIT License. Please see the LICENSE file for
|
||||
more details.
|
||||
|
||||
[1]: http://list.org
|
||||
[2]: https://www.docker.com/
|
||||
[3]: https://docs.docker.com/compose/
|
||||
[4]: http://docs.mailman3.org/en/latest/
|
||||
[5]: https://docs.docker.com/engine/installation/
|
||||
[6]: https://docs.docker.com/compose/install/
|
||||
[7]: https://uwsgi-docs.readthedocs.io/en/latest/
|
||||
[8]: http://exim.org/
|
||||
[9]: https://letsencrypt.org/
|
||||
[10]: https://certbot.eff.org/
|
||||
[11]: https://mailman.readthedocs.io/en/latest/src/mailman/docs/database.html
|
||||
[12]: http://www.postfix.org/
|
||||
[13]: http://semver.org/
|
||||
[14]: https://docs.docker.com/engine/security/trust/content_trust/
|
||||
[15]: http://docs.mailman3.org/en/latest/config-web.html#setting-up-email
|
||||
[17]: https://docs.mailman3.org/en/latest/install/virtualenv.html#nginx-configuration
|
||||
[18]: http://docs.list.org/en/latest/pre-installation-guide.html#django-static-files
|
||||
[19]: https://github.com/maxking/docker-mailman/blob/master/docker-compose.yaml
|
35
mailman/build.sh
Executable file
35
mailman/build.sh
Executable file
@ -0,0 +1,35 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -ex
|
||||
|
||||
# Since all the dockerfiles now require buildkit features.
|
||||
export DOCKER_BUILDKIT=1
|
||||
|
||||
# Set the default value of BUILD_ROLLING to no.
|
||||
export BUILD_ROLLING="${1:-no}"
|
||||
|
||||
DOCKER=docker
|
||||
|
||||
if [ "$BUILD_ROLLING" = "yes" ]; then
|
||||
echo "Building rolling releases..."
|
||||
# Build the mailman-core image.
|
||||
$DOCKER build -f core/Dockerfile.dev \
|
||||
--label version.git_commit="$COMMIT_ID" \
|
||||
-t maxking/mailman-core:rolling core/
|
||||
|
||||
# Build the mailman-web image.
|
||||
$DOCKER build -f web/Dockerfile.dev \
|
||||
--label version.git_commit="$COMMIT_ID" \
|
||||
-t maxking/mailman-web:rolling web/
|
||||
|
||||
# build the postorius image.
|
||||
$DOCKER build -f postorius/Dockerfile.dev\
|
||||
--label version.git_commit="$COMMIT_ID"\
|
||||
-t maxking/postorius:rolling postorius/
|
||||
else
|
||||
echo "Building stable releases..."
|
||||
# Build the stable releases.
|
||||
$DOCKER build -t maxking/mailman-core:rolling core/
|
||||
$DOCKER build -t maxking/mailman-web:rolling web/
|
||||
$DOCKER build -t maxking/postorius:rolling postorius/
|
||||
fi
|
37
mailman/core/Dockerfile
Normal file
37
mailman/core/Dockerfile
Normal file
@ -0,0 +1,37 @@
|
||||
# syntax = docker/dockerfile:1.3
|
||||
# Use 3.15 for Core since it has Python 3.9
|
||||
FROM alpine:3.19
|
||||
|
||||
# Add requirements file.
|
||||
COPY requirements.txt /tmp/
|
||||
|
||||
#Install all required packages, add user for executing mailman and set execution rights for startup script
|
||||
RUN --mount=type=cache,target=/root/.cache \
|
||||
apk update \
|
||||
&& apk add --virtual build-deps gcc python3-dev musl-dev postgresql-dev \
|
||||
libffi-dev \
|
||||
# Mailman html to plaintext conversion uses lynx.
|
||||
# psutil needs linux-headers to compile on musl c library.
|
||||
&& apk add --no-cache bash su-exec postgresql-client mysql-client curl python3 py3-pip linux-headers py-cryptography mariadb-connector-c lynx tzdata \
|
||||
&& python3 -m pip install --break-system-packages -U pip setuptools wheel \
|
||||
&& python3 -m pip install --break-system-packages psycopg2 \
|
||||
gunicorn==19.9.0 \
|
||||
pymysql \
|
||||
-r /tmp/requirements.txt \
|
||||
'importlib-resources<6.0.0' \
|
||||
&& apk del build-deps \
|
||||
&& adduser -S mailman
|
||||
|
||||
#Add startup script to container
|
||||
COPY docker-entrypoint.sh /usr/local/bin/
|
||||
|
||||
# Change the working directory.
|
||||
WORKDIR /opt/mailman
|
||||
|
||||
#Expose the ports for the api (8001) and lmtp (8024)
|
||||
EXPOSE 8001 8024
|
||||
|
||||
ENV MAILMAN_CONFIG_FILE /etc/mailman.cfg
|
||||
|
||||
ENTRYPOINT ["docker-entrypoint.sh"]
|
||||
CMD ["master", "--force"]
|
39
mailman/core/Dockerfile.dev
Normal file
39
mailman/core/Dockerfile.dev
Normal file
@ -0,0 +1,39 @@
|
||||
# syntax = docker/dockerfile:1.3
|
||||
# Use 3.15 for Core since it has Python 3.9
|
||||
FROM alpine:3.19
|
||||
|
||||
# Set the commits that we are building.
|
||||
ARG CORE_REF
|
||||
ARG MM3_HK_REF
|
||||
|
||||
#Install all required packages, add user for executing mailman and set execution
|
||||
#rights for startup script
|
||||
RUN --mount=type=cache,target=/root/.cache \
|
||||
apk update \
|
||||
&& apk add --no-cache --virtual build-deps gcc python3-dev musl-dev \
|
||||
postgresql-dev git libffi-dev g++ \
|
||||
&& apk add --no-cache bash su-exec postgresql-client mysql-client \
|
||||
curl python3 py3-pip linux-headers py-cryptography mariadb-connector-c tzdata \
|
||||
&& python3 -m pip install -U --break-system-packages psycopg2 pymysql setuptools wheel \
|
||||
&& python3 -m pip install --break-system-packages \
|
||||
git+https://gitlab.com/mailman/mailman \
|
||||
git+https://gitlab.com/mailman/mailman-hyperkitty \
|
||||
gunicorn==19.9.0 \
|
||||
&& apk del build-deps \
|
||||
&& adduser -S mailman
|
||||
|
||||
#Add startup script to container
|
||||
COPY docker-entrypoint.sh /usr/local/bin/
|
||||
|
||||
# Change the working directory.
|
||||
WORKDIR /opt/mailman
|
||||
|
||||
#Expose the ports for the api (8001) and lmtp (8024)
|
||||
EXPOSE 8001 8024
|
||||
|
||||
# Set the default configuration file.
|
||||
ENV MAILMAN_CONFIG_FILE /etc/mailman.cfg
|
||||
|
||||
ENTRYPOINT ["docker-entrypoint.sh"]
|
||||
|
||||
CMD ["master"]
|
154
mailman/core/README.md
Normal file
154
mailman/core/README.md
Normal file
@ -0,0 +1,154 @@
|
||||
Mailman3 Core Docker Image
|
||||
==========================
|
||||
|
||||
When you spawn off this container, you must mount `/opt/mailman` to the
|
||||
container. Mailman's `var` directory will also be stored here so that it can
|
||||
persist across different sessions and containers. Any configuration at
|
||||
`/opt/mailman/core/mailman-extra.cfg` (on the host) will be added to the mailman's default
|
||||
generated confifguration (see below).
|
||||
|
||||
It is not advised to run multiple mailman processes on the same host sharing the
|
||||
same `/opt/mailman` (`/opt/mailman/core` on the host) directory as this will
|
||||
almost certainly be dangerous.
|
||||
|
||||
|
||||
Configuration
|
||||
=============
|
||||
|
||||
These are the variables that you MUST change before deploying:
|
||||
|
||||
- `HYPERKITTY_API_KEY`: Hyperkitty's API Key, should be set to the same value as
|
||||
set for the mailman-core.
|
||||
|
||||
- `DATABASE_URL`: URL of the type
|
||||
`driver://user:password@hostname:port/databasename` for the django to use. If
|
||||
not set, the default is set to
|
||||
`sqlite:///opt/mailman-web-data/mailmanweb.db`. The standard
|
||||
docker-compose.yaml comes with it set to a postgres database. It is not must
|
||||
to change this if you are happy with PostgreSQL.
|
||||
|
||||
- `DATABASE_TYPE`: Its value can be one of `sqlite`, `postgres` or `mysql` as
|
||||
these are the only three database types that Mailman 3 supports. Its default
|
||||
value is set to `sqlite` along with the default database class and default
|
||||
database url above.
|
||||
|
||||
- `DATABASE_CLASS`: Default value is
|
||||
`mailman.database.sqlite.SQLiteDatabase`. The values for this can be found in
|
||||
the mailman's documentation [here][11].
|
||||
|
||||
|
||||
These are the variables that you don't need to change if you are using a
|
||||
standard version of docker-compose.yaml from this repository.
|
||||
|
||||
- `MM_HOSTNAME`: Which hostname or IP should Core bind to for REST API and
|
||||
LMTP. If not defined output from the `hostname -i` command is used.
|
||||
|
||||
- `MAILMAN_REST_PORT`: Which port should Core use for the REST API. If not defined
|
||||
the default is `8001`.
|
||||
|
||||
- `MAILMAN_REST_USER`: Which username should Core use for the REST API. If not
|
||||
defined the default is `restadmin`.
|
||||
|
||||
- `MAILMAN_REST_PASSWORD`: Which password should Core use for the REST API. If
|
||||
not defined the default is `restpass`.
|
||||
|
||||
- `MTA`: Mail Transfer Agent to use. Either `exim` or `postfix`. Default value is `exim`.
|
||||
|
||||
- `SMTP_HOST`: IP Address/hostname from which you will be sending
|
||||
emails. Default value is the container's gateway retrieved from:
|
||||
/sbin/ip route | awk '/default/ { print $3 }'
|
||||
|
||||
- `SMTP_PORT`: Port used for SMTP. Default is `25`.
|
||||
|
||||
- `HYPERKITTY_URL`: Default value is `http://mailman-web:8000/hyperkitty`
|
||||
|
||||
In case of a need for fine tuning of REST API web-server that uses [Gunicorn](https://docs.gunicorn.org/en/stable/settings.html) (e.g. for raising of timeouts) `/opt/mailman/core/gunicorn-extra.cfg` file could be provided holding necessary configuration options.
|
||||
|
||||
Configuration file, [shipped with Mailman Core](https://gitlab.com/mailman/mailman/-/blob/master/src/mailman/config/gunicorn.cfg), is used by default.
|
||||
|
||||
For example, to increase the default 30 sec timeout, which won't work for some API calls to highly populated lists, provide the following `gunicorn-extra.cfg` file:
|
||||
|
||||
```
|
||||
[gunicorn]
|
||||
graceful_timeout = 30
|
||||
timeout = 300
|
||||
```
|
||||
|
||||
Running Mailman-Core
|
||||
====================
|
||||
|
||||
It is highly recomended that you run this image along with the
|
||||
docker-compose.yaml file provided at the [github repo][1] of this
|
||||
image. However, it is possible to run this image as a standalone container if
|
||||
you want just a mailman-core.
|
||||
|
||||
```bash
|
||||
$ mkdir -p /opt/mailman/core
|
||||
$ docker run -it -e "HYPERKITTY_API_KEY=changeme" -h mailman-core -v /opt/mailman/core:/opt/mailman mailman-core
|
||||
```
|
||||
|
||||
However, if you don't provide the environment `DATABASE_URL`, the database _may_
|
||||
not be persisted. All the configuration options are explained in more detail.
|
||||
|
||||
If you need mode advanced configuration for mailman, you can create
|
||||
`/opt/mailman/mailman.cfg` and it be added to the configuration inside the
|
||||
container. Note that anything inside this configuration will override the
|
||||
settings provided via the environment variables and their default values.
|
||||
|
||||
By default, the following settings are generated:
|
||||
|
||||
```
|
||||
# mailman.cfg
|
||||
[mta]
|
||||
incoming: mailman.mta.exim4.LMTP
|
||||
outgoing: mailman.mta.deliver.deliver
|
||||
lmtp_host: $MM_HOSTNAME
|
||||
lmtp_port: 8024
|
||||
smtp_host: $SMTP_HOST
|
||||
smtp_port: $SMTP_PORT
|
||||
configuration: python:mailman.config.exim4
|
||||
|
||||
[runner.retry]
|
||||
sleep_time: 10s
|
||||
|
||||
[webservice]
|
||||
hostname: $MM_HOSTNAME
|
||||
port: $MAILMAN_REST_PORT
|
||||
admin_user: $MAILMAN_REST_USER
|
||||
admin_pass: $MAILMAN_REST_PASSWORD
|
||||
configuration: /etc/gunicorn.cfg
|
||||
|
||||
[archiver.hyperkitty]
|
||||
class: mailman_hyperkitty.Archiver
|
||||
enable: yes
|
||||
configuration: /etc/mailman-hyperkitty.cfg
|
||||
|
||||
[database]
|
||||
class: $DATABASE_CLASS
|
||||
url: $DATABASE_URL
|
||||
```
|
||||
|
||||
```
|
||||
# mailman-hyperkitty.cfg
|
||||
[general]
|
||||
base_url: $HYPERKITTY_URL
|
||||
api_key: $HYPERKITTY_API_KEY
|
||||
```
|
||||
|
||||
MTA
|
||||
===
|
||||
|
||||
You can use Postfix or [Exim][2] with this image to send emails. Mailman Core
|
||||
can interact with any modern MTA which can deliver emails over LMTP. The
|
||||
documentation for Mailman Core has configuration settigs for using them.
|
||||
|
||||
Only Exim and Postfix have been tested with these images and are supported as of
|
||||
now. There _might_ be some limitations with using other MTAs in a containerized
|
||||
environments. Contributions are welcome for anything additional needed to
|
||||
support other MTAs.
|
||||
|
||||
To setup Exim or Posfix, checkout the [documentation][3].
|
||||
|
||||
[1]: https://github.com/maxking/docker-mailman
|
||||
[2]: http://www.exim.org
|
||||
[3]: https://asynchronous.in/docker-mailman#setting-up-your-mta
|
1
mailman/core/VERSION
Normal file
1
mailman/core/VERSION
Normal file
@ -0,0 +1 @@
|
||||
0.1.1
|
17
mailman/core/assets/exim/25_mm3_macros
Normal file
17
mailman/core/assets/exim/25_mm3_macros
Normal file
@ -0,0 +1,17 @@
|
||||
# Place this file at
|
||||
# /etc/exim4/conf.d/main/25_mm3_macros
|
||||
|
||||
domainlist mm3_domains=MY_DOMAIN_NAME
|
||||
# Depending on your network configuration
|
||||
#MM3_LMTP_HOST=mailman-core
|
||||
MM3_LMTP_HOST=localhost
|
||||
MM3_LMTP_PORT=8024
|
||||
MM3_HOME=/opt/mailman/core/var
|
||||
|
||||
################################################################
|
||||
# The configuration below is boilerplate:
|
||||
# you should not need to change it.
|
||||
|
||||
# The path to the list receipt (used as the required file when
|
||||
# matching list addresses)
|
||||
MM3_LISTCHK=MM3_HOME/lists/${local_part}.${domain}
|
15
mailman/core/assets/exim/455_mm3_router
Normal file
15
mailman/core/assets/exim/455_mm3_router
Normal file
@ -0,0 +1,15 @@
|
||||
# Place this file at
|
||||
# /etc/exim4/conf.d/router/455_mm3_router
|
||||
|
||||
mailman3_router:
|
||||
driver = accept
|
||||
domains = +mm3_domains
|
||||
require_files = MM3_LISTCHK
|
||||
local_part_suffix_optional
|
||||
local_part_suffix = -admin : \
|
||||
-bounces : -bounces+* : \
|
||||
-confirm : -confirm+* : \
|
||||
-join : -leave : \
|
||||
-owner : -request : \
|
||||
-subscribe : -unsubscribe
|
||||
transport = mailman3_transport
|
11
mailman/core/assets/exim/55_mm3_transport
Normal file
11
mailman/core/assets/exim/55_mm3_transport
Normal file
@ -0,0 +1,11 @@
|
||||
# Place this file at
|
||||
# /etc/exim4/conf.d/transport/55_mm3_transport
|
||||
|
||||
mailman3_transport:
|
||||
debug_print = "Email for mailman"
|
||||
driver = smtp
|
||||
protocol = lmtp
|
||||
allow_localhost
|
||||
hosts = MM3_LMTP_HOST
|
||||
port = MM3_LMTP_PORT
|
||||
rcpt_include_affixes = true
|
9
mailman/core/assets/mailman-hyperkitty.cfg
Normal file
9
mailman/core/assets/mailman-hyperkitty.cfg
Normal file
@ -0,0 +1,9 @@
|
||||
[general]
|
||||
# This is your HyperKitty installation, preferably on the localhost. This
|
||||
# address will be used by Mailman to forward incoming emails to HyperKitty
|
||||
# for archiving. It does not need to be publicly available, in fact it's
|
||||
# better if it is not.
|
||||
base_url: http://mailman-web:8000/hyperkitty/
|
||||
# Shared API key, must be the identical to the value in HyperKitty's
|
||||
# settings.
|
||||
api_key: ASmallAPIKey
|
35
mailman/core/assets/mailman.cfg
Normal file
35
mailman/core/assets/mailman.cfg
Normal file
@ -0,0 +1,35 @@
|
||||
[mta]
|
||||
incoming: mailman.mta.exim4.LMTP
|
||||
outgoing: mailman.mta.deliver.deliver
|
||||
lmtp_host: mailman-core
|
||||
lmtp_port: 8024
|
||||
smtp_host: 172.19.199.1
|
||||
smtp_port: 25
|
||||
configuration: python:mailman.config.exim4
|
||||
|
||||
# [archiver.mhonarc]
|
||||
# enable: yes
|
||||
|
||||
# [archiver.mail_archive]
|
||||
# enable: yes
|
||||
|
||||
# [archiver.prototype]
|
||||
# enable: yes
|
||||
|
||||
[runner.retry]
|
||||
sleep_time: 10s
|
||||
|
||||
[shell]
|
||||
use_ipython: yes
|
||||
|
||||
[webservice]
|
||||
hostname: mailman-core
|
||||
|
||||
[archiver.hyperkitty]
|
||||
class: mailman_hyperkitty.Archiver
|
||||
enable: yes
|
||||
configuration: /opt/mailman/mailman-hyperkitty.cfg
|
||||
|
||||
[database]
|
||||
class: mailman.database.postgresql.PostgreSQLDatabase
|
||||
url: postgres://mailman:mailmanpass@database/mailmandb
|
237
mailman/core/docker-entrypoint.sh
Executable file
237
mailman/core/docker-entrypoint.sh
Executable file
@ -0,0 +1,237 @@
|
||||
#! /bin/bash
|
||||
set -e
|
||||
|
||||
function wait_for_postgres () {
|
||||
# Check if the postgres database is up and accepting connections before
|
||||
# moving forward.
|
||||
# TODO: Use python3's psycopg2 module to do this in python3 instead of
|
||||
# installing postgres-client in the image.
|
||||
until psql -P pager=off $DATABASE_URL -c '\l'; do
|
||||
>&2 echo "Postgres is unavailable - sleeping"
|
||||
sleep 1
|
||||
done
|
||||
>&2 echo "Postgres is up - continuing"
|
||||
}
|
||||
|
||||
function wait_for_mysql () {
|
||||
# Check if MySQL is up and accepting connections.
|
||||
readarray -d' ' -t ENDPOINT <<< $(python3 -c "from urllib.parse import urlparse; o = urlparse('$DATABASE_URL'); print('%s %s' % (o.hostname, o.port if o.port else '3306'));")
|
||||
until mysqladmin ping --host ${ENDPOINT[0]} --port ${ENDPOINT[1]} --silent; do
|
||||
>&2 echo "MySQL is unavailable - sleeping"
|
||||
sleep 1
|
||||
done
|
||||
>&2 echo "MySQL is up - continuing"
|
||||
}
|
||||
|
||||
# Empty the config file.
|
||||
echo "# This file is autogenerated at container startup." > /etc/mailman.cfg
|
||||
|
||||
# Check if $MM_HOSTNAME is set, if not, set it to the value returned by
|
||||
# `hostname -i` command to set it to whatever IP address is assigned to the
|
||||
# container.
|
||||
if [[ ! -v MM_HOSTNAME ]]; then
|
||||
export MM_HOSTNAME=`hostname -i`
|
||||
fi
|
||||
|
||||
# SMTP_HOST defaults to the gateway
|
||||
if [[ ! -v SMTP_HOST ]]; then
|
||||
export SMTP_HOST=$(/sbin/ip route | awk '/default/ { print $3 }')
|
||||
echo "SMTP_HOST not specified, using the gateway ($SMTP_HOST) as default"
|
||||
fi
|
||||
|
||||
if [[ ! -v SMTP_PORT ]]; then
|
||||
export SMTP_PORT=25
|
||||
fi
|
||||
|
||||
# Check if REST port, username, and password are set, if not, set them
|
||||
# to default values.
|
||||
if [[ ! -v MAILMAN_REST_PORT ]]; then
|
||||
export MAILMAN_REST_PORT='8001'
|
||||
fi
|
||||
|
||||
if [[ ! -v MAILMAN_REST_USER ]]; then
|
||||
export MAILMAN_REST_USER='restadmin'
|
||||
fi
|
||||
|
||||
if [[ ! -v MAILMAN_REST_PASSWORD ]]; then
|
||||
export MAILMAN_REST_PASSWORD='restpass'
|
||||
fi
|
||||
|
||||
function setup_database () {
|
||||
if [[ ! -v DATABASE_URL ]]
|
||||
then
|
||||
echo "Environment variable DATABASE_URL should be defined..."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Translate mysql:// urls to mysql+mysql:// backend:
|
||||
if [[ "$DATABASE_URL" == mysql://* ]]; then
|
||||
DATABASE_URL="mysql+pymysql://${DATABASE_URL:8}"
|
||||
echo "Database URL prefix was automatically rewritten to: mysql+pymysql://"
|
||||
fi
|
||||
|
||||
# If DATABASE_CLASS is not set, guess it for common databases:
|
||||
if [ -z "$DATABASE_CLASS" ]; then
|
||||
if [[ ("$DATABASE_URL" == mysql:*) ||
|
||||
("$DATABASE_URL" == mysql+*) ]]; then
|
||||
DATABASE_CLASS=mailman.database.mysql.MySQLDatabase
|
||||
fi
|
||||
if [[ ("$DATABASE_URL" == postgres:*) ||
|
||||
("$DATABASE_URL" == postgres+*) ]]; then
|
||||
DATABASE_CLASS=mailman.database.postgresql.PostgreSQLDatabase
|
||||
fi
|
||||
fi
|
||||
|
||||
cat >> /etc/mailman.cfg <<EOF
|
||||
[database]
|
||||
class: $DATABASE_CLASS
|
||||
url: $DATABASE_URL
|
||||
EOF
|
||||
}
|
||||
|
||||
|
||||
# Check if $DATABASE_URL is defined, if not, use a standard sqlite database.
|
||||
#
|
||||
# If the $DATABASE_URL is defined and is postgres, check if it is available
|
||||
# yet. Do not start the container before the postgresql boots up.
|
||||
#
|
||||
# TODO: If the $DATABASE_URL is defined and is mysql, check if the database is
|
||||
# available before the container boots up.
|
||||
#
|
||||
# TODO: Check the database type and detect if it is up based on that. For now,
|
||||
# assume that postgres is being used if DATABASE_URL is defined.
|
||||
if [[ ! -v DATABASE_URL ]]; then
|
||||
echo "DATABASE_URL is not defined. Using sqlite database..."
|
||||
else
|
||||
setup_database
|
||||
fi
|
||||
|
||||
|
||||
if [[ "$DATABASE_TYPE" = 'postgres' ]]
|
||||
then
|
||||
wait_for_postgres
|
||||
elif [[ "$DATABASE_TYPE" = 'mysql' ]]
|
||||
then
|
||||
wait_for_mysql
|
||||
fi
|
||||
|
||||
# Generate a basic mailman.cfg.
|
||||
cat >> /etc/mailman.cfg << EOF
|
||||
[runner.retry]
|
||||
sleep_time: 10s
|
||||
|
||||
[webservice]
|
||||
hostname: $MM_HOSTNAME
|
||||
port: $MAILMAN_REST_PORT
|
||||
admin_user: $MAILMAN_REST_USER
|
||||
admin_pass: $MAILMAN_REST_PASSWORD
|
||||
configuration: /etc/gunicorn.cfg
|
||||
|
||||
EOF
|
||||
|
||||
# Generate a basic gunicorn.cfg.
|
||||
SITE_DIR=$(python3 -c 'import site; print(site.getsitepackages()[0])')
|
||||
cp "${SITE_DIR}/mailman/config/gunicorn.cfg" /etc/gunicorn.cfg
|
||||
|
||||
# Generate a basic configuration to use exim
|
||||
cat > /tmp/exim-mailman.cfg <<EOF
|
||||
[mta]
|
||||
incoming: mailman.mta.exim4.LMTP
|
||||
outgoing: mailman.mta.deliver.deliver
|
||||
lmtp_host: $MM_HOSTNAME
|
||||
lmtp_port: 8024
|
||||
smtp_host: $SMTP_HOST
|
||||
smtp_port: $SMTP_PORT
|
||||
smtp_user: $SMTP_HOST_USER
|
||||
smtp_pass: $SMTP_HOST_PASSWORD
|
||||
configuration: python:mailman.config.exim4
|
||||
|
||||
EOF
|
||||
|
||||
cat > /etc/postfix-mailman.cfg << EOF
|
||||
[postfix]
|
||||
transport_file_type: regex
|
||||
# While in regex mode, postmap_command is never used, a placeholder
|
||||
# is added here so that it doesn't break anything.
|
||||
postmap_command: true
|
||||
EOF
|
||||
|
||||
# Generate a basic configuration to use postfix.
|
||||
cat > /tmp/postfix-mailman.cfg <<EOF
|
||||
[mta]
|
||||
incoming: mailman.mta.postfix.LMTP
|
||||
outgoing: mailman.mta.deliver.deliver
|
||||
lmtp_host: $MM_HOSTNAME
|
||||
lmtp_port: 8024
|
||||
smtp_host: $SMTP_HOST
|
||||
smtp_port: $SMTP_PORT
|
||||
smtp_user: $SMTP_HOST_USER
|
||||
smtp_pass: $SMTP_HOST_PASSWORD
|
||||
configuration: /etc/postfix-mailman.cfg
|
||||
|
||||
EOF
|
||||
|
||||
if [ "$MTA" == "exim" ]
|
||||
then
|
||||
echo "Using Exim configuration"
|
||||
cat /tmp/exim-mailman.cfg >> /etc/mailman.cfg
|
||||
elif [ "$MTA" == "postfix" ]
|
||||
then
|
||||
echo "Using Postfix configuration"
|
||||
cat /tmp/postfix-mailman.cfg >> /etc/mailman.cfg
|
||||
else
|
||||
echo "No MTA environment variable found, defaulting to Exim"
|
||||
cat /tmp/exim-mailman.cfg >> /etc/mailman.cfg
|
||||
fi
|
||||
|
||||
rm -f /tmp/{postfix,exim}-mailman.cfg
|
||||
|
||||
if [[ -e /opt/mailman/mailman-extra.cfg ]]
|
||||
then
|
||||
echo "Found configuration file at /opt/mailman/mailman-extra.cfg"
|
||||
cat /opt/mailman/mailman-extra.cfg >> /etc/mailman.cfg
|
||||
fi
|
||||
|
||||
if [[ -e /opt/mailman/gunicorn-extra.cfg ]]
|
||||
then
|
||||
echo "Found [webserver] configuration file at /opt/mailman/gunicorn-extra.cfg"
|
||||
cat /opt/mailman/gunicorn-extra.cfg > /etc/gunicorn.cfg
|
||||
fi
|
||||
|
||||
if [[ -v HYPERKITTY_API_KEY ]]; then
|
||||
|
||||
echo "HYPERKITTY_API_KEY found, setting up HyperKitty archiver..."
|
||||
|
||||
cat >> /etc/mailman.cfg << EOF
|
||||
[archiver.hyperkitty]
|
||||
class: mailman_hyperkitty.Archiver
|
||||
enable: yes
|
||||
configuration: /etc/mailman-hyperkitty.cfg
|
||||
|
||||
EOF
|
||||
|
||||
if [[ ! -v HYPERKITTY_URL ]]; then
|
||||
echo "HYPERKITTY_URL not set, using the default value of http://mailman-web:8000/hyperkitty"
|
||||
export HYPERKITTY_URL="http://mailman-web:8000/hyperkitty/"
|
||||
fi
|
||||
|
||||
# Generate a basic mailman-hyperkitty.cfg.
|
||||
cat > /etc/mailman-hyperkitty.cfg <<EOF
|
||||
[general]
|
||||
base_url: $HYPERKITTY_URL
|
||||
api_key: $HYPERKITTY_API_KEY
|
||||
EOF
|
||||
|
||||
else
|
||||
|
||||
echo "HYPERKITTY_API_KEY not defined, skipping HyperKitty setup..."
|
||||
|
||||
fi
|
||||
|
||||
# Now chown the places where mailman wants to write stuff.
|
||||
chown -R mailman /opt/mailman
|
||||
|
||||
# Generate the LMTP files for postfix if needed.
|
||||
su-exec mailman mailman aliases
|
||||
|
||||
exec su-exec mailman "$@"
|
5
mailman/core/requirements.txt
Normal file
5
mailman/core/requirements.txt
Normal file
@ -0,0 +1,5 @@
|
||||
# This is a separate file from Dockerfile so that we can use dependabot
|
||||
# for version updates that isn't supported for contents inside the
|
||||
# Dockerfile.
|
||||
mailman==3.3.9
|
||||
mailman-hyperkitty==1.2.1
|
148
mailman/deploy.py
Normal file
148
mailman/deploy.py
Normal file
@ -0,0 +1,148 @@
|
||||
#! /usr/bin/env python
|
||||
|
||||
# Author: Abhilash Raj
|
||||
#
|
||||
# This is the primary deployment script for the docker-mailman repo. It does
|
||||
# deployment for stable and rolling releases both. It should be *always* invoked
|
||||
# and it will make the deployment decision based on the environment variables
|
||||
# that it sees.
|
||||
#
|
||||
# There are two kinds of deploymnets primarily:
|
||||
# 1. Rolling tags, which are built from each commit. These are typically run
|
||||
# in CI every day as well. These always update the "rolling" tag in the
|
||||
# docker registry.
|
||||
# 2. Stable tags, which are built from git tags with "va.b.c" tags. We don't
|
||||
# do the tag verification because for now, Circle CI does this for us. We
|
||||
# will tag and release a stable version whenever the right ENV var is set.
|
||||
#
|
||||
# Publishing:
|
||||
# We are typically publishing all the images to three repositories:
|
||||
# 1. Docker Hub: This is now rate-limited and might cause issues for people
|
||||
# pulling too frequently.
|
||||
# 2. Quay: This is an old registry that we started publishing too, so let's
|
||||
# continue publishing here too.
|
||||
# 3. Github Registry: This is the newest one in the mix and supports free
|
||||
# uploads and downloads (without very strict rate limits like Dockerhub.)
|
||||
|
||||
import os
|
||||
import subprocess
|
||||
from packaging import version
|
||||
|
||||
#: Default user, which owns the repositories.
|
||||
USER = 'maxking'
|
||||
|
||||
TAG_VAR = 'CIRCLE_TAG'
|
||||
BRANCH_VAR = 'CIRCLE_BRANCH'
|
||||
PRIMARY_BRANCH = 'main'
|
||||
ROLLING_VAR = "BUILD_ROLLING"
|
||||
|
||||
|
||||
def tag(original, final):
|
||||
"""Tag the source image with final tag."""
|
||||
try:
|
||||
print('Tagging {0} to {1}'.format(original, final))
|
||||
subprocess.run(
|
||||
['docker', 'tag', original, final],
|
||||
check=True,
|
||||
)
|
||||
except subprocess.CalledProcessError:
|
||||
print('Failed to tag {0}'.format(original))
|
||||
|
||||
|
||||
def login(url):
|
||||
"""Login to the registry."""
|
||||
if 'quay' in url.lower():
|
||||
password = os.environ['QUAY_PASSWORD']
|
||||
elif 'docker' in url.lower():
|
||||
password = os.environ['DOCKER_PASSWORD']
|
||||
elif 'ghcr' in url.lower():
|
||||
password = os.environ['GITHUB_PASSWORD']
|
||||
else:
|
||||
print('Password not found for {0}'.format(url))
|
||||
return None
|
||||
print('Logging in to {0}'.format(url))
|
||||
subprocess.run(
|
||||
['docker', 'login', '-u', USER, '-p', password, url],
|
||||
check=True
|
||||
)
|
||||
print('Logged in to {0}'.format(url))
|
||||
|
||||
|
||||
def push(image):
|
||||
"""Push all the images."""
|
||||
print('Pushing {}'.format(image))
|
||||
subprocess.run(['docker', 'push', image], check=True)
|
||||
|
||||
|
||||
def tag_and_push(image_names, url, img_tag):
|
||||
"""Given the URL to repository, tag and push the images."""
|
||||
# Tag recently built images.
|
||||
source, final = image_names
|
||||
tag(source, final)
|
||||
# Finall, push all the images.
|
||||
push(final)
|
||||
|
||||
|
||||
def get_tag_without_patch(tag):
|
||||
"""Given A.B.C return A.B"""
|
||||
v = version.parse(tag)
|
||||
return '{}.{}'.format(v.major, v.minor)
|
||||
|
||||
|
||||
def get_urls(url, img_tag):
|
||||
core = ('maxking/mailman-core:rolling',
|
||||
'{0}/maxking/mailman-core:{1}'.format(url, img_tag))
|
||||
web = ('maxking/mailman-web:rolling',
|
||||
'{0}/maxking/mailman-web:{1}'.format(url, img_tag))
|
||||
postorius = ('maxking/postorius:rolling',
|
||||
'{0}/maxking/postorius:{1}'.format(url, img_tag))
|
||||
|
||||
return (core, web, postorius)
|
||||
|
||||
|
||||
|
||||
def main():
|
||||
"""Primary entrypoint to this script."""
|
||||
# Boolean signifying if this is a stable release tag or just a branch.
|
||||
is_release = False
|
||||
|
||||
if os.environ.get(TAG_VAR) not in (None, ''):
|
||||
img_tag = os.environ.get(TAG_VAR)
|
||||
# Released versions are tagged vA.B.C, so remove
|
||||
# v from the tag when creating the release.
|
||||
if img_tag.startswith('v'):
|
||||
img_tag = img_tag[1:]
|
||||
is_release = True
|
||||
elif os.environ.get(BRANCH_VAR) == PRIMARY_BRANCH and os.environ.get(ROLLING_VAR) == "yes":
|
||||
img_tag = 'rolling'
|
||||
else:
|
||||
print('Not running on {PRIMARY_BRANCH} branch or Git tag so not publishing...'.format(
|
||||
PRIMARY_BRANCH=PRIMARY_BRANCH))
|
||||
exit(0)
|
||||
|
||||
# All the registries we are pushing to.
|
||||
for url in ('quay.io', 'docker.io', 'ghcr.io'):
|
||||
|
||||
try:
|
||||
login(url)
|
||||
except subprocess.CalledProcessError:
|
||||
print('Failed to login to {}'.format(url))
|
||||
continue
|
||||
|
||||
# Push all the container images.
|
||||
for each in get_urls(url, img_tag):
|
||||
tag_and_push(each, url, img_tag)
|
||||
|
||||
# If this is a release tag, tag them also with a.b version.
|
||||
if is_release:
|
||||
rel_tag = get_tag_without_patch(img_tag)
|
||||
for each in get_urls(url, rel_tag):
|
||||
tag_and_push(each, url, rel_tag)
|
||||
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
main()
|
||||
|
||||
|
70
mailman/docker-compose-mysql.yaml
Normal file
70
mailman/docker-compose-mysql.yaml
Normal file
@ -0,0 +1,70 @@
|
||||
version: '2'
|
||||
|
||||
services:
|
||||
mailman-core:
|
||||
image: maxking/mailman-core:0.4 # Use a specific version tag (tag latest is not published)
|
||||
container_name: mailman-core
|
||||
hostname: mailman-core
|
||||
restart: unless-stopped
|
||||
volumes:
|
||||
- /opt/mailman/core:/opt/mailman/
|
||||
stop_grace_period: 30s
|
||||
links:
|
||||
- database:database
|
||||
depends_on:
|
||||
- database
|
||||
environment:
|
||||
- DATABASE_URL=mysql+pymysql://mailman:mailmanpass@database/mailmandb?charset=utf8mb4&use_unicode=1 # Do use mysql+pymysql:// here
|
||||
- DATABASE_TYPE=mysql
|
||||
- HYPERKITTY_API_KEY=someapikey
|
||||
ports:
|
||||
- "127.0.0.1:8001:8001" # API
|
||||
- "127.0.0.1:8024:8024" # LMTP - incoming emails
|
||||
networks:
|
||||
mailman:
|
||||
|
||||
mailman-web:
|
||||
image: maxking/mailman-web:0.4 # Use a specific version tag (tag latest is not published)
|
||||
container_name: mailman-web
|
||||
hostname: mailman-web
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
- database
|
||||
links:
|
||||
- mailman-core:mailman-core
|
||||
- database:database
|
||||
volumes:
|
||||
- /opt/mailman/web:/opt/mailman-web-data
|
||||
environment:
|
||||
- DATABASE_TYPE=mysql
|
||||
- DATABASE_URL=mysql://mailman:mailmanpass@database/mailmandb?charset=utf8mb4 # Do use mysql:// here
|
||||
- HYPERKITTY_API_KEY=someapikey
|
||||
- SECRET_KEY=thisisaverysecretkey
|
||||
- DYLD_LIBRARY_PATH=/usr/local/mysql/lib/
|
||||
ports:
|
||||
- "127.0.0.1:8000:8000" # HTTP
|
||||
- "127.0.0.1:8080:8080" # uwsgi
|
||||
networks:
|
||||
mailman:
|
||||
|
||||
database:
|
||||
environment:
|
||||
MYSQL_DATABASE: mailmandb
|
||||
MYSQL_USER: mailman
|
||||
MYSQL_PASSWORD: mailmanpass
|
||||
MYSQL_RANDOM_ROOT_PASSWORD: "yes"
|
||||
image: mariadb:10.5
|
||||
command: --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci
|
||||
volumes:
|
||||
- /opt/mailman/database:/var/lib/mysql
|
||||
networks:
|
||||
mailman:
|
||||
|
||||
networks:
|
||||
mailman:
|
||||
driver: bridge
|
||||
ipam:
|
||||
driver: default
|
||||
config:
|
||||
-
|
||||
subnet: 172.19.199.0/24
|
68
mailman/docker-compose-postorius.yaml
Normal file
68
mailman/docker-compose-postorius.yaml
Normal file
@ -0,0 +1,68 @@
|
||||
version: '2'
|
||||
|
||||
services:
|
||||
mailman-core:
|
||||
image: maxking/mailman-core:0.4 # Use a specific version tag (tag latest is not published)
|
||||
container_name: mailman-core
|
||||
hostname: mailman-core
|
||||
restart: unless-stopped
|
||||
volumes:
|
||||
- /opt/mailman/core:/opt/mailman/
|
||||
stop_grace_period: 30s
|
||||
links:
|
||||
- database:database
|
||||
depends_on:
|
||||
- database
|
||||
environment:
|
||||
- DATABASE_URL=postgres://mailman:mailmanpass@database/mailmandb
|
||||
- DATABASE_TYPE=postgres
|
||||
- DATABASE_CLASS=mailman.database.postgresql.PostgreSQLDatabase
|
||||
ports:
|
||||
- "127.0.0.1:8001:8001" # API
|
||||
- "127.0.0.1:8024:8024" # LMTP - incoming emails
|
||||
networks:
|
||||
mailman:
|
||||
|
||||
mailman-web:
|
||||
image: maxking/postorius:0.4 # Use a specific version tag (tag latest is not published)
|
||||
container_name: mailman-web
|
||||
hostname: mailman-web
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
- database
|
||||
links:
|
||||
- mailman-core:mailman-core
|
||||
- database:database
|
||||
volumes:
|
||||
- /opt/mailman/web:/opt/mailman-web-data
|
||||
environment:
|
||||
- DATABASE_TYPE=postgres
|
||||
- DATABASE_URL=postgres://mailman:mailmanpass@database/mailmandb
|
||||
- SECRET_KEY=ksjdbaksdba
|
||||
- UWSGI_STATIC_MAP=/static=/opt/mailman-web-data/static
|
||||
ports:
|
||||
- "127.0.0.1:8000:8000" # HTTP
|
||||
- "127.0.0.1:8080:8080" # uwsgi
|
||||
networks:
|
||||
mailman:
|
||||
|
||||
database:
|
||||
environment:
|
||||
POSTGRES_DB: mailmandb
|
||||
POSTGRES_USER: mailman
|
||||
POSTGRES_PASSWORD: mailmanpass
|
||||
restart: always
|
||||
image: postgres:9.6-alpine
|
||||
volumes:
|
||||
- /opt/mailman/database:/var/lib/postgresql/data
|
||||
networks:
|
||||
mailman:
|
||||
|
||||
networks:
|
||||
mailman:
|
||||
driver: bridge
|
||||
ipam:
|
||||
driver: default
|
||||
config:
|
||||
-
|
||||
subnet: 172.19.199.0/24
|
67
mailman/docker-compose.yaml
Normal file
67
mailman/docker-compose.yaml
Normal file
@ -0,0 +1,67 @@
|
||||
version: '2'
|
||||
|
||||
services:
|
||||
mailman-core:
|
||||
image: maxking/mailman-core:0.4 # Use a specific version tag (tag latest is not published)
|
||||
container_name: mailman-core
|
||||
hostname: mailman-core
|
||||
restart: unless-stopped
|
||||
volumes:
|
||||
- /opt/mailman/core:/opt/mailman/
|
||||
stop_grace_period: 30s
|
||||
links:
|
||||
- database:database
|
||||
depends_on:
|
||||
- database
|
||||
environment:
|
||||
- DATABASE_URL=postgresql://mailman:mailmanpass@database/mailmandb
|
||||
- DATABASE_TYPE=postgres
|
||||
- DATABASE_CLASS=mailman.database.postgresql.PostgreSQLDatabase
|
||||
- HYPERKITTY_API_KEY=someapikey
|
||||
ports:
|
||||
- "127.0.0.1:8001:8001" # API
|
||||
- "127.0.0.1:8024:8024" # LMTP - incoming emails
|
||||
networks:
|
||||
mailman:
|
||||
|
||||
mailman-web:
|
||||
image: maxking/mailman-web:0.4 # Use a specific version tag (tag latest is not published)
|
||||
container_name: mailman-web
|
||||
hostname: mailman-web
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
- database
|
||||
links:
|
||||
- mailman-core:mailman-core
|
||||
- database:database
|
||||
volumes:
|
||||
- /opt/mailman/web:/opt/mailman-web-data
|
||||
environment:
|
||||
- DATABASE_TYPE=postgres
|
||||
- DATABASE_URL=postgresql://mailman:mailmanpass@database/mailmandb
|
||||
- HYPERKITTY_API_KEY=someapikey
|
||||
ports:
|
||||
- "127.0.0.1:8000:8000" # HTTP
|
||||
- "127.0.0.1:8080:8080" # uwsgi
|
||||
networks:
|
||||
mailman:
|
||||
|
||||
database:
|
||||
environment:
|
||||
- POSTGRES_DB=mailmandb
|
||||
- POSTGRES_USER=mailman
|
||||
- POSTGRES_PASSWORD=mailmanpass
|
||||
image: postgres:12-alpine
|
||||
volumes:
|
||||
- /opt/mailman/database:/var/lib/postgresql/data
|
||||
networks:
|
||||
mailman:
|
||||
|
||||
networks:
|
||||
mailman:
|
||||
driver: bridge
|
||||
ipam:
|
||||
driver: default
|
||||
config:
|
||||
-
|
||||
subnet: 172.19.199.0/24
|
1
mailman/docs/core.md
Symbolic link
1
mailman/docs/core.md
Symbolic link
@ -0,0 +1 @@
|
||||
../core/README.md
|
1
mailman/docs/index.md
Symbolic link
1
mailman/docs/index.md
Symbolic link
@ -0,0 +1 @@
|
||||
../README.md
|
1
mailman/docs/news.md
Symbolic link
1
mailman/docs/news.md
Symbolic link
@ -0,0 +1 @@
|
||||
../NEWS.md
|
1
mailman/docs/web.md
Symbolic link
1
mailman/docs/web.md
Symbolic link
@ -0,0 +1 @@
|
||||
../web/README.md
|
19
mailman/mkdocs.yml
Normal file
19
mailman/mkdocs.yml
Normal file
@ -0,0 +1,19 @@
|
||||
site_name: docker-mailman
|
||||
site_description: Container images for GNU Mailman
|
||||
|
||||
theme:
|
||||
name: material
|
||||
|
||||
repo_name: maxking/docker-mailman
|
||||
repo_url: https://github.com/maxking/docker-mailman
|
||||
use_directory_urls: true
|
||||
nav:
|
||||
- Home: "index.md"
|
||||
- Mailman Core: "core.md"
|
||||
- Mailman Web: "web.md"
|
||||
- News: "news.md"
|
||||
|
||||
markdown_extensions:
|
||||
- toc:
|
||||
permalink: true
|
||||
toc_depth: 3
|
46
mailman/postorius/Dockerfile
Normal file
46
mailman/postorius/Dockerfile
Normal file
@ -0,0 +1,46 @@
|
||||
# syntax = docker/dockerfile:1.3
|
||||
FROM alpine:3.19.1
|
||||
|
||||
# Install packages and dependencies for postorius and hyperkitty Add user for
|
||||
# executing apps, change ownership for uwsgi+django files and set execution
|
||||
# rights for management script
|
||||
RUN --mount=type=cache,target=/root/.cache \
|
||||
set -ex \
|
||||
&& apk add --no-cache --virtual .build-deps gcc libc-dev linux-headers libldap \
|
||||
postgresql-dev mariadb-dev mariadb-connector-c python3-dev libffi-dev openldap-dev cargo rust \
|
||||
&& apk add --no-cache --virtual .mailman-rundeps bash sassc tzdata \
|
||||
postgresql-client mysql-client py3-mysqlclient curl mailcap gettext \
|
||||
python3 py3-pip libffi libuuid pcre-dev py-cryptography \
|
||||
&& python3 -m pip install --break-system-packages -U 'Django<4.2' pip setuptools wheel \
|
||||
&& python3 -m pip install --break-system-packages postorius==1.3.10 \
|
||||
uwsgi \
|
||||
psycopg2 \
|
||||
dj-database-url \
|
||||
mysqlclient \
|
||||
typing \
|
||||
django-auth-ldap \
|
||||
python-memcached \
|
||||
tzdata \
|
||||
&& apk del .build-deps \
|
||||
&& addgroup -S mailman \
|
||||
&& adduser -S -G mailman mailman
|
||||
|
||||
# Add needed files for uwsgi server + settings for django
|
||||
COPY mailman-web /opt/mailman-web
|
||||
# Add startup script to container
|
||||
COPY docker-entrypoint.sh /usr/local/bin/
|
||||
|
||||
RUN chown -R mailman /opt/mailman-web/ \
|
||||
&& chmod u+x /opt/mailman-web/manage.py
|
||||
|
||||
WORKDIR /opt/mailman-web
|
||||
|
||||
# Expose port 8000 for http and port 8080 for uwsgi
|
||||
# (see web/mailman-web/uwsgi.ini#L2-L4)
|
||||
EXPOSE 8000 8080
|
||||
|
||||
# Use stop signal for uwsgi server
|
||||
STOPSIGNAL SIGINT
|
||||
|
||||
ENTRYPOINT ["docker-entrypoint.sh"]
|
||||
CMD ["uwsgi", "--ini", "/opt/mailman-web/uwsgi.ini"]
|
54
mailman/postorius/Dockerfile.dev
Normal file
54
mailman/postorius/Dockerfile.dev
Normal file
@ -0,0 +1,54 @@
|
||||
# syntax = docker/dockerfile:1.3
|
||||
FROM alpine:3.19.1
|
||||
|
||||
ARG POSTORIUS_REF
|
||||
ARG DJ_MM3_REF
|
||||
ARG CLIENT_REF
|
||||
|
||||
# Install packages and dependencies for postorius and hyperkitty Add user for
|
||||
# executing apps, change ownership for uwsgi+django files and set execution
|
||||
# rights for management script
|
||||
RUN --mount=type=cache,target=/root/.cache \
|
||||
set -ex \
|
||||
&& apk add --no-cache --virtual .build-deps gcc libc-dev linux-headers libldap \
|
||||
postgresql-dev mariadb-dev mariadb-connector-c python3-dev libffi-dev git cargo rust \
|
||||
&& apk add --no-cache --virtual .mailman-rundeps bash sassc tzdata \
|
||||
postgresql-client mysql-client py3-mysqlclient curl mailcap \
|
||||
python3 py3-pip libffi gettext py-cryptography \
|
||||
&& python3 -m pip install --break-system-packages -U pip setuptools wheel \
|
||||
&& python3 -m pip install --break-system-packages -U \
|
||||
git+https://gitlab.com/mailman/mailmanclient \
|
||||
git+https://gitlab.com/mailman/postorius \
|
||||
uwsgi \
|
||||
'psycopg2<2.9' \
|
||||
dj-database-url \
|
||||
mysqlclient \
|
||||
typing \
|
||||
django-utils-six \
|
||||
&& python3 -m pip install --break-system-packages -U 'Django<3.2' \
|
||||
&& python3 -m pip install --break-system-packages -U \
|
||||
git+https://gitlab.com/mailman/django-mailman3 \
|
||||
&& apk del .build-deps \
|
||||
&& addgroup -S mailman \
|
||||
&& adduser -S -G mailman mailman
|
||||
|
||||
# Add needed files for uwsgi server + settings for django
|
||||
COPY mailman-web /opt/mailman-web
|
||||
# Add startup script to container
|
||||
COPY docker-entrypoint.sh /usr/local/bin/
|
||||
|
||||
RUN chown -R mailman /opt/mailman-web/ \
|
||||
&& chmod u+x /opt/mailman-web/manage.py
|
||||
|
||||
WORKDIR /opt/mailman-web
|
||||
|
||||
# Expose port 8000 for http and port 8080 for uwsgi
|
||||
# (see web/mailman-web/uwsgi.ini#L2-L4)
|
||||
EXPOSE 8000 8080
|
||||
|
||||
# Use stop signal for uwsgi server
|
||||
STOPSIGNAL SIGINT
|
||||
|
||||
ENTRYPOINT ["docker-entrypoint.sh"]
|
||||
|
||||
CMD ["uwsgi", "--ini", "/opt/mailman-web/uwsgi.ini"]
|
147
mailman/postorius/docker-entrypoint.sh
Executable file
147
mailman/postorius/docker-entrypoint.sh
Executable file
@ -0,0 +1,147 @@
|
||||
#! /bin/bash
|
||||
set -e
|
||||
|
||||
|
||||
function wait_for_postgres () {
|
||||
# Check if the postgres database is up and accepting connections before
|
||||
# moving forward.
|
||||
# TODO: Use python's psycopg2 module to do this in python instead of
|
||||
# installing postgres-client in the image.
|
||||
until psql -P pager=off $DATABASE_URL -c '\l'; do
|
||||
>&2 echo "Postgres is unavailable - sleeping"
|
||||
sleep 1
|
||||
done
|
||||
>&2 echo "Postgres is up - continuing"
|
||||
}
|
||||
|
||||
function wait_for_mysql () {
|
||||
# Check if MySQL is up and accepting connections.
|
||||
readarray -d' ' -t ENDPOINT <<< $(python3 -c "from urllib.parse import urlparse; o = urlparse('$DATABASE_URL'); print('%s %s' % (o.hostname, o.port if o.port else '3306'));")
|
||||
until mysqladmin ping --host ${ENDPOINT[0]} --port ${ENDPOINT[1]} --silent; do
|
||||
>&2 echo "MySQL is unavailable - sleeping"
|
||||
sleep 1
|
||||
done
|
||||
>&2 echo "MySQL is up - continuing"
|
||||
}
|
||||
|
||||
function check_or_create () {
|
||||
# Check if the path exists, if not, create the directory.
|
||||
if [[ ! -e dir ]]; then
|
||||
echo "$1 does not exist, creating ..."
|
||||
mkdir "$1"
|
||||
fi
|
||||
}
|
||||
|
||||
# function postgres_ready(){
|
||||
# python << END
|
||||
# import sys
|
||||
# import psycopg2
|
||||
# try:
|
||||
# conn = psycopg2.connect(dbname="$POSTGRES_DB", user="$POSTGRES_USER", password="$POSTGRES_PASSWORD", host="postgres")
|
||||
# except psycopg2.OperationalError:
|
||||
# sys.exit(-1)
|
||||
# sys.exit(0)
|
||||
# END
|
||||
# }
|
||||
|
||||
# SMTP_HOST defaults to the gateway
|
||||
if [[ ! -v SMTP_HOST ]]; then
|
||||
export SMTP_HOST=$(/sbin/ip route | awk '/default/ { print $3 }')
|
||||
fi
|
||||
|
||||
# Check if $SECRET_KEY is defined, if not, bail out.
|
||||
if [[ ! -v SECRET_KEY ]]; then
|
||||
echo "SECRET_KEY is not defined. Aborting."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check if $DATABASE_URL is defined, if not, use a standard sqlite database.
|
||||
#
|
||||
# If the $DATABASE_URL is defined and is postgres, check if it is available
|
||||
# yet. Do not start the container before the postgresql boots up.
|
||||
#
|
||||
# If the $DATABASE_URL is defined and is mysql, check if the database is
|
||||
# available before the container boots up.
|
||||
#
|
||||
# TODO: Check the database type and detect if it is up based on that. For now,
|
||||
# assume that postgres is being used if DATABASE_URL is defined.
|
||||
|
||||
if [[ ! -v DATABASE_URL ]]; then
|
||||
echo "DATABASE_URL is not defined. Using sqlite database..."
|
||||
export DATABASE_URL=sqlite://mailmanweb.db
|
||||
export DATABASE_TYPE='sqlite'
|
||||
fi
|
||||
|
||||
if [[ "$DATABASE_TYPE" = 'postgres' ]]
|
||||
then
|
||||
wait_for_postgres
|
||||
elif [[ "$DATABASE_TYPE" = 'mysql' ]]
|
||||
then
|
||||
wait_for_mysql
|
||||
fi
|
||||
|
||||
# Check if we are in the correct directory before running commands.
|
||||
if [[ ! $(pwd) == '/opt/mailman-web' ]]; then
|
||||
echo "Running in the wrong directory...switching to /opt/mailman-web"
|
||||
cd /opt/mailman-web
|
||||
fi
|
||||
|
||||
# Check if the logs directory is setup.
|
||||
if [[ ! -e /opt/mailman-web-data/logs/mailmanweb.log ]]; then
|
||||
echo "Creating log file for mailman web"
|
||||
mkdir -p /opt/mailman-web-data/logs/
|
||||
touch /opt/mailman-web-data/logs/mailmanweb.log
|
||||
fi
|
||||
|
||||
if [[ ! -e /opt/mailman-web-data/logs/uwsgi.log ]]; then
|
||||
echo "Creating log file for uwsgi.."
|
||||
touch /opt/mailman-web-data/logs/uwsgi.log
|
||||
fi
|
||||
|
||||
# Check if the settings_local.py file exists, if yes, copy it too.
|
||||
if [[ -e /opt/mailman-web-data/settings_local.py ]]; then
|
||||
echo "Copying settings_local.py ..."
|
||||
cp /opt/mailman-web-data/settings_local.py /opt/mailman-web/settings_local.py
|
||||
chown mailman:mailman /opt/mailman-web/settings_local.py
|
||||
else
|
||||
echo "settings_local.py not found, it is highly recommended that you provide one"
|
||||
echo "Using default configuration to run."
|
||||
fi
|
||||
|
||||
# Collect static for the django installation.
|
||||
python3 manage.py collectstatic --noinput --clear --verbosity 0
|
||||
|
||||
# Compile all the installed po files to mo.
|
||||
SITE_DIR=$(python3 -c 'import site; print(site.getsitepackages()[0])')
|
||||
echo "Compiling locale files in $SITE_DIR"
|
||||
cd $SITE_DIR && python3 /opt/mailman-web/manage.py compilemessages && cd -
|
||||
|
||||
# Migrate all the data to the database if this is a new installation, otherwise
|
||||
# this command will upgrade the database.
|
||||
python3 manage.py migrate
|
||||
|
||||
# If MAILMAN_ADMIN_USER and MAILMAN_ADMIN_EMAIL is defined create a new
|
||||
# superuser for Django. There is no password setup so it can't login yet unless
|
||||
# the password is reset.
|
||||
if [[ -v MAILMAN_ADMIN_USER ]] && [[ -v MAILMAN_ADMIN_EMAIL ]];
|
||||
then
|
||||
echo "Creating admin user $MAILMAN_ADMIN_USER ..."
|
||||
python3 manage.py createsuperuser --noinput --username "$MAILMAN_ADMIN_USER"\
|
||||
--email "$MAILMAN_ADMIN_EMAIL" 2> /dev/null || \
|
||||
echo "Superuser $MAILMAN_ADMIN_USER already exists"
|
||||
fi
|
||||
|
||||
# If SERVE_FROM_DOMAIN is defined then rename the default `example.com`
|
||||
# domain to the defined domain.
|
||||
if [[ -v SERVE_FROM_DOMAIN ]];
|
||||
then
|
||||
echo "Setting $SERVE_FROM_DOMAIN as the default domain ..."
|
||||
python3 manage.py shell -c \
|
||||
"from django.contrib.sites.models import Site; Site.objects.filter(domain='example.com').update(domain='$SERVE_FROM_DOMAIN', name='$SERVE_FROM_DOMAIN')"
|
||||
fi
|
||||
|
||||
# Create a mailman user with the specific UID and GID and do not create home
|
||||
# directory for it. Also chown the logs directory to write the files.
|
||||
chown mailman:mailman /opt/mailman-web-data -R
|
||||
|
||||
exec $@
|
0
mailman/postorius/mailman-web/__init__.py
Normal file
0
mailman/postorius/mailman-web/__init__.py
Normal file
10
mailman/postorius/mailman-web/manage.py
Executable file
10
mailman/postorius/mailman-web/manage.py
Executable file
@ -0,0 +1,10 @@
|
||||
#!/usr/bin/env python
|
||||
import os
|
||||
import sys
|
||||
|
||||
if __name__ == "__main__":
|
||||
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "settings")
|
||||
|
||||
from django.core.management import execute_from_command_line
|
||||
|
||||
execute_from_command_line(sys.argv)
|
357
mailman/postorius/mailman-web/settings.py
Normal file
357
mailman/postorius/mailman-web/settings.py
Normal file
@ -0,0 +1,357 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (C) 1998-2016 by the Free Software Foundation, Inc.
|
||||
#
|
||||
# This file is part of Mailman Suite.
|
||||
#
|
||||
# Mailman Suite is free sofware: you can redistribute it and/or modify it
|
||||
# under the terms of the GNU General Public License as published by the Free
|
||||
# Software Foundation, either version 3 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# Mailman Suite is distributed in the hope that it will be useful, but
|
||||
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
# for more details.
|
||||
|
||||
# You should have received a copy of the GNU General Public License along
|
||||
# with Mailman Suite. If not, see <http://www.gnu.org/licenses/>.
|
||||
"""
|
||||
Django Settings for Mailman Suite (hyperkitty + postorius)
|
||||
|
||||
For more information on this file, see
|
||||
https://docs.djangoproject.com/en/1.8/topics/settings/
|
||||
|
||||
For the full list of settings and their values, see
|
||||
https://docs.djangoproject.com/en/1.8/ref/settings/
|
||||
"""
|
||||
|
||||
# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
|
||||
import os
|
||||
import dj_database_url
|
||||
import sys
|
||||
from socket import gethostbyname
|
||||
|
||||
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
|
||||
|
||||
# SECURITY WARNING: keep the secret key used in production secret!
|
||||
SECRET_KEY = os.environ.get('SECRET_KEY')
|
||||
|
||||
# SECURITY WARNING: don't run with debug turned on in production!
|
||||
DEBUG = False
|
||||
|
||||
ADMINS = (
|
||||
('Mailman Suite Admin', 'root@localhost'),
|
||||
)
|
||||
|
||||
SITE_ID = 1
|
||||
|
||||
# Hosts/domain names that are valid for this site; required if DEBUG is False
|
||||
# See https://docs.djangoproject.com/en/3.1/ref/settings/#allowed-hosts
|
||||
ALLOWED_HOSTS = [
|
||||
"localhost", # Archiving API from Mailman, keep it.
|
||||
"mailman-web",
|
||||
gethostbyname("mailman-web"),
|
||||
os.environ.get('SERVE_FROM_DOMAIN'),
|
||||
]
|
||||
ALLOWED_HOSTS.extend(os.getenv("DJANGO_ALLOWED_HOSTS", "").split(","))
|
||||
|
||||
# Mailman API credentials
|
||||
MAILMAN_REST_API_URL = os.environ.get('MAILMAN_REST_URL', 'http://mailman-core:8001')
|
||||
MAILMAN_REST_API_USER = os.environ.get('MAILMAN_REST_USER', 'restadmin')
|
||||
MAILMAN_REST_API_PASS = os.environ.get('MAILMAN_REST_PASSWORD', 'restpass')
|
||||
|
||||
# Application definition
|
||||
|
||||
INSTALLED_APPS = []
|
||||
DEFAULT_APPS = [
|
||||
'postorius',
|
||||
'django_mailman3',
|
||||
# Uncomment the next line to enable the admin:
|
||||
'django.contrib.admin',
|
||||
# Uncomment the next line to enable admin documentation:
|
||||
# 'django.contrib.admindocs',
|
||||
'django.contrib.auth',
|
||||
'django.contrib.contenttypes',
|
||||
'django.contrib.sessions',
|
||||
'django.contrib.sites',
|
||||
'django.contrib.messages',
|
||||
'django.contrib.staticfiles',
|
||||
'django.contrib.humanize',
|
||||
'django_gravatar',
|
||||
'allauth',
|
||||
'allauth.account',
|
||||
'allauth.socialaccount',
|
||||
]
|
||||
MAILMAN_WEB_SOCIAL_AUTH = [
|
||||
'django_mailman3.lib.auth.fedora',
|
||||
'allauth.socialaccount.providers.openid',
|
||||
'allauth.socialaccount.providers.github',
|
||||
'allauth.socialaccount.providers.gitlab',
|
||||
'allauth.socialaccount.providers.google',
|
||||
]
|
||||
|
||||
MIDDLEWARE = (
|
||||
'django.contrib.sessions.middleware.SessionMiddleware',
|
||||
'django.middleware.common.CommonMiddleware',
|
||||
'django.middleware.csrf.CsrfViewMiddleware',
|
||||
'django.middleware.locale.LocaleMiddleware',
|
||||
'django.contrib.auth.middleware.AuthenticationMiddleware',
|
||||
'django.contrib.messages.middleware.MessageMiddleware',
|
||||
'django.middleware.clickjacking.XFrameOptionsMiddleware',
|
||||
'django.middleware.security.SecurityMiddleware',
|
||||
'django_mailman3.middleware.TimezoneMiddleware',
|
||||
'allauth.account.middleware.AccountMiddleware',
|
||||
'postorius.middleware.PostoriusMiddleware',
|
||||
)
|
||||
|
||||
ROOT_URLCONF = 'urls'
|
||||
|
||||
TEMPLATES = [
|
||||
{
|
||||
'BACKEND': 'django.template.backends.django.DjangoTemplates',
|
||||
'DIRS': [],
|
||||
'APP_DIRS': True,
|
||||
'OPTIONS': {
|
||||
'context_processors': [
|
||||
'django.template.context_processors.debug',
|
||||
'django.template.context_processors.i18n',
|
||||
'django.template.context_processors.media',
|
||||
'django.template.context_processors.static',
|
||||
'django.template.context_processors.tz',
|
||||
'django.template.context_processors.csrf',
|
||||
'django.template.context_processors.request',
|
||||
'django.contrib.auth.context_processors.auth',
|
||||
'django.contrib.messages.context_processors.messages',
|
||||
'django_mailman3.context_processors.common',
|
||||
'postorius.context_processors.postorius',
|
||||
],
|
||||
},
|
||||
},
|
||||
]
|
||||
|
||||
WSGI_APPLICATION = 'wsgi.application'
|
||||
|
||||
|
||||
# Database
|
||||
# https://docs.djangoproject.com/en/1.8/ref/settings/#databases
|
||||
|
||||
|
||||
# This uses $DATABASE_URL from the environment variable to create a
|
||||
# django-style-config-dict.
|
||||
# https://github.com/kennethreitz/dj-database-url
|
||||
DATABASES = {
|
||||
'default': dj_database_url.config(conn_max_age=600)
|
||||
}
|
||||
|
||||
# Avoid Django 3.2+ warning
|
||||
# https://github.com/maxking/docker-mailman/issues/595
|
||||
DEFAULT_AUTO_FIELD = 'django.db.models.AutoField'
|
||||
|
||||
|
||||
# If you're behind a proxy, use the X-Forwarded-Host header
|
||||
# See https://docs.djangoproject.com/en/1.8/ref/settings/#use-x-forwarded-host
|
||||
USE_X_FORWARDED_HOST = True
|
||||
|
||||
|
||||
# Password validation
|
||||
# https://docs.djangoproject.com/en/1.9/ref/settings/#auth-password-validators
|
||||
AUTH_PASSWORD_VALIDATORS = [
|
||||
{
|
||||
'NAME':
|
||||
'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
|
||||
},
|
||||
{
|
||||
'NAME':
|
||||
'django.contrib.auth.password_validation.MinimumLengthValidator',
|
||||
},
|
||||
{
|
||||
'NAME':
|
||||
'django.contrib.auth.password_validation.CommonPasswordValidator',
|
||||
},
|
||||
{
|
||||
'NAME':
|
||||
'django.contrib.auth.password_validation.NumericPasswordValidator',
|
||||
},
|
||||
]
|
||||
|
||||
# Internationalization
|
||||
# https://docs.djangoproject.com/en/1.8/topics/i18n/
|
||||
|
||||
LANGUAGE_CODE = 'en-us'
|
||||
|
||||
TIME_ZONE = 'UTC'
|
||||
|
||||
USE_I18N = True
|
||||
|
||||
USE_L10N = True
|
||||
|
||||
USE_TZ = True
|
||||
|
||||
STATIC_ROOT = '/opt/mailman-web-data/static'
|
||||
|
||||
STATIC_URL = '/static/'
|
||||
|
||||
# Additional locations of static files
|
||||
|
||||
|
||||
# List of finder classes that know how to find static files in
|
||||
# various locations.
|
||||
STATICFILES_FINDERS = (
|
||||
'django.contrib.staticfiles.finders.FileSystemFinder',
|
||||
'django.contrib.staticfiles.finders.AppDirectoriesFinder',
|
||||
)
|
||||
|
||||
|
||||
SESSION_SERIALIZER = 'django.contrib.sessions.serializers.PickleSerializer'
|
||||
|
||||
LOGIN_URL = 'account_login'
|
||||
LOGIN_REDIRECT_URL = 'list_index'
|
||||
LOGOUT_URL = 'account_logout'
|
||||
|
||||
|
||||
# Use SERVE_FROM_DOMAIN as the default domain in the email.
|
||||
hostname = os.environ.get('SERVE_FROM_DOMAIN', 'localhost.local')
|
||||
DEFAULT_FROM_EMAIL = 'postorius@{}'.format(hostname)
|
||||
SERVER_EMAIL = 'root@{}'.format(hostname)
|
||||
|
||||
# Change this when you have a real email backend
|
||||
EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
|
||||
EMAIL_HOST = os.environ.get('SMTP_HOST', '')
|
||||
EMAIL_PORT = os.environ.get('SMTP_PORT', 25)
|
||||
EMAIL_HOST_USER = os.environ.get('SMTP_HOST_USER', '')
|
||||
EMAIL_HOST_PASSWORD = os.environ.get('SMTP_HOST_PASSWORD', '')
|
||||
EMAIL_USE_TLS = os.environ.get('SMTP_USE_TLS', False)
|
||||
EMAIL_USE_SSL = os.environ.get('SMTP_USE_SSL', False)
|
||||
|
||||
# Compatibility with Bootstrap 3
|
||||
from django.contrib.messages import constants as messages # flake8: noqa
|
||||
MESSAGE_TAGS = {
|
||||
messages.ERROR: 'danger'
|
||||
}
|
||||
|
||||
|
||||
#
|
||||
# Social auth
|
||||
#
|
||||
AUTHENTICATION_BACKENDS = (
|
||||
'django.contrib.auth.backends.ModelBackend',
|
||||
'allauth.account.auth_backends.AuthenticationBackend',
|
||||
)
|
||||
|
||||
# Django Allauth
|
||||
ACCOUNT_AUTHENTICATION_METHOD = "username_email"
|
||||
ACCOUNT_EMAIL_REQUIRED = True
|
||||
ACCOUNT_EMAIL_VERIFICATION = "mandatory"
|
||||
# You probably want https in production, but this is a dev setup file
|
||||
ACCOUNT_DEFAULT_HTTP_PROTOCOL = "https"
|
||||
ACCOUNT_UNIQUE_EMAIL = True
|
||||
|
||||
SOCIALACCOUNT_PROVIDERS = {
|
||||
'openid': {
|
||||
'SERVERS': [
|
||||
dict(id='yahoo',
|
||||
name='Yahoo',
|
||||
openid_url='http://me.yahoo.com'),
|
||||
],
|
||||
},
|
||||
'google': {
|
||||
'SCOPE': ['profile', 'email'],
|
||||
'AUTH_PARAMS': {'access_type': 'online'},
|
||||
},
|
||||
'facebook': {
|
||||
'METHOD': 'oauth2',
|
||||
'SCOPE': ['email'],
|
||||
'FIELDS': [
|
||||
'email',
|
||||
'name',
|
||||
'first_name',
|
||||
'last_name',
|
||||
'locale',
|
||||
'timezone',
|
||||
],
|
||||
'VERSION': 'v2.4',
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
import sys
|
||||
# A sample logging configuration. The only tangible logging
|
||||
# performed by this configuration is to send an email to
|
||||
# the site admins on every HTTP 500 error when DEBUG=False.
|
||||
# See http://docs.djangoproject.com/en/dev/topics/logging for
|
||||
# more details on how to customize your logging configuration.
|
||||
LOGGING = {
|
||||
'version': 1,
|
||||
'disable_existing_loggers': False,
|
||||
'filters': {
|
||||
'require_debug_false': {
|
||||
'()': 'django.utils.log.RequireDebugFalse'
|
||||
}
|
||||
},
|
||||
'handlers': {
|
||||
'mail_admins': {
|
||||
'level': 'ERROR',
|
||||
'filters': ['require_debug_false'],
|
||||
'class': 'django.utils.log.AdminEmailHandler'
|
||||
},
|
||||
'file':{
|
||||
'level': 'INFO',
|
||||
'class': 'logging.handlers.RotatingFileHandler',
|
||||
#'class': 'logging.handlers.WatchedFileHandler',
|
||||
'filename': os.environ.get('DJANGO_LOG_URL','/opt/mailman-web-data/logs/mailmanweb.log'),
|
||||
'formatter': 'verbose',
|
||||
},
|
||||
'console': {
|
||||
'class': 'logging.StreamHandler',
|
||||
'formatter': 'simple',
|
||||
'level': 'INFO',
|
||||
'stream': sys.stdout,
|
||||
},
|
||||
# TODO: use an environment variable $DJ_LOG_URL to configure the logging
|
||||
# using an environment variable.
|
||||
},
|
||||
'loggers': {
|
||||
'django.request': {
|
||||
'handlers': ['mail_admins', 'file'],
|
||||
'level': 'INFO',
|
||||
'propagate': True,
|
||||
},
|
||||
'django': {
|
||||
'handlers': ['file'],
|
||||
'level': 'INFO',
|
||||
'propagate': True,
|
||||
},
|
||||
'postorius': {
|
||||
'handlers': ['file'],
|
||||
'level': 'INFO',
|
||||
'propagate': True
|
||||
},
|
||||
},
|
||||
'formatters': {
|
||||
'verbose': {
|
||||
'format': '%(levelname)s %(asctime)s %(process)d %(name)s %(message)s'
|
||||
},
|
||||
'simple': {
|
||||
'format': '%(levelname)s %(message)s'
|
||||
},
|
||||
},
|
||||
#'root': {
|
||||
# 'handlers': ['file'],
|
||||
# 'level': 'INFO',
|
||||
#},
|
||||
}
|
||||
|
||||
|
||||
if os.environ.get('LOG_TO_CONSOLE') == 'yes':
|
||||
LOGGING['loggers']['django']['handlers'].append('console')
|
||||
LOGGING['loggers']['django.request']['handlers'].append('console')
|
||||
POSTORIUS_TEMPLATE_BASE_URL = os.environ.get('POSTORIUS_TEMPLATE_BASE_URL', 'http://mailman-web:8000')
|
||||
|
||||
try:
|
||||
from settings_local import *
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
# Compatibility for older installs that override INSTALLED_APPS
|
||||
if not INSTALLED_APPS:
|
||||
INSTALLED_APPS = DEFAULT_APPS + MAILMAN_WEB_SOCIAL_AUTH
|
33
mailman/postorius/mailman-web/urls.py
Normal file
33
mailman/postorius/mailman-web/urls.py
Normal file
@ -0,0 +1,33 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (C) 1998-2016 by the Free Software Foundation, Inc.
|
||||
#
|
||||
# This file is part of Postorius.
|
||||
#
|
||||
# Postorius is free software: you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free
|
||||
# Software Foundation, either version 3 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# Postorius is distributed in the hope that it will be useful, but WITHOUT
|
||||
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
# more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License along with
|
||||
# Postorius. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from django.conf.urls import include
|
||||
from django.contrib import admin
|
||||
from django.urls import re_path, reverse_lazy
|
||||
from django.views.generic import RedirectView
|
||||
|
||||
urlpatterns = [
|
||||
re_path(r'^$', RedirectView.as_view(
|
||||
url=reverse_lazy('list_index'),
|
||||
permanent=True)),
|
||||
re_path(r'postorius/', include('postorius.urls')),
|
||||
re_path(r'', include('django_mailman3.urls')),
|
||||
re_path(r'accounts/', include('allauth.urls')),
|
||||
# Django admin
|
||||
re_path(r'^admin/', admin.site.urls),
|
||||
]
|
25
mailman/postorius/mailman-web/uwsgi.ini
Normal file
25
mailman/postorius/mailman-web/uwsgi.ini
Normal file
@ -0,0 +1,25 @@
|
||||
[uwsgi]
|
||||
# Port on which uwsgi will be listening.
|
||||
uwsgi-socket = 0.0.0.0:8080
|
||||
http-socket = 0.0.0.0:8000
|
||||
|
||||
# Move to the directory where the django files are.
|
||||
chdir = /opt/mailman-web
|
||||
|
||||
# Use the wsgi file provided with the django project.
|
||||
wsgi-file = wsgi.py
|
||||
|
||||
# Setup default number of processes and threads per process.
|
||||
master = true
|
||||
processes = 2
|
||||
threads = 2
|
||||
|
||||
# Drop privileges and don't run as root.
|
||||
uid = mailman
|
||||
gid = mailman
|
||||
|
||||
# Setup the request log.
|
||||
req-logger = file:/opt/mailman-web-data/logs/uwsgi.log
|
||||
|
||||
# Last log and it logs the rest of the stuff.
|
||||
logger = file:/opt/mailman-web-data/logs/uwsgi-error.log
|
38
mailman/postorius/mailman-web/wsgi.py
Executable file
38
mailman/postorius/mailman-web/wsgi.py
Executable file
@ -0,0 +1,38 @@
|
||||
"""
|
||||
WSGI config for HyperKitty project.
|
||||
|
||||
It exposes the WSGI callable as a module-level variable named ``application``.
|
||||
|
||||
For more information on this file, see
|
||||
https://docs.djangoproject.com/en/{{ docs_version }}/howto/deployment/wsgi/
|
||||
"""
|
||||
|
||||
import os
|
||||
|
||||
# import sys
|
||||
# import site
|
||||
|
||||
# For some unknown reason, sometimes mod_wsgi fails to set the python paths to
|
||||
# the virtualenv, with the 'python-path' option. You can do it here too.
|
||||
#
|
||||
# # Remember original sys.path.
|
||||
# prev_sys_path = list(sys.path)
|
||||
# # Add here, for the settings module
|
||||
# site.addsitedir(os.path.abspath(os.path.dirname(__file__)))
|
||||
# # Add the virtualenv
|
||||
# venv = os.path.join(os.path.abspath(os.path.dirname(__file__)),
|
||||
# '..', 'lib', 'python2.6', 'site-packages')
|
||||
# site.addsitedir(venv)
|
||||
# # Reorder sys.path so new directories at the front.
|
||||
# new_sys_path = []
|
||||
# for item in list(sys.path):
|
||||
# if item not in prev_sys_path:
|
||||
# new_sys_path.append(item)
|
||||
# sys.path.remove(item)
|
||||
# sys.path[:0] = new_sys_path
|
||||
|
||||
from django.core.wsgi import get_wsgi_application
|
||||
|
||||
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "settings")
|
||||
|
||||
application = get_wsgi_application()
|
11
mailman/tests/docker-test.yaml
Normal file
11
mailman/tests/docker-test.yaml
Normal file
@ -0,0 +1,11 @@
|
||||
version: '2'
|
||||
|
||||
services:
|
||||
mailman-core:
|
||||
image: maxking/mailman-core:rolling
|
||||
|
||||
mailman-web:
|
||||
image: maxking/mailman-web:rolling
|
||||
environment:
|
||||
- SECRET_KEY=abcdefghijklmnopqrstuv
|
||||
- SERVE_FROM_DOMAIN=araj.me
|
36
mailman/tests/test.sh
Executable file
36
mailman/tests/test.sh
Executable file
@ -0,0 +1,36 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
# If the DB environment variable is not set, use postgres.x
|
||||
if [ "$DB" = "postgres" ] || [ -z $DB ]
|
||||
then
|
||||
docker-compose -f docker-compose.yaml -f tests/docker-test.yaml up -d
|
||||
elif [ "$DB" = "mysql" ]
|
||||
then
|
||||
docker-compose -f docker-compose-mysql.yaml -f tests/docker-test.yaml up -d
|
||||
fi
|
||||
|
||||
# Print the IP Addresses of the Containers.
|
||||
docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' mailman-core
|
||||
docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' mailman-web
|
||||
|
||||
# Make sure all the containers are running.
|
||||
docker-compose ps
|
||||
|
||||
# Sleep for a while and check again if the containers are up.
|
||||
sleep 60
|
||||
docker ps
|
||||
|
||||
# Check if there is anything interesting in the logs.
|
||||
docker logs mailman-web
|
||||
docker logs mailman-core
|
||||
|
||||
|
||||
# Check to see if the core is working as expected.
|
||||
docker exec mailman-core curl -u restadmin:restpass http://mailman-core:8001/3.1/system | grep "GNU Mailman"
|
||||
|
||||
# Check to see if postorius is working.
|
||||
docker exec mailman-web curl -L http://mailman-web:8000/postorius/lists | grep "Mailing List"
|
||||
|
||||
# Check to see if hyperkitty is working.
|
||||
docker exec mailman-web curl -L http://mailman-web:8000/hyperkitty/ | grep "Available lists"
|
54
mailman/web/Dockerfile
Normal file
54
mailman/web/Dockerfile
Normal file
@ -0,0 +1,54 @@
|
||||
# syntax = docker/dockerfile:1.3
|
||||
FROM alpine:3.19.1
|
||||
|
||||
# Add requirements file.
|
||||
COPY requirements.txt /tmp/
|
||||
|
||||
# Install packages and dependencies for postorius and hyperkitty Add user for
|
||||
# executing apps, change ownership for uwsgi+django files and set execution
|
||||
# rights for management script
|
||||
RUN --mount=type=cache,target=/root/.cache \
|
||||
set -ex \
|
||||
&& apk add --no-cache --virtual .build-deps gcc libc-dev linux-headers libldap \
|
||||
postgresql-dev mariadb-dev mariadb-connector-c python3-dev libffi-dev openldap-dev cargo rust \
|
||||
&& apk add --no-cache --virtual .mailman-rundeps bash sassc tzdata \
|
||||
postgresql-client mysql-client py3-mysqlclient curl mailcap gettext \
|
||||
python3 py3-pip xapian-core xapian-bindings-python3 libffi pcre-dev py-cryptography \
|
||||
&& python3 -m pip install --break-system-packages -U 'Django<4.2' pip setuptools wheel \
|
||||
&& pip install --break-system-packages -r /tmp/requirements.txt \
|
||||
whoosh \
|
||||
uwsgi \
|
||||
psycopg2 \
|
||||
dj-database-url \
|
||||
mysqlclient \
|
||||
typing \
|
||||
xapian-haystack \
|
||||
django-auth-ldap \
|
||||
pymemcache \
|
||||
diskcache \
|
||||
django-utils-six \
|
||||
tzdata \
|
||||
&& apk del .build-deps \
|
||||
&& addgroup -S mailman \
|
||||
&& adduser -S -G mailman mailman
|
||||
|
||||
# Add needed files for uwsgi server + settings for django
|
||||
COPY mailman-web /opt/mailman-web
|
||||
# Add startup script to container
|
||||
COPY docker-entrypoint.sh /usr/local/bin/
|
||||
|
||||
RUN chown -R mailman /opt/mailman-web/ \
|
||||
&& chmod u+x /opt/mailman-web/manage.py
|
||||
|
||||
WORKDIR /opt/mailman-web
|
||||
|
||||
# Expose port 8000 for http and port 8080 for uwsgi
|
||||
# (see web/mailman-web/uwsgi.ini#L2-L4)
|
||||
EXPOSE 8000 8080
|
||||
|
||||
# Use stop signal for uwsgi server
|
||||
STOPSIGNAL SIGINT
|
||||
|
||||
ENTRYPOINT ["docker-entrypoint.sh"]
|
||||
|
||||
CMD ["uwsgi", "--ini", "/opt/mailman-web/uwsgi.ini"]
|
61
mailman/web/Dockerfile.dev
Normal file
61
mailman/web/Dockerfile.dev
Normal file
@ -0,0 +1,61 @@
|
||||
# syntax = docker/dockerfile:1.3
|
||||
FROM alpine:3.19.1
|
||||
|
||||
ARG POSTORIUS_REF
|
||||
ARG HYPERKITTY_REF
|
||||
ARG DJ_MM3_REF
|
||||
ARG CLIENT_REF
|
||||
|
||||
# Install packages and dependencies for postorius and hyperkitty Add user for
|
||||
# executing apps, change ownership for uwsgi+django files and set execution
|
||||
# rights for management script
|
||||
RUN --mount=type=cache,target=/root/.cache \
|
||||
set -ex \
|
||||
&& apk add --no-cache --virtual .build-deps gcc libc-dev linux-headers git libldap \
|
||||
postgresql-dev mariadb-dev mariadb-connector-c python3-dev libffi-dev openldap-dev cargo rust \
|
||||
&& apk add --no-cache --virtual .mailman-rundeps bash sassc pcre-dev tzdata \
|
||||
python3 py3-pip postgresql-client mysql-client py3-mysqlclient \
|
||||
curl mailcap xapian-core xapian-bindings-python3 libffi gettext py-cryptography \
|
||||
&& python3 -m pip install --break-system-packages -U pip setuptools wheel \
|
||||
&& python3 -m pip install --break-system-packages -U \
|
||||
git+https://gitlab.com/mailman/mailmanclient \
|
||||
git+https://gitlab.com/mailman/postorius \
|
||||
git+https://gitlab.com/mailman/hyperkitty \
|
||||
whoosh \
|
||||
uwsgi \
|
||||
psycopg2 \
|
||||
dj-database-url \
|
||||
mysqlclient \
|
||||
xapian-haystack \
|
||||
django-auth-ldap \
|
||||
pymemcache \
|
||||
tzdata \
|
||||
diskcache \
|
||||
django-utils-six \
|
||||
&& python3 -m pip install --break-system-packages -U 'Django<4.2' \
|
||||
&& python3 -m pip install --break-system-packages -U \
|
||||
git+https://gitlab.com/mailman/django-mailman3 \
|
||||
&& apk del .build-deps \
|
||||
&& addgroup -S mailman \
|
||||
&& adduser -S -G mailman mailman
|
||||
|
||||
# Add needed files for uwsgi server + settings for django
|
||||
COPY mailman-web /opt/mailman-web
|
||||
# Add startup script to container
|
||||
COPY docker-entrypoint.sh /usr/local/bin/
|
||||
|
||||
RUN chown -R mailman /opt/mailman-web/ \
|
||||
&& chmod u+x /opt/mailman-web/manage.py
|
||||
|
||||
WORKDIR /opt/mailman-web
|
||||
|
||||
# Expose port 8000 for http and port 8080 for uwsgi
|
||||
# (see web/mailman-web/uwsgi.ini#L2-L4)
|
||||
EXPOSE 8000 8080
|
||||
|
||||
# Use stop signal for uwsgi server
|
||||
STOPSIGNAL SIGINT
|
||||
|
||||
ENTRYPOINT ["docker-entrypoint.sh"]
|
||||
|
||||
CMD ["uwsgi", "--ini", "/opt/mailman-web/uwsgi.ini"]
|
161
mailman/web/README.md
Normal file
161
mailman/web/README.md
Normal file
@ -0,0 +1,161 @@
|
||||
# Mailman 3 Web UI
|
||||
|
||||
This image consists of Mailman3's Web UI(Postorius) and Archiver
|
||||
(Hyperkitty). This image is built from latest sources on [gitlab][1]. In future,
|
||||
latest and stable releases will be seperate. I am looking forward to the release
|
||||
of Mailman Suite 3.1 before that.
|
||||
|
||||
## Configuration
|
||||
|
||||
|
||||
These are the settings that you MUST change before deploying:
|
||||
|
||||
- `SERVE_FROM_DOMAIN`: The domain name from which Django will be served. To be
|
||||
added to `ALLOWED_HOSTS` in django settings. Default value is not set. This
|
||||
also replaces Django's default `example.com` SITE and becomes the default SITE
|
||||
(with SITE_ID=1).
|
||||
|
||||
- `HYPERKITTY_API_KEY`: Hyperkitty's API Key, should be set to the same value as
|
||||
set for the mailman-core.
|
||||
|
||||
- `MAILMAN_ADMIN_USER`: The username for the admin user to be created by default.
|
||||
|
||||
- `MAILMAN_ADMIN_EMAIL`: The email for the admin user to be created by default.
|
||||
|
||||
- `SECRET_KEY`: Django's secret key, mainly used for signing cookies and others.
|
||||
|
||||
These are the settings that are set to sane default and you do not need to
|
||||
change them unless you know what you want.
|
||||
|
||||
- `DATABASE_URL`: URL of the type
|
||||
`driver://user:password@hostname:port/databasename` for the django to use. If
|
||||
not set, the default is set to
|
||||
`sqlite:///opt/mailman-web-data/mailmanweb.db`. The standard
|
||||
docker-compose.yaml comes with it set to a postgres database. It is not must
|
||||
to change this if you are happy with PostgreSQL.
|
||||
|
||||
- `MAILMAN_REST_URL`: The URL to the Mailman core's REST API server. Defaut
|
||||
value is `http://mailman-core:8001`.
|
||||
|
||||
- `MAILMAN_REST_USER`: Mailman's REST API username. Default value is `restadmin`
|
||||
|
||||
- `MAILMAN_REST_PASSWORD`: Mailman's REST API user's password. Default value is
|
||||
`restpass`
|
||||
|
||||
- `MAILMAN_HOSTNAME`: IP of the Container from which Mailman will send emails to
|
||||
hyperkitty (django). Set to `mailman-core` by default.
|
||||
|
||||
- `SMTP_HOST`: IP Address/hostname from which you will be sending
|
||||
emails. Default value is the container's gateway retrieved from:
|
||||
/sbin/ip route | awk '/default/ { print $3 }'
|
||||
|
||||
- `SMTP_PORT`: Port used for SMTP. Default is `25`.
|
||||
|
||||
- `SMTP_HOST_USER`: Used for SMTP authentication. Default is an empty string.
|
||||
|
||||
- `SMTP_HOST_PASSWORD`: Default is an empty string.
|
||||
|
||||
- `SMTP_USE_TLS`: Specifies wheather the SMTP connection is encrypted
|
||||
via TLS. Default is `False`. (`EMAIL_USE_TLS`/`EMAIL_USE_SSL` are mutually exclusive, so only set one of those settings.)
|
||||
|
||||
- `SMTP_USE_SSL`: Specifies wheather the SMTP connection is encrypted
|
||||
via SSL. Default is `False`. (EMAIL_USE_TLS/EMAIL_USE_SSL are mutually exclusive, so only set one of those settings.)
|
||||
|
||||
- `DJANGO_LOG_URL`: Path to the django's log file. Defaults to
|
||||
`/opt/mailman-web-data/logs/mailmanweb.log`.
|
||||
|
||||
- `DJANGO_ALLOWED_HOSTS`: Entry to add to ALLOWED_HOSTS in Django
|
||||
configuration. Format as comma-separated list (no whitespace). This is a separate configuration from`SERVE_FROM_DOMAIN` as
|
||||
latter is used for other purposes too.
|
||||
|
||||
- `POSTORIUS_TEMPLATE_BASE_URL`: The base url at which the `mailman-web`
|
||||
container can be reached from `mailman-core` container. This is set to
|
||||
`http://mailman-web:8000` by default so that Core can fetch templates from
|
||||
Web.
|
||||
|
||||
- `DISKCACHE_PATH` and `DISKCACHE_SIZE`: Django Diskcache location path and
|
||||
size respectively. Defaults are `/opt/mailman-web-data/diskcache` and 1G.
|
||||
|
||||
[1]: https://github.com/maxking/docker-mailman/blob/master/web/mailman-web/settings.py
|
||||
|
||||
## Social Auth
|
||||
|
||||
In order to separate `INSTALLED_APPS` from the social authentication plugins a new settings `MAILMAN_WEB_SOCIAL_AUTH` is created. This includes all the enabled social auth plugins.
|
||||
|
||||
### Disable social auth
|
||||
|
||||
In order to disable social auth, you can add the following to your
|
||||
settings_local.py
|
||||
|
||||
```python
|
||||
MAILMAN_WEB_SOCIAL_AUTH = []
|
||||
```
|
||||
|
||||
In older versions of continer images (0.3.*), you had to override
|
||||
`INSTALLED_APPS` in order to disable social auth, but addition of
|
||||
this new setting will make it easier to disable social auth making
|
||||
sure that you get any updates to the django apps that are added in
|
||||
future.
|
||||
|
||||
The default behavior will remain the same as 0.3 release if you
|
||||
have not overriden `INSTALLED_APPS` though.
|
||||
|
||||
## Running
|
||||
|
||||
It is highly recommended that you run this using the [docker-compose.yaml][2]
|
||||
provided in the [github repo][3] of this project. You will need to proxy the
|
||||
requests the container that you create with this image using an actual web
|
||||
server like Nginx. The [github repo][3] provides the setup instructions for
|
||||
Nginx.
|
||||
|
||||
Since the setup has `USE_SSL` set to `True` in django's `settings.py`, you may
|
||||
also want to get a SSL certificate if you don't already have one. [Lets
|
||||
Encrypt][4] provides free SSL certiticates for everyone and there are _some_
|
||||
instructions about that also.
|
||||
|
||||
After the first run, you can create a superuser for django using the following
|
||||
command:
|
||||
|
||||
```bash
|
||||
$ docker exec -it mailman-web python3 manage.py createsuperuser
|
||||
```
|
||||
|
||||
## Django management commands
|
||||
|
||||
In order to run Django management commands in the `mailman-web` container, you
|
||||
can run following:
|
||||
|
||||
```bash
|
||||
$ docker exec -it mailman-web python3 manage.py <command>
|
||||
```
|
||||
|
||||
And replace `<command>` with the appropriate management command.
|
||||
|
||||
|
||||
## Importing Archives from Mailman 2
|
||||
|
||||
In order to import archvies from Mailman 2, you need to get the `listname.mbox`
|
||||
file in a location that is readable inside `mailman-web` container.
|
||||
|
||||
Please place `listname.mbox` file at `/opt/mailman/web` **on the host**. Verify
|
||||
that the file is present inside the `mailman-web` contianer by running:
|
||||
|
||||
```bash
|
||||
$ docker exec -it mailman-web ls /opt/mailman-web-data
|
||||
```
|
||||
And verify that you can see `listname.mbox` in the `ls` output above. After you
|
||||
have verified that, you can then run the `hyperkitty_import` command to do the
|
||||
actual import:
|
||||
|
||||
```bash
|
||||
$ docker exec -it mailman-web python3 manage.py hyperkitty_import -l listname@domain /opt/mailman-web-data/listname.mbox
|
||||
```
|
||||
|
||||
This should take some time to import depending on how many emails are in the
|
||||
archives.
|
||||
|
||||
|
||||
[1]: https://gitlab.com/mailman
|
||||
[3]: https://github.com/maxking/docker-mailman/
|
||||
[2]: https://github.com/maxking/docker-mailman/blob/master/docker-compose.yaml
|
||||
[4]: https://letsencrypt.org
|
1
mailman/web/VERSION
Normal file
1
mailman/web/VERSION
Normal file
@ -0,0 +1 @@
|
||||
0.1.1
|
154
mailman/web/docker-entrypoint.sh
Executable file
154
mailman/web/docker-entrypoint.sh
Executable file
@ -0,0 +1,154 @@
|
||||
#! /bin/bash
|
||||
set -e
|
||||
|
||||
|
||||
function wait_for_postgres () {
|
||||
# Check if the postgres database is up and accepting connections before
|
||||
# moving forward.
|
||||
# TODO: Use python's psycopg2 module to do this in python instead of
|
||||
# installing postgres-client in the image.
|
||||
until psql -P pager=off $DATABASE_URL -c '\l'; do
|
||||
>&2 echo "Postgres is unavailable - sleeping"
|
||||
sleep 1
|
||||
done
|
||||
>&2 echo "Postgres is up - continuing"
|
||||
}
|
||||
|
||||
function wait_for_mysql () {
|
||||
# Check if MySQL is up and accepting connections.
|
||||
readarray -d' ' -t ENDPOINT <<< $(python3 -c "from urllib.parse import urlparse; o = urlparse('$DATABASE_URL'); print('%s %s' % (o.hostname, o.port if o.port else '3306'));")
|
||||
until mysqladmin ping --host ${ENDPOINT[0]} --port ${ENDPOINT[1]} --silent; do
|
||||
>&2 echo "MySQL is unavailable - sleeping"
|
||||
sleep 1
|
||||
done
|
||||
>&2 echo "MySQL is up - continuing"
|
||||
}
|
||||
|
||||
function check_or_create () {
|
||||
# Check if the path exists, if not, create the directory.
|
||||
if [[ ! -e dir ]]; then
|
||||
echo "$1 does not exist, creating ..."
|
||||
mkdir "$1"
|
||||
fi
|
||||
}
|
||||
|
||||
# function postgres_ready(){
|
||||
# python << END
|
||||
# import sys
|
||||
# import psycopg2
|
||||
# try:
|
||||
# conn = psycopg2.connect(dbname="$POSTGRES_DB", user="$POSTGRES_USER", password="$POSTGRES_PASSWORD", host="postgres")
|
||||
# except psycopg2.OperationalError:
|
||||
# sys.exit(-1)
|
||||
# sys.exit(0)
|
||||
# END
|
||||
# }
|
||||
|
||||
# SMTP_HOST defaults to the gateway
|
||||
if [[ ! -v SMTP_HOST ]]; then
|
||||
export SMTP_HOST=$(/sbin/ip route | awk '/default/ { print $3 }')
|
||||
fi
|
||||
|
||||
# Check if $SECRET_KEY is defined, if not, bail out.
|
||||
if [[ ! -v SECRET_KEY ]]; then
|
||||
echo "SECRET_KEY is not defined. Aborting."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check if $DATABASE_URL is defined, if not, use a standard sqlite database.
|
||||
#
|
||||
# If the $DATABASE_URL is defined and is postgres, check if it is available
|
||||
# yet. Do not start the container before the postgresql boots up.
|
||||
#
|
||||
# If the $DATABASE_URL is defined and is mysql, check if the database is
|
||||
# available before the container boots up.
|
||||
#
|
||||
# TODO: Check the database type and detect if it is up based on that. For now,
|
||||
# assume that postgres is being used if DATABASE_URL is defined.
|
||||
|
||||
if [[ ! -v DATABASE_URL ]]; then
|
||||
echo "DATABASE_URL is not defined. Using sqlite database..."
|
||||
export DATABASE_URL=sqlite://mailmanweb.db
|
||||
export DATABASE_TYPE='sqlite'
|
||||
fi
|
||||
|
||||
if [[ "$DATABASE_TYPE" = 'postgres' ]]
|
||||
then
|
||||
wait_for_postgres
|
||||
elif [[ "$DATABASE_TYPE" = 'mysql' ]]
|
||||
then
|
||||
wait_for_mysql
|
||||
fi
|
||||
|
||||
# Check if we are in the correct directory before running commands.
|
||||
if [[ ! $(pwd) == '/opt/mailman-web' ]]; then
|
||||
echo "Running in the wrong directory...switching to /opt/mailman-web"
|
||||
cd /opt/mailman-web
|
||||
fi
|
||||
|
||||
# Check if the logs directory is setup.
|
||||
if [[ ! -e /opt/mailman-web-data/logs/mailmanweb.log ]]; then
|
||||
echo "Creating log file for mailman web"
|
||||
mkdir -p /opt/mailman-web-data/logs/
|
||||
touch /opt/mailman-web-data/logs/mailmanweb.log
|
||||
fi
|
||||
|
||||
if [[ ! -e /opt/mailman-web-data/logs/uwsgi.log ]]; then
|
||||
echo "Creating log file for uwsgi.."
|
||||
touch /opt/mailman-web-data/logs/uwsgi.log
|
||||
fi
|
||||
|
||||
# Check if the settings_local.py file exists, if yes, copy it too.
|
||||
if [[ -e /opt/mailman-web-data/settings_local.py ]]; then
|
||||
echo "Copying settings_local.py ..."
|
||||
cp /opt/mailman-web-data/settings_local.py /opt/mailman-web/settings_local.py
|
||||
chown mailman:mailman /opt/mailman-web/settings_local.py
|
||||
else
|
||||
echo "settings_local.py not found, it is highly recommended that you provide one"
|
||||
echo "Using default configuration to run."
|
||||
fi
|
||||
|
||||
# Collect static for the django installation.
|
||||
python3 manage.py collectstatic --noinput --clear --verbosity 0
|
||||
|
||||
|
||||
# Compile all the installed po files to mo.
|
||||
SITE_DIR=$(python3 -c 'import site; print(site.getsitepackages()[0])')
|
||||
echo "Compiling locale files in $SITE_DIR"
|
||||
cd $SITE_DIR && /opt/mailman-web/manage.py compilemessages && cd -
|
||||
|
||||
# Compress static files.
|
||||
python3 /opt/mailman-web/manage.py compress --force
|
||||
|
||||
|
||||
# Migrate all the data to the database if this is a new installation, otherwise
|
||||
# this command will upgrade the database.
|
||||
python3 /opt/mailman-web/manage.py migrate
|
||||
|
||||
# If MAILMAN_ADMIN_USER and MAILMAN_ADMIN_EMAIL is defined create a new
|
||||
# superuser for Django. There is no password setup so it can't login yet unless
|
||||
# the password is reset.
|
||||
if [[ -v MAILMAN_ADMIN_USER ]] && [[ -v MAILMAN_ADMIN_EMAIL ]];
|
||||
then
|
||||
echo "Creating admin user $MAILMAN_ADMIN_USER ..."
|
||||
python3 /opt/mailman-web/manage.py createsuperuser --noinput --username "$MAILMAN_ADMIN_USER"\
|
||||
--email "$MAILMAN_ADMIN_EMAIL" 2> /dev/null || \
|
||||
echo "Superuser $MAILMAN_ADMIN_USER already exists"
|
||||
fi
|
||||
|
||||
# If SERVE_FROM_DOMAIN is defined then rename the default `example.com`
|
||||
# domain to the defined domain.
|
||||
if [[ -v SERVE_FROM_DOMAIN ]];
|
||||
then
|
||||
echo "Setting $SERVE_FROM_DOMAIN as the default domain ..."
|
||||
python3 /opt/mailman-web/manage.py shell -c \
|
||||
"from django.contrib.sites.models import Site; Site.objects.filter(domain='example.com').update(domain='$SERVE_FROM_DOMAIN', name='$SERVE_FROM_DOMAIN')"
|
||||
fi
|
||||
|
||||
# Create a mailman user with the specific UID and GID and do not create home
|
||||
# directory for it. Also chown the logs directory to write the files.
|
||||
chown mailman:mailman /opt/mailman-web-data -R
|
||||
|
||||
[[ -v DISKCACHE_PATH ]] && chown mailman:mailman "${DISKCACHE_PATH}" -R
|
||||
|
||||
exec $@
|
0
mailman/web/mailman-web/__init__.py
Normal file
0
mailman/web/mailman-web/__init__.py
Normal file
10
mailman/web/mailman-web/manage.py
Executable file
10
mailman/web/mailman-web/manage.py
Executable file
@ -0,0 +1,10 @@
|
||||
#!/usr/bin/env python3
|
||||
import os
|
||||
import sys
|
||||
|
||||
if __name__ == "__main__":
|
||||
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "settings")
|
||||
|
||||
from django.core.management import execute_from_command_line
|
||||
|
||||
execute_from_command_line(sys.argv)
|
426
mailman/web/mailman-web/settings.py
Normal file
426
mailman/web/mailman-web/settings.py
Normal file
@ -0,0 +1,426 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (C) 1998-2016 by the Free Software Foundation, Inc.
|
||||
#
|
||||
# This file is part of Mailman Suite.
|
||||
#
|
||||
# Mailman Suite is free sofware: you can redistribute it and/or modify it
|
||||
# under the terms of the GNU General Public License as published by the Free
|
||||
# Software Foundation, either version 3 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# Mailman Suite is distributed in the hope that it will be useful, but
|
||||
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
# for more details.
|
||||
|
||||
# You should have received a copy of the GNU General Public License along
|
||||
# with Mailman Suite. If not, see <http://www.gnu.org/licenses/>.
|
||||
"""
|
||||
Django Settings for Mailman Suite (hyperkitty + postorius)
|
||||
|
||||
For more information on this file, see
|
||||
https://docs.djangoproject.com/en/1.8/topics/settings/
|
||||
|
||||
For the full list of settings and their values, see
|
||||
https://docs.djangoproject.com/en/1.8/ref/settings/
|
||||
"""
|
||||
|
||||
# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
|
||||
import os
|
||||
import dj_database_url
|
||||
import sys
|
||||
from socket import gethostbyname, gaierror
|
||||
|
||||
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
|
||||
|
||||
# SECURITY WARNING: keep the secret key used in production secret!
|
||||
SECRET_KEY = os.environ.get('SECRET_KEY')
|
||||
|
||||
# SECURITY WARNING: don't run with debug turned on in production!
|
||||
DEBUG = False
|
||||
|
||||
ADMINS = (
|
||||
('Mailman Suite Admin', 'james_mailman@ketrenos.com'),
|
||||
)
|
||||
|
||||
SITE_ID = 1
|
||||
|
||||
# Hosts/domain names that are valid for this site; required if DEBUG is False
|
||||
# See https://docs.djangoproject.com/en/3.1/ref/settings/#allowed-hosts
|
||||
ALLOWED_HOSTS = [
|
||||
"127.0.0.1",
|
||||
"localhost", # Archiving API from Mailman, keep it.
|
||||
"mailman-web",
|
||||
"ketrenet-mailman-web",
|
||||
os.environ.get('SERVE_FROM_DOMAIN'),
|
||||
]
|
||||
|
||||
try:
|
||||
ALLOWED_HOSTS.append(gethostbyname("ketrenet-mailman-web")) # only add if this resolves
|
||||
except gaierror:
|
||||
pass
|
||||
|
||||
ALLOWED_HOSTS.extend(os.getenv("DJANGO_ALLOWED_HOSTS", "").split(","))
|
||||
|
||||
# Mailman API credentials
|
||||
MAILMAN_REST_API_URL = os.environ.get('MAILMAN_REST_URL', 'http://ketrenet-mailman-core:8001')
|
||||
MAILMAN_REST_API_USER = os.environ.get('MAILMAN_REST_USER', 'restadmin')
|
||||
MAILMAN_REST_API_PASS = os.environ.get('MAILMAN_REST_PASSWORD', 'restpass')
|
||||
MAILMAN_ARCHIVER_KEY = os.environ.get('HYPERKITTY_API_KEY')
|
||||
MAILMAN_ARCHIVER_FROM = (os.environ.get('MAILMAN_HOST_IP', gethostbyname(os.environ.get('MAILMAN_HOSTNAME', 'ketrenet-mailman-core'))),)
|
||||
|
||||
# Application definition
|
||||
|
||||
INSTALLED_APPS = []
|
||||
DEFAULT_APPS = [
|
||||
'hyperkitty',
|
||||
'postorius',
|
||||
'django_mailman3',
|
||||
# Uncomment the next line to enable the admin:
|
||||
'django.contrib.admin',
|
||||
# Uncomment the next line to enable admin documentation:
|
||||
# 'django.contrib.admindocs',
|
||||
'django.contrib.auth',
|
||||
'django.contrib.contenttypes',
|
||||
'django.contrib.sessions',
|
||||
'django.contrib.sites',
|
||||
'django.contrib.messages',
|
||||
'django.contrib.staticfiles',
|
||||
'django.contrib.humanize',
|
||||
'rest_framework',
|
||||
'django_gravatar',
|
||||
'compressor',
|
||||
'haystack',
|
||||
'django_extensions',
|
||||
'django_q',
|
||||
'allauth',
|
||||
'allauth.account',
|
||||
'allauth.socialaccount',
|
||||
]
|
||||
|
||||
MAILMAN_WEB_SOCIAL_AUTH = [
|
||||
'django_mailman3.lib.auth.fedora',
|
||||
'allauth.socialaccount.providers.openid',
|
||||
'allauth.socialaccount.providers.github',
|
||||
'allauth.socialaccount.providers.gitlab',
|
||||
'allauth.socialaccount.providers.google',
|
||||
]
|
||||
|
||||
MIDDLEWARE = (
|
||||
'django.contrib.sessions.middleware.SessionMiddleware',
|
||||
'django.middleware.common.CommonMiddleware',
|
||||
'django.middleware.csrf.CsrfViewMiddleware',
|
||||
'django.middleware.locale.LocaleMiddleware',
|
||||
'django.contrib.auth.middleware.AuthenticationMiddleware',
|
||||
'django.contrib.messages.middleware.MessageMiddleware',
|
||||
'django.middleware.clickjacking.XFrameOptionsMiddleware',
|
||||
'django.middleware.security.SecurityMiddleware',
|
||||
'allauth.account.middleware.AccountMiddleware',
|
||||
'django_mailman3.middleware.TimezoneMiddleware',
|
||||
'postorius.middleware.PostoriusMiddleware',
|
||||
)
|
||||
|
||||
ROOT_URLCONF = 'urls'
|
||||
|
||||
TEMPLATES = [
|
||||
{
|
||||
'BACKEND': 'django.template.backends.django.DjangoTemplates',
|
||||
'DIRS': [],
|
||||
'APP_DIRS': True,
|
||||
'OPTIONS': {
|
||||
'context_processors': [
|
||||
'django.template.context_processors.debug',
|
||||
'django.template.context_processors.i18n',
|
||||
'django.template.context_processors.media',
|
||||
'django.template.context_processors.static',
|
||||
'django.template.context_processors.tz',
|
||||
'django.template.context_processors.csrf',
|
||||
'django.template.context_processors.request',
|
||||
'django.contrib.auth.context_processors.auth',
|
||||
'django.contrib.messages.context_processors.messages',
|
||||
'django_mailman3.context_processors.common',
|
||||
'hyperkitty.context_processors.common',
|
||||
'postorius.context_processors.postorius',
|
||||
],
|
||||
},
|
||||
},
|
||||
]
|
||||
|
||||
WSGI_APPLICATION = 'wsgi.application'
|
||||
|
||||
|
||||
# Database
|
||||
# https://docs.djangoproject.com/en/1.8/ref/settings/#databases
|
||||
# dj_database_url uses $DATABASE_URL environment variable to create a
|
||||
# django-style-config-dict.
|
||||
# https://github.com/kennethreitz/dj-database-url
|
||||
DATABASES = {
|
||||
'default': dj_database_url.config(conn_max_age=600)
|
||||
}
|
||||
|
||||
# Avoid Django 3.2+ warning
|
||||
# https://github.com/maxking/docker-mailman/issues/595
|
||||
DEFAULT_AUTO_FIELD = 'django.db.models.AutoField'
|
||||
|
||||
|
||||
# If you're behind a proxy, use the X-Forwarded-Host header
|
||||
# See https://docs.djangoproject.com/en/1.8/ref/settings/#use-x-forwarded-host
|
||||
USE_X_FORWARDED_HOST = True
|
||||
|
||||
|
||||
# Password validation
|
||||
# https://docs.djangoproject.com/en/1.9/ref/settings/#auth-password-validators
|
||||
AUTH_PASSWORD_VALIDATORS = [
|
||||
{
|
||||
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
|
||||
},
|
||||
{
|
||||
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
|
||||
},
|
||||
{
|
||||
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
|
||||
},
|
||||
{
|
||||
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
|
||||
},
|
||||
]
|
||||
|
||||
# Internationalization
|
||||
# https://docs.djangoproject.com/en/1.8/topics/i18n/
|
||||
|
||||
LANGUAGE_CODE = 'en-us'
|
||||
|
||||
TIME_ZONE = os.environ.get('TZ', 'UTC')
|
||||
|
||||
USE_I18N = True
|
||||
|
||||
USE_L10N = True
|
||||
|
||||
USE_TZ = True
|
||||
|
||||
STATIC_ROOT = '/opt/mailman-web-data/static'
|
||||
|
||||
STATIC_URL = '/mailman/static/'
|
||||
|
||||
# Additional locations of static files
|
||||
|
||||
|
||||
# List of finder classes that know how to find static files in
|
||||
# various locations.
|
||||
STATICFILES_FINDERS = (
|
||||
'django.contrib.staticfiles.finders.FileSystemFinder',
|
||||
'django.contrib.staticfiles.finders.AppDirectoriesFinder',
|
||||
'compressor.finders.CompressorFinder',
|
||||
)
|
||||
|
||||
|
||||
SESSION_SERIALIZER = 'django.contrib.sessions.serializers.PickleSerializer'
|
||||
|
||||
LOGIN_URL = 'account_login'
|
||||
LOGIN_REDIRECT_URL = 'list_index'
|
||||
LOGOUT_URL = 'account_logout'
|
||||
|
||||
|
||||
# Use SERVE_FROM_DOMAIN as the default domain in the email.
|
||||
hostname = os.environ.get('SERVE_FROM_DOMAIN', 'localhost.local')
|
||||
DEFAULT_FROM_EMAIL = 'postorius@{}'.format(hostname)
|
||||
SERVER_EMAIL = 'root@{}'.format(hostname)
|
||||
|
||||
# Change this when you have a real email backend
|
||||
EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
|
||||
EMAIL_HOST = os.environ.get('SMTP_HOST', '')
|
||||
EMAIL_PORT = os.environ.get('SMTP_PORT', 25)
|
||||
EMAIL_HOST_USER = os.environ.get('SMTP_HOST_USER', '')
|
||||
EMAIL_HOST_PASSWORD = os.environ.get('SMTP_HOST_PASSWORD', '')
|
||||
EMAIL_USE_TLS = os.environ.get('SMTP_USE_TLS', False)
|
||||
EMAIL_USE_SSL = os.environ.get('SMTP_USE_SSL', False)
|
||||
|
||||
# Compatibility with Bootstrap 3
|
||||
from django.contrib.messages import constants as messages # flake8: noqa
|
||||
MESSAGE_TAGS = {
|
||||
messages.ERROR: 'danger'
|
||||
}
|
||||
|
||||
|
||||
#
|
||||
# Social auth
|
||||
#
|
||||
AUTHENTICATION_BACKENDS = (
|
||||
'django.contrib.auth.backends.ModelBackend',
|
||||
'allauth.account.auth_backends.AuthenticationBackend',
|
||||
)
|
||||
|
||||
# Django Allauth
|
||||
ACCOUNT_AUTHENTICATION_METHOD = "username_email"
|
||||
ACCOUNT_EMAIL_REQUIRED = True
|
||||
ACCOUNT_EMAIL_VERIFICATION = "mandatory"
|
||||
# You probably want https in production, but this is a dev setup file
|
||||
ACCOUNT_DEFAULT_HTTP_PROTOCOL = "https"
|
||||
ACCOUNT_UNIQUE_EMAIL = True
|
||||
|
||||
SOCIALACCOUNT_PROVIDERS = {
|
||||
'openid': {
|
||||
'SERVERS': [
|
||||
dict(id='yahoo',
|
||||
name='Yahoo',
|
||||
openid_url='http://me.yahoo.com'),
|
||||
],
|
||||
},
|
||||
'google': {
|
||||
'SCOPE': ['profile', 'email'],
|
||||
'AUTH_PARAMS': {'access_type': 'online'},
|
||||
},
|
||||
'facebook': {
|
||||
'METHOD': 'oauth2',
|
||||
'SCOPE': ['email'],
|
||||
'FIELDS': [
|
||||
'email',
|
||||
'name',
|
||||
'first_name',
|
||||
'last_name',
|
||||
'locale',
|
||||
'timezone',
|
||||
],
|
||||
'VERSION': 'v2.4',
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
# django-compressor
|
||||
# https://pypi.python.org/pypi/django_compressor
|
||||
#
|
||||
COMPRESS_PRECOMPILERS = (
|
||||
('text/less', 'lessc {infile} {outfile}'),
|
||||
('text/x-scss', 'sassc -t compressed {infile} {outfile}'),
|
||||
('text/x-sass', 'sassc -t compressed {infile} {outfile}'),
|
||||
)
|
||||
|
||||
# On a production setup, setting COMPRESS_OFFLINE to True will bring a
|
||||
# significant performance improvement, as CSS files will not need to be
|
||||
# recompiled on each requests. It means running an additional "compress"
|
||||
# management command after each code upgrade.
|
||||
# http://django-compressor.readthedocs.io/en/latest/usage/#offline-compression
|
||||
# COMPRESS_OFFLINE = True
|
||||
|
||||
#
|
||||
# Full-text search engine
|
||||
#
|
||||
HAYSTACK_CONNECTIONS = {
|
||||
'default': {
|
||||
'ENGINE': 'haystack.backends.whoosh_backend.WhooshEngine',
|
||||
'PATH': "/opt/mailman-web-data/fulltext_index",
|
||||
# You can also use the Xapian engine, it's faster and more accurate,
|
||||
# but requires another library.
|
||||
# http://django-haystack.readthedocs.io/en/v2.4.1/installing_search_engines.html#xapian
|
||||
# Example configuration for Xapian:
|
||||
#'ENGINE': 'xapian_backend.XapianEngine'
|
||||
},
|
||||
}
|
||||
|
||||
import sys
|
||||
# A sample logging configuration. The only tangible logging
|
||||
# performed by this configuration is to send an email to
|
||||
# the site admins on every HTTP 500 error when DEBUG=False.
|
||||
# See http://docs.djangoproject.com/en/dev/topics/logging for
|
||||
# more details on how to customize your logging configuration.
|
||||
LOGGING = {
|
||||
'version': 1,
|
||||
'disable_existing_loggers': False,
|
||||
'filters': {
|
||||
'require_debug_false': {
|
||||
'()': 'django.utils.log.RequireDebugFalse'
|
||||
}
|
||||
},
|
||||
'handlers': {
|
||||
'mail_admins': {
|
||||
'level': 'ERROR',
|
||||
'filters': ['require_debug_false'],
|
||||
'class': 'django.utils.log.AdminEmailHandler'
|
||||
},
|
||||
'file':{
|
||||
'level': 'INFO',
|
||||
'class': 'logging.handlers.RotatingFileHandler',
|
||||
#'class': 'logging.handlers.WatchedFileHandler',
|
||||
'filename': os.environ.get('DJANGO_LOG_URL','/opt/mailman-web-data/logs/mailmanweb.log'),
|
||||
'formatter': 'verbose',
|
||||
},
|
||||
'console': {
|
||||
'class': 'logging.StreamHandler',
|
||||
'formatter': 'simple',
|
||||
'level': 'INFO',
|
||||
'stream': sys.stdout,
|
||||
},
|
||||
},
|
||||
'loggers': {
|
||||
'django.request': {
|
||||
'handlers': ['mail_admins', 'file'],
|
||||
'level': 'INFO',
|
||||
'propagate': True,
|
||||
},
|
||||
'django': {
|
||||
'handlers': ['file'],
|
||||
'level': 'INFO',
|
||||
'propagate': True,
|
||||
},
|
||||
'hyperkitty': {
|
||||
'handlers': ['file'],
|
||||
'level': 'INFO',
|
||||
'propagate': True,
|
||||
},
|
||||
'postorius': {
|
||||
'handlers': ['file'],
|
||||
'level': 'INFO',
|
||||
'propagate': True
|
||||
},
|
||||
},
|
||||
'formatters': {
|
||||
'verbose': {
|
||||
'format': '%(levelname)s %(asctime)s %(process)d %(name)s %(message)s'
|
||||
},
|
||||
'simple': {
|
||||
'format': '%(levelname)s %(message)s'
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
if os.environ.get('LOG_TO_CONSOLE') == 'yes':
|
||||
LOGGING['loggers']['django']['handlers'].append('console')
|
||||
LOGGING['loggers']['django.request']['handlers'].append('console')
|
||||
|
||||
# HyperKitty-specific
|
||||
#
|
||||
# Only display mailing-lists from the same virtual host as the webserver
|
||||
FILTER_VHOST = False
|
||||
|
||||
|
||||
Q_CLUSTER = {
|
||||
'timeout': 300,
|
||||
'retry': 300,
|
||||
'save_limit': 100,
|
||||
'orm': 'default',
|
||||
}
|
||||
|
||||
POSTORIUS_TEMPLATE_BASE_URL = os.environ.get('POSTORIUS_TEMPLATE_BASE_URL', 'http://ketrenet-mailman-web:8000')
|
||||
|
||||
DISKCACHE_PATH = os.environ.get('DISKCACHE_PATH', '/opt/mailman-web-data/diskcache')
|
||||
DISKCACHE_SIZE = os.environ.get('DISKCACHE_SIZE', 2 ** 30) # 1 gigabyte
|
||||
|
||||
CACHES = {
|
||||
'default': {
|
||||
'BACKEND': 'diskcache.DjangoCache',
|
||||
'LOCATION': DISKCACHE_PATH,
|
||||
'OPTIONS': {
|
||||
'size_limit': DISKCACHE_SIZE,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
try:
|
||||
from settings_local import *
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
# Compatibility for older installs that override INSTALLED_APPS
|
||||
if not INSTALLED_APPS:
|
||||
INSTALLED_APPS = DEFAULT_APPS + MAILMAN_WEB_SOCIAL_AUTH
|
34
mailman/web/mailman-web/urls.py
Normal file
34
mailman/web/mailman-web/urls.py
Normal file
@ -0,0 +1,34 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (C) 1998-2016 by the Free Software Foundation, Inc.
|
||||
#
|
||||
# This file is part of Postorius.
|
||||
#
|
||||
# Postorius is free software: you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free
|
||||
# Software Foundation, either version 3 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# Postorius is distributed in the hope that it will be useful, but WITHOUT
|
||||
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
# more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License along with
|
||||
# Postorius. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from django.conf.urls import include
|
||||
from django.contrib import admin
|
||||
from django.urls import path, reverse_lazy
|
||||
from django.views.generic import RedirectView
|
||||
|
||||
urlpatterns = [
|
||||
path(r'', RedirectView.as_view(
|
||||
url=reverse_lazy('list_index'),
|
||||
permanent=True)),
|
||||
path(r'postorius/', include('postorius.urls')),
|
||||
path(r'hyperkitty/', include('hyperkitty.urls')),
|
||||
path(r'', include('django_mailman3.urls')),
|
||||
path(r'accounts/', include('allauth.urls')),
|
||||
# Django admin
|
||||
path(r'admin/', admin.site.urls),
|
||||
]
|
53
mailman/web/mailman-web/uwsgi.ini
Normal file
53
mailman/web/mailman-web/uwsgi.ini
Normal file
@ -0,0 +1,53 @@
|
||||
[uwsgi]
|
||||
# Port on which uwsgi will be listening.
|
||||
uwsgi-socket = 0.0.0.0:8080
|
||||
http-socket = 0.0.0.0:8000
|
||||
|
||||
# Enable threading for python
|
||||
enable-threads = true
|
||||
|
||||
# Setting uwsgi buffer size to what Apache2 supports.
|
||||
buffer-size = 8190
|
||||
|
||||
# Move to the directory where the django files are.
|
||||
chdir = /opt/mailman-web
|
||||
|
||||
# Use the wsgi file provided with the django project.
|
||||
wsgi-file = wsgi.py
|
||||
|
||||
# Setup default number of processes and threads per process.
|
||||
master = true
|
||||
processes = 2
|
||||
threads = 2
|
||||
|
||||
# Drop privileges and don't run as root.
|
||||
uid = mailman
|
||||
gid = mailman
|
||||
|
||||
# Setup the django_q related worker processes.
|
||||
attach-daemon = ./manage.py qcluster
|
||||
|
||||
# Setup hyperkitty's cron jobs.
|
||||
# 'minutely' jobs are run hourly for perf reasons.
|
||||
# See https://github.com/maxking/docker-mailman/issues/327
|
||||
unique-cron = 0 -1 -1 -1 -1 ./manage.py runjobs minutely
|
||||
unique-cron = -15 -1 -1 -1 -1 ./manage.py runjobs quarter_hourly
|
||||
unique-cron = 0 -1 -1 -1 -1 ./manage.py runjobs hourly
|
||||
unique-cron = 0 0 -1 -1 -1 ./manage.py runjobs daily
|
||||
unique-cron = 0 0 1 -1 -1 ./manage.py runjobs monthly
|
||||
unique-cron = 0 0 -1 -1 0 ./manage.py runjobs weekly
|
||||
unique-cron = 0 0 1 1 -1 ./manage.py runjobs yearly
|
||||
|
||||
# Setup the request log.
|
||||
req-logger = file:/opt/mailman-web-data/logs/uwsgi.log
|
||||
|
||||
# Log cron seperately.
|
||||
logger = cron file:/opt/mailman-web-data/logs/uwsgi-cron.log
|
||||
log-route = cron uwsgi-cron
|
||||
|
||||
# Log qcluster commands seperately.
|
||||
logger = qcluster file:/opt/mailman-web-data/logs/uwsgi-qcluster.log
|
||||
log-route = qcluster uwsgi-daemons
|
||||
|
||||
# Last log and it logs the rest of the stuff.
|
||||
logger = file:/opt/mailman-web-data/logs/uwsgi-error.log
|
38
mailman/web/mailman-web/wsgi.py
Executable file
38
mailman/web/mailman-web/wsgi.py
Executable file
@ -0,0 +1,38 @@
|
||||
"""
|
||||
WSGI config for HyperKitty project.
|
||||
|
||||
It exposes the WSGI callable as a module-level variable named ``application``.
|
||||
|
||||
For more information on this file, see
|
||||
https://docs.djangoproject.com/en/{{ docs_version }}/howto/deployment/wsgi/
|
||||
"""
|
||||
|
||||
import os
|
||||
|
||||
# import sys
|
||||
# import site
|
||||
|
||||
# For some unknown reason, sometimes mod_wsgi fails to set the python paths to
|
||||
# the virtualenv, with the 'python-path' option. You can do it here too.
|
||||
#
|
||||
# # Remember original sys.path.
|
||||
# prev_sys_path = list(sys.path)
|
||||
# # Add here, for the settings module
|
||||
# site.addsitedir(os.path.abspath(os.path.dirname(__file__)))
|
||||
# # Add the virtualenv
|
||||
# venv = os.path.join(os.path.abspath(os.path.dirname(__file__)),
|
||||
# '..', 'lib', 'python2.6', 'site-packages')
|
||||
# site.addsitedir(venv)
|
||||
# # Reorder sys.path so new directories at the front.
|
||||
# new_sys_path = []
|
||||
# for item in list(sys.path):
|
||||
# if item not in prev_sys_path:
|
||||
# new_sys_path.append(item)
|
||||
# sys.path.remove(item)
|
||||
# sys.path[:0] = new_sys_path
|
||||
|
||||
from django.core.wsgi import get_wsgi_application
|
||||
|
||||
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "settings")
|
||||
|
||||
application = get_wsgi_application()
|
4
mailman/web/requirements.txt
Normal file
4
mailman/web/requirements.txt
Normal file
@ -0,0 +1,4 @@
|
||||
mailmanclient==3.3.5
|
||||
postorius==1.3.10
|
||||
hyperkitty==1.3.9
|
||||
django-mailman3==1.3.11
|
@ -1,5 +1,5 @@
|
||||
<IfModule mod_ssl.c>
|
||||
SSLPassPhraseDialog exec:/etc/apache2/ssl/passphrase
|
||||
# SSLPassPhraseDialog exec:/etc/apache2/ssl/passphrase
|
||||
<VirtualHost *:4430>
|
||||
ServerAdmin james_webmaster@ketrenos.com
|
||||
ServerName ketrenos.com
|
||||
|
File diff suppressed because it is too large
Load Diff
Loading…
x
Reference in New Issue
Block a user