Mailman3 on OpenBSD 7.1
A short guide on how to install and run Mailman version 3 on OpenBSD 7.1.
For an idea that a contact of mine brought up a few weeks ago I decided to set up some infrastructure in order to evaluate/validate a couple of things. One key part of the infrastructure is a mailing list. Even though I could have used one of the many online services to set up a mailing list, I felt like it would make sense to build it myself in order to have greater control over it. Ideally I would like to do some automation at a later stage, for which I would like to be able to hook into the mailing list software. Besides, it’s something I never tried before and I thought it might be a fun way to spend a Friday evening on.
I decided to use GNU Mailman for that purpose. As so often with this type of infrastructure, I also decided to go for OpenBSD for the base system – not only because OpenSMTPd is a breeze to use, but also because of security considerations.
Unfortunately only Mailman2 is available through the official OpenBSD repos, so I decided to manually install Mailman3 and put together a short guide on how to successfully set up and run GNU Mailman v3 (Tom Sawyer) on the latest OpenBSD 7.1.
Preparation
I’m starting with a blank VPS instance here. As usual, I’m going for Vultr (referral link, gives you $100 on signup), because it’s cheap and easy to use. They also have the latest OpenBSD 7.1 OS image readily available, and all one has to do is ask the Vultr support to open port 25 (SMTP). I did so and it took less than 3 hours for them to get back and allow me outgoing SMTP access.
Note: I won’t explicitly mention under which user I’m running each command, hence please read carefully. When the prompt shows
lists#
, I’m acting asroot
user, otherwise, when it showslists%
, I’m the using the_mailman
user.
As soon as the OpenBSD VPS has booted, we can log in via SSH and perform a quick update of the system:
lists# syspatch
lists# pkg_add -u
After that, let’s begin by installing some handy tools:
lists# pkg_add git zsh neovim curl mosh rsync htop base64
What I like to do is link nvim
to vim
, because typing vim
is in my muscle
memory. I also like to use zsh
as shell. Since that’s all preference, these
steps are optional:
lists# ln -s /usr/local/bin/nvim /usr/local/bin/vim
lists# chsh -s /usr/local/bin/zsh root
Another optional but useful thing that I like to do is to change the SSH port
and disable password authentication / enable pubkey authentication. Make sure
you have an authorized_keys
entry with your pubkey in place before applying
this change:
lists# sed -i 's/^#Port 22/Port 31231/g;\
s/^#PubkeyAuthentication .*/PubkeyAuthentication yes/g;\
s/^#PasswordAuthentication .*/PasswordAuthentication no/g' \
/etc/ssh/sshd_config
lists# rcctl restart sshd
Afterwards, disconnect from SSH, and re-connect, ideally using mosh
, and
launch tmux
for the sake of comfort.
Next, let’s update the host information. I’m using the primary domain lists.myowndomain.com for this Mailman installation, so make sure to adapt these entries to the domain you’d like to use. Also, don’t worry, Mailman is able to use multiple domains with multiple mailing lists per domain.
Make sure all host information is set correctly or adjust it if necessary:
lists# cat /etc/myname
lists.myowndomain.com
lists# cat /etc/hosts
127.0.0.1 localhost
::1 localhost
70.30.220.10 lists.myowndomain.com lists
2a00:f100:2000:10af:1000:1ff:fa1a:1a11 lists.myowndomain.com lists
Mailman
With the base system ready, let’s begin installing Mailman. We’ll start with the core first, which is basically the backend of the whole Mailman service. See this page for more information on the Mailman architecture.
Core
As mentioned before, the latest Mailman version available in the OpenBSD repos
is 2.1.39
:
lists# pkg_info mailman
Information for https://cdn.openbsd.org/pub/OpenBSD/7.1/packages/amd64/mailman-2.1.39.tgz
Hence we will have to manually install v3. Let’s start by fetching some dependencies first:
lists# pkg_add py-pip py-virtualenv sassc lynx gettext-tools
Before we continue, make sure that your environment is using en_US.UTF-8
as
LANG
and LC_ALL
:
lists# echo $LANG
en_US.UTF-8
lists# echo $LC_ALL
en_US.UTF-8
Next, add a new user for our Mailman installation:
lists# groupadd _mailman
lists# useradd -d /var/mailman -m -c "Mailman" -g _mailman -L daemon -s /sbin/nologin _mailman
After that, log in as that user – I like to use zsh
here, but you’re free to
use whichever shell you prefer – and continue by setting up a Python
virtualenv
:
lists# su -s /usr/local/bin/zsh -l _mailman -
mailman% python3 -m venv venv
Test the virtualenv
:
mailman% source /var/mailman/venv/bin/activate
(venv) lists%
We can see the virtualenv
activated successfully, so we can go ahead and add
it to our .zshrc
(or .bashrc
or whatever shell you’re using), in order to
automatically switch into the virtualenv
when we log in as _mailman
user:
mailman% echo 'source /var/mailman/venv/bin/activate' >> ~/.zshrc
Try it:
mailman% exit
lists# su -s /usr/local/bin/zsh -l _mailman -
(venv) lists%
Next, let’s install the wheel
package and afterwards the mailman
package
via pip
:
(venv) lists% pip install wheel
(venv) lists% pip install mailman
While this is installing, let’s create a new tmux window and switch back over to
the root account using the C-b c
key combo. Then, create a configuration
folder under /etc
:
lists# mkdir /etc/mailman
lists# ln -s /etc/mailman /etc/mailman3
Add a configuration file for Mailman called mailman.cfg
, with the following
content:
lists# cat /etc/mailman/mailman.cfg
[paths.custom]
var_dir: /var/mailman/mailman
[mailman]
layout: custom
site_owner: root@myowndomain.com
[database]
[shell]
history_file: $var_dir/history.py
[mta]
incoming: mailman.mta.null.NullMTA
outgoing: mailman.mta.deliver.deliver
lmtp_host: 127.0.0.1
lmtp_port: 8024
smtp_host: 127.0.0.1
smtp_port: 25
smtp_secure_mode: smtp
remove_dkim_headers: yes
[ARC]
enabled: no
dmarc: yes
dkim: yes
authserv_id: lists.myowndomain.com
privkey: /etc/mail/dkim/lists.myowndomain.com.key
selector: mail
domain: lists.myowndomain.com
[archiver.hyperkitty]
class: mailman_hyperkitty.Archiver
enable: yes
configuration: /etc/mailman/hyperkitty.cfg
[webservice]
hostname: localhost
port: 8001
use_https: no
admin_user: mailman
admin_pass: mailpass
api_version: 3.1
[logging.root]
level: info
[logging.smtp]
level: info
[logging.subscribe]
level: info
path: subscribe.log
Make sure to adjust values like domain
or files that contain the domain name
to whatever domain you’ll be using. Then switch back to the previous tmux window
using C-b p
and try to run Mailman:
(venv) lists% /var/mailman/venv/bin/mailman -C /etc/mailman/mailman.cfg start
If everything worked out, Mailman should now have been forked and continue
running in the background (ps aux
to check).
Let’s quickly add our first two mailing lists:
(venv) lists% /var/mailman/venv/bin/mailman -C /etc/mailman/mailman.cfg create\
-o root@myowndomain.com --language en null@lists.myowndomain.com
(venv) lists% /var/mailman/venv/bin/mailman -C /etc/mailman/mailman.cfg create\
-o root@myowndomain.com --language en root@lists.myowndomain.com
We need to add a couple of cron jobs for Mailman, so let’s add those:
(venv) lists% crontab -e
@daily /var/mailman/venv/bin/mailman digests --periodic
@daily /var/mailman/venv/bin/mailman notify
Note: If you’re having trouble editing that file,
export EDITOR=
with your favorite editor before runningcrontab -e
, e.g.export EDITOR=nvim
.
Now the only thing that’s left to be done is making sure that Mailman will
launch on boot. Ideally we would want to use supervisord
here, to make sure
that, in case the mailman
process should ever die, it gets restarted. For the
sake of keeping this guide lightweight we’re instead going to add a cron job
instead, that will simply fire-up-and-forget the Mailman instance on (re)boot:
lists# crontab -e
@reboot su -s /usr/local/bin/zsh -l _mailman -c '/var/mailman/venv/bin/mailman -C /etc/mailman/mailman.cfg start --force' 2>&1 | logger -t mailman
Web UI
Next up, we’re going to set up the web UI for Mailman, which not only offers an administrative dashboard for maintaining Mailman, but also gives subscribes the ability to log in and manage their subscriptions.
Before we actually install the web UI, we have to fetch a Rust compiler first.
Otherwise, the cryptography
wheel won’t compile.
lists# pkg_add rust
Now, let’s switch back to the _mailman
user, create some folders that we will
need and run pip install
:
(venv) lists% mkdir -p web/logs
(venv) lists% pip install uwsgi mailman-web mailman-hyperkitty mistune==2.0.0rc1
Note: I had to specify the
mistune
version because apparentlymailman-web migrate
is only compatible with that specific version and the Mailman developers didn’t seem to have pinned to exact dependency version.
While this is installing, let’s create a configuration under
/etc/mailman/settings.py
:
lists# cat /etc/mailman/settings.py
from mailman_web.settings.base import *
from mailman_web.settings.mailman import *
ADMINS = (
('root', 'root@myowndomain.com'),
)
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'HOST': '',
'PORT': '',
'NAME': '/var/mailman/web/mailman-web.db',
}
}
# 'collectstatic' command will copy all the static files here.
STATIC_ROOT = '/var/mailman/web/static'
LOGGING['handlers']['file']['filename'] = '/var/mailman/web/logs/mailmanweb.log'
#: See https://docs.djangoproject.com/en/dev/ref/settings/#allowed-hosts
ALLOWED_HOSTS = [
"localhost", # Archiving API from Mailman, keep it.
"127.0.0.1",
# Add here all production domains you have.
"lists.myowndomain.com",
]
# /etc/mailman/mailman.cfg
MAILMAN_REST_API_USER = 'mailman'
MAILMAN_REST_API_PASS = 'mailpass'
#: Current Django Site being served. This is used to customize the web host
#: being used to serve the current website. For more details about Django
#: site, see: https://docs.djangoproject.com/en/dev/ref/contrib/sites/
SITE_ID = 1
# dd if=/dev/urandom bs=64 count=1 | argon2 'saltysalty' | rg Hash | cut -d ':' -f2
SECRET_KEY = '8bf2ef3217d95b37b67eb5ff7eb5544cb7083b27bf563802272'
# /etc/mailman/hyperkitty.cfg (quoted here, not there).
MAILMAN_ARCHIVER_KEY = '24f217d95b37b6d305876e3e519d5fb03a06315585923556e93'
DEFAULT_FROM_EMAIL = 'root@lists.myowndomain.com'
SERVER_EMAIL = 'root@lists.myowndomain.com'
In order to run the web UI we need a WSGI server config, so let’s add that quickly:
lists# cat /etc/mailman/uwsgi.ini
[uwsgi]
daemonize = true
http-socket = 127.0.0.1:8000
virtualenv = /var/mailman/venv/
module=mailman_web.wsgi:application
env = PYTHONPATH=/etc/mailman/
env = DJANGO_SETTINGS_MODULE=settings
master = true
processes = 2
threads = 2
attach-daemon = /var/mailman/venv/bin/mailman-web qcluster
req-logger = file:/var/mailman/web/logs/uwsgi.log
logger = qcluster file:/var/mailman/web/logs/uwsgi-qcluster.log
log-route = qcluster uwsgi-daemons
logger = file:/var/mailman/web/logs/uwsgi-error.log
Let’s also add a dedicated HyperKitty config for the sake of consistency:
lists# cat /etc/mailman/hyperkitty.cfg
[general]
base_url: https://lists.myowndomain.com/archives/
api_key: 24f217d95b37b6d305876e3e519d5fb03a06315585923556e93
The previous pip install
should be done by now, so we can switch back to the
_mailman
user and continue there. Let’s run the database migrations, collect
static files, compress CSS and compile messages for l18n:
(venv) lists% mailman-web migrate
(venv) lists% mailman-web collectstatic
(venv) lists% mailman-web compress
(venv) lists% mailman-web compilemessages
We’re also going to create a superuser for the web UI:
(venv) lists% mailman-web createsuperuser
Username (leave blank to use '_mailman'): root
Email address: root@myowndomain.com
Password:
Password (again):
Superuser created successfully.
After we’re done with that, we can quickly test uwsgi
to check that our config
works:
(venv) lists% uwsgi --ini /etc/mailman/uwsgi.ini
Next, let’s add the required cron jobs, in addition to the previously added cron jobs for core:
(venv) lists% crontab -e
* * * * * /var/mailman/venv/bin/mailman-web runjobs minutely
0,15,30,45 * * * * /var/mailman/venv/bin/mailman-web runjobs quarter_hourly
@hourly /var/mailman/venv/bin/mailman-web runjobs hourly
@daily /var/mailman/venv/bin/mailman-web runjobs daily
@weekly /var/mailman/venv/bin/mailman-web runjobs weekly
@monthly /var/mailman/venv/bin/mailman-web runjobs monthly
@yearly /var/mailman/venv/bin/mailman-web runjobs yearly
Just like with core, we’ll have uwsgi
start via cron, although on an
actual production system we would probably set up a supervisord
to take care
of that:
lists# crontab -e
@reboot su -s /usr/local/bin/zsh -l _mailman -c '/var/mailman/venv/bin/uwsgi --ini /etc/mailman/uwsgi.ini' 2>&1 | logger -t uwsgi
HTTPd
For issuing a Let’s Encrypt SSL certificate as well as serving the web UI’s
static files we’re going to configure OpenBSD’s httpd
:
lists# mkdir /var/www/mailman
lists# chown root:daemon /var/www/mailman
lists# rsync -avH /var/mailman/web/static /var/www/mailman/
lists# chown -R www:daemon /var/www/mailman/static
Note: You could also try to adjust the
STATIC_ROOT
variable within thesettings.py
file to directly write static files into/var/www/mailman/static
. However, you’ll likely need to adjust the user/group afterwards, so it doesn’t really matter. Static files were generated using themailman-web collectstatic
command anyway, so it’s only one additional command (rsync
) that you need to remember to run whenever you re-generate static files.
lists# cat /etc/httpd.conf
server "lists.myowndomain.com" {
listen on * port 80
location "/.well-known/acme-challenge/*" {
root "/acme"
request strip 2
}
location "/" {
block return 301 "https://$SERVER_NAME$REQUEST_URI"
}
}
server "default" {
listen on 127.0.0.1 port 8080
location "*" {
root "/mailman/static/"
request strip 1
}
}
lists# rcctl enable httpd
lists# rcctl start httpd
lists# cat /etc/acme-client.conf
api_url="https://acme-v02.api.letsencrypt.org/directory"
authority letsencrypt {
api url $api_url
account key "/etc/acme/letsencrypt-privkey.pem"
}
domain lists.myowndomain.com {
domain key "/etc/ssl/private/lists.myowndomain.com.key"
domain full chain certificate "/etc/ssl/lists.myowndomain.com.crt"
sign with letsencrypt
}
With the httpd
in place, let’s obtain the certificate:
lists# acme-client -v lists.myowndomain.com
Also make sure to add a cron job for certificate renewals:
lists# crontab -e
30 0 * * * /usr/sbin/acme-client lists.myowndomain.com && /usr/sbin/rcctl restart smtpd && /usr/sbin/rcctl restart relayd
Relayd
Next we’re going to created a relayd.conf
that allows us to use relayd
as a
reverse proxy for the static files hosted through httpd
and the web UI running
via uwsgi
:
lists# cat /etc/relayd.conf
ipv4="70.30.220.10"
ipv6="2a00:f100:2000:10af:1000:1ff:fa1a:1a11"
log state changes
log connection errors
table <httpd> { 127.0.0.1 }
table <uwsgi> { 127.0.0.1 }
http protocol mailman {
tls keypair "lists.myowndomain.com"
tcp { nodelay, sack, socket buffer 65536, backlog 128 }
tls ciphers "ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305"
tls ecdhe secp384r1
pass request quick path "/static/*" forward to <httpd>
match request header append "X-Forwarded-For" value "$REMOTE_ADDR"
match request header append "X-Forwarded-By" value "$SERVER_ADDR:$SERVER_PORT"
match response header append "X-XSS-Protection" value "1; mode=block"
match response header append "X-Permitted-Cross-Domain-Policies" value "none"
match response header append "X-Frame-Options" value "DENY"
match response header append "X-Content-Type-Options" value "nosniff"
match response header append "Referrer-Policy" value "same-origin"
match response header append "X-Download-Options" value "noopen"
match response header append "Content-Security-Policy" value "default-src 'none'; base-uri 'self'; form-action 'self'; img-src 'self' data: https:; media-src 'self' https:; style-src 'self' 'unsafe-inline'; font-src 'self'; script-src 'self' 'unsafe-inline'; upgrade-insecure-requests;"
match request header append "Connection" value "upgrade"
match response header append "Strict-Transport-Security" value "max-age=31536000; includeSubDomains"
match response header append "Access-Control-Allow-Origin" value "*"
match response header append "Access-Control-Allow-Methods" value "POST, PUT, DELETE, GET, PATCH, OPTIONS"
match response header append "Access-Control-Allow-Headers" value "Authorization, Content-Type, Idempotency-Key"
match response header append "Access-Control-Expose-Headers" value "Link, X-RateLimit-Reset, X-RateLimit-Limit, X-RateLimit-Remaining, X-Request-Id"
}
relay wwwtls {
listen on $ipv4 port https tls
protocol mailman
forward to <uwsgi> port 8000 check http "/" code 301
forward to <httpd> port 8080 check http "/static/admin/css/fonts.css" code 200
}
relay wwwtls6 {
listen on $ipv6 port https tls
protocol mailman
forward to <uwsgi> port 8000 check http "/" code 301
forward to <httpd> port 8080 check http "/static/admin/css/fonts.css" code 200
}
lists# rcctl enable relayd
lists# rcctl start relayd
OpenSMTPd
Now we need to take care of the actual mailing functionality. For that we’re going to install OpenSMTPd, Rspamd (+ Redis) and Senderscore. This won’t be a complete example of how to set up a mail server on OpenBSD. I will do the bare minimum that is required for the Mailman setup to function.
Start by installing the required packages:
lists# pkg_add opensmtpd-extras opensmtpd-filter-rspamd opensmtpd-filter-senderscore rspamd redis
Hint: Pick the
rspamd-x.x-hyperscan
package.
Then, create all the necessary files and configurations:
lists# cat /etc/mail/domains
lists.myowndomain.com
lists# cat /etc/mail/mailname
lists.myowndomain.com
lists# cat /etc/mail/smtpd.conf
pki lists.myowndomain.com cert "/etc/ssl/lists.myowndomain.com.crt"
pki lists.myowndomain.com key "/etc/ssl/private/lists.myowndomain.com.key"
srs key "0d303891jeX6SjWCoiA2d2018b59e32d2018b59e33A2d2018b59e332"
table domains file:/etc/mail/domains
filter check_dyndns phase connect match rdns regex { '.*\.dyn\..*', '.*\.dsl\..*', '.*\.dynamic\..*' } \
disconnect "550 get off your moms phone line"
filter check_rdns phase connect match !rdns \
disconnect "550 no rdns who dis"
filter check_fcrdns phase connect match !fcrdns \
disconnect "550 no fcrdns who dis"
filter senderscore \
proc-exec "filter-senderscore -blockBelow 10 -junkBelow 70 -slowFactor 5000"
filter rspamd proc-exec "filter-rspamd"
listen on lo0 \
filter { rspamd }
listen on vio0 port 25 tls pki "lists.myowndomain.com" \
filter { check_dyndns, check_rdns, check_fcrdns, senderscore, rspamd }
action "lmtp" lmtp "127.0.0.1:8024" rcpt-to virtual { "@" = _mailman }
action "relay" relay helo lists.myowndomain.com srs
match from any for domain <domains> action "lmtp"
match from local for any action "relay"
Let’s also set up basic DKIM/ARC support:
lists# mkdir /etc/mail/dkim
lists# rspamadm dkim_keygen -s 'mail' -d lists.myowndomain.com -k /etc/mail/dkim/lists.myowndomain.com.key > /etc/mail/dkim/lists.myowndomain.com.pub
lists# cat /etc/rspamd/local.d/dkim_signing.conf
enabled = true;
allow_username_mismatch = true;
path = "/etc/mail/dkim/lists.myowndomain.com.key";
selector = "mail";
domain {
lists.myowndomain.com {
path = "/etc/mail/dkim/lists.myowndomain.com.key";
selector = "mail";
}
}
lists# cat /etc/rspamd/local.d/arc.conf
enabled = true;
allow_username_mismatch = true;
path = "/etc/mail/dkim/lists.myowndomain.com.key";
selector = "mail";
domain {
lists.myowndomain.com {
path = "/etc/mail/dkim/lists.myowndomain.com.key";
selector = "mail";
}
}
Next, add the content of the .pub
file as TXT record to your DNS:
lists# cat /etc/mail/dkim/lists.myowndomain.com.pub
In addition, add a DMARC entry to the DNS and adjust the mailto
address:
_dmarc.lists.myowndomain.com. IN TXT "v=DMARC1;p=none;pct=100;rua=mailto:root@myowndomain.com;"
Enable and restart all services once done:
lists# rcctl enable redis
lists# rcctl start redis
lists# rcctl enable rspamd
lists# rcctl start rspamd
lists# rcctl enable smtpd
lists# rcctl start smtpd
PF
As before, I’m going to create a basic boilerplate for the firewall, that will provide us with some protection:
lists# cat /etc/pf.conf
##########
# Macros #
##########
minefield="1024:9999"
ssh_alternate_port=31231
##########
# Tables #
##########
table <bruteforce> persist
table <troublemakers> persist
###########
# Options #
###########
set skip on lo
#########
# Rules #
#########
block return # Block stateless traffic
pass # Establish keep-state
# Block brute-forcers
block quick proto tcp from <bruteforce> \
to (egress) port $ssh_alternate_port
block quick proto tcp from <troublemakers> \
to (egress) port $ssh_alternate_port
# By default, do not permit remote connections to X11
block return in on ! lo0 proto tcp to port 6000:6010
# Port build user does not need network
block return out log proto {tcp udp} user _pbuild
# Connections that made it through to the SSH port but abusing it
pass proto tcp from any to (egress) \
port {$ssh_alternate_port} \
flags S/SA keep state \
(max-src-conn 5, max-src-conn-rate 5/5, \
overload <bruteforce> flush global \
)
# Port 22 is a dead-giveaway # because we know we moved our SSH server
# elsewhere. Anyone poking there is up to no good.
pass in on egress proto tcp \
from any \
to (egress) port { \
telnet, \
ssh, \
netbios-ns, \
netbios-ssn, \
microsoft-ds \
} \
synproxy state \
tag trouble
# Tag stuff in the range $minefield as trouble ...
pass in on egress proto tcp from any to (egress) port $minefield \
synproxy state \
tag trouble
# ... unless it's to our alternate port
pass in proto tcp from any to (egress) port $ssh_alternate_port tag good
# Otherwise add to the troublemakers
pass proto tcp from any to (egress) port $ssh_alternate_port \
tagged trouble \
synproxy state \
(max-src-conn 1, max-src-conn-rate 1/10, \
overload <troublemakers> flush global \
)
pass proto tcp from any to any port {http https smtp} \
keep state (max-src-conn 10, max-src-conn-rate 10/3)
DNS
You will need to have the following records in place for all this to work properly:
lists.myowndomain.com. 300 IN A 70.30.220.10
lists.myowndomain.com. 300 IN AAAA 2a00:f100:2000:10af:1000:1ff:fa1a:1a11
lists.myowndomain.com. 900 IN MX 10 lists.myowndomain.com.
lists.myowndomain.com. 300 IN TXT "v=spf1 mx a ~all"
_dmarc.lists.myowndomain.com. 300 IN TXT "v=DMARC1;p=none;pct=100;rua=mailto:root@myowndomain.com;"
mail._domainkey.lists.myowndomain.com. 300 IN TXT "v=DKIM1; k=rsa;p=MIGfMA0GCSqGS...Ib3DQEBAQUAA4"
Final Steps
After a quick reboot we can double-check that all services will start up
correctly and that we are able to connect to the Mailman web UI via
https://lists.myowndomain.com
.
Check that you can log in with the previously created root
user under
https://lists.myowndomain.com/admin/
.
From this point on we would go ahead and configure Mailman, as well as other parts of the mail service (full DKIM/ARC, Rspamd, etc.). I won’t cover these topics here, as there is plenty documentation on those things available and it depends a lot on the individual use case. The main goal was to show how mailman3 can be configured and run on OpenBSD 7.1.
Further Reading
- https://man.openbsd.org/httpd.conf.5
- https://man.openbsd.org/relayd.conf.5
- https://man.openbsd.org/relayctl.8
- https://man.openbsd.org/smtpd.conf
- https://rspamd.com/doc/modules/dkim_signing.html
- https://doc.coker.com.au/internet/dkim-and-mailing-lists/
- https://www.openbsdhandbook.com/pf/
- https://docs.mailman3.org/en/latest/install/virtualenv.html
- https://docs.mailman3.org/projects/mailman/en/latest/src/mailman/docs/mta.html#id2
- https://gitlab.com/mailman/mailman/-/blob/master/src/mailman/config/schema.cfg
- https://docs.mailman3.org/projects/mailman-web/en/latest/settings.html
- https://man.sr.ht/lists.sr.ht/configuration.md
- https://docs.mailman3.org/projects/hyperkitty/en/latest/install.html
- https://gitlab.com/mailman/mailman-hyperkitty/blob/master/mailman-hyperkitty.cfg
- https://en.internet.nl/test-mail/
Enjoyed this? Support me via Monero, Bitcoin, Lightning, or Ethereum! More info.