Mailman 2.1 has served its purpose at work for several years. Python 2 is at its end of life in FreeBSD. It’s time to move on to Mailman 3 and Python 3, or to Sympa if I’m unsuccessful in getting Mailman 3 up & running. I’ll try and document each step along the way.

Note, this is a long and dull blog entry. I’ll summarize this gig in another blog post when everything works as planned.

Parallel to my own efforts, Dan Langille (dvl@) is attempting to get Mailman 3 working for the FreeBSD project. I’ll be leaning on most of his work, e.g. r556421, r556422, and this.

I started by creating a new, dedicated VM almost 7 weeks ago. It has been given 4 vCPUs and 8 GiB of memory. It uses a 17 GiB virtual disk for boot and swap partitions; 512K legacy boot partition, 128M UEFI system partition, and 16G swap partition. The root pool is configured as raidz1 stretching over 3 virtual disks, each of 12 GiB, yielding roughly 35G. A dedicated data pool is configured as raidz1 stretching over 3 drives, each of 2 GiB, yielding roughly 5G. All disks are GPT partitioned and uses GPT labels for every partition.

xbd0 aka ada0:

GPT 128
1   freebsd-boot       40     1024 gptboot0
2            efi     1064   262144 esp0
3   freebsd-swap   263208 33554432 swap0

xbd1 aka ada1:

GPT 128
1   freebsd-zfs       40 25165744 lists_zroot0

xbd2 aka ada2:

GPT 128
1   freebsd-zfs       40 25165744 lists_zroot1

xbd4:

GPT 128
1   freebsd-zfs       40 25165744 lists_zroot2

xbd5:

GPT 128
1   freebsd-zfs      40 4194224 lists_zdata0

xbd6:

GPT 128
1   freebsd-zfs      40 4194224 lists_zdata1

xbd7:

GPT 128
1   freebsd-zfs      40 4194224 lists_zdata2

(The virtual DVD drive is mapped to xbd3 in case you were wondering.)

I might redo the root pool as it has been given way too much storage.

To keep the user data separated from the system, the dedicated zpool named lists_zdata will store these filesystems (and possibly more):

  • /home
  • /usr/local/mailman
  • /usr/local/www
  • /var/db/postfix
  • /var/db/postgres/data11
  • /var/db/postgres/data11/base
  • /var/db/postgres/data11/pg_wal
  • /var/spool/clientmqueue
  • /var/spool/mqueue
  • /var/spool/postfix
  • /var/spool/sympa

The VM has these ports installed:

Webserver and certificate management:

  • www/apache24
  • security/py-certbot@py37

Postfix as the MTA instead of sendmail from base:

  • mail/postfix

Mailman 3 & friends:

  • mail/mailman3@py37
  • www/py-postorius@py37
  • www/py-gunicorn@py37
  • devel/py-importlib-resources@py37
  • mail/py-authres@py37
  • mail/py-authheaders@py37

Sympa & friends:

  • mail/sympa
  • databases/postgresql11-client
  • databases/postgresql11-contrib
  • databases/postgresql11-server

(I’ll take the leap to PostgreSQL 13 in early spring 2021.)

For sysadmin tasks:

  • misc/amanda-client
  • shells/bash
  • devel/cvs+ipv6
  • editors/emacs@nox
  • devel/gdb
  • sysutils/htop
  • net-mgmt/iftop
  • sysutils/lsof
  • sysutils/lsop
  • misc/mc-nox11
  • sysutils/ncdu
  • net-mgmt/net-snmp
  • security/rkhunter
  • sysutils/parallel
  • sysutils/psmisc
  • sysutils/screen

For communicating back to the mothership^Whypervisor:

  • sysutils/xe-guest-utilities
  • sysutils/xen-guest-tools

All packages are built from the ports tree by our resident builder.

The Mailman 3 Pre-Installation Guide and subsequent chapters will undoubtly be handy along the way.


2020-12-01

I got sidetracked too many times today to do anything useful beyond writing the initial part of these notes. Maybe I’ll fare better tomorrow.


2020-12-02

I ran these two commands and got myself a surprise:

# cd /usr/local/mailman
# mailman info
Traceback (most recent call last):
  File "/usr/local/lib/python3.7/site-packages/pkg_resources/__init__.py", line 583, in _build_master
    ws.require(__requires__)
  File "/usr/local/lib/python3.7/site-packages/pkg_resources/__init__.py", line 900, in require
    needed = self.resolve(parse_requirements(requirements))
  File "/usr/local/lib/python3.7/site-packages/pkg_resources/__init__.py", line 791, in resolve
    raise VersionConflict(dist, req).with_context(dependent_req)
pkg_resources.ContextualVersionConflict: (zope.interface 4.6.0 (/usr/local/lib/python3.7/site-packages), Requirement.parse('zope.interface>=5.0'), {'mailman'})

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/usr/local/bin/mailman", line 6, in 
    from pkg_resources import load_entry_point
  File "/usr/local/lib/python3.7/site-packages/pkg_resources/__init__.py", line 3251, in 
    @_call_aside
  File "/usr/local/lib/python3.7/site-packages/pkg_resources/__init__.py", line 3235, in _call_aside
    f(*args, **kwargs)
  File "/usr/local/lib/python3.7/site-packages/pkg_resources/__init__.py", line 3264, in _initialize_master_working_set
    working_set = WorkingSet._build_master()
  File "/usr/local/lib/python3.7/site-packages/pkg_resources/__init__.py", line 585, in _build_master
    return cls._build_from_requirements(__requires__)
  File "/usr/local/lib/python3.7/site-packages/pkg_resources/__init__.py", line 598, in _build_from_requirements
    dists = ws.resolve(reqs, Environment())
  File "/usr/local/lib/python3.7/site-packages/pkg_resources/__init__.py", line 786, in resolve
    raise DistributionNotFound(req, requirers)
pkg_resources.DistributionNotFound: The 'zope.interface>=5.0' distribution was not found and is required by mailman

I have an old version of zope which is strange since the installed zope packages got pulled in by mail/mailman3@py37 and security/py-certbot@py37:

# pkg info -r py37-zope\*
py37-zope.component-4.2.2:
	py37-mailman-3.3.1
	py37-certbot-1.9.0,1
py37-zope.configuration-4.1.0:
	py37-mailman-3.3.1
py37-zope.event-4.1.0:
	py37-mailman-3.3.1
	py37-zope.component-4.2.2
	py37-zope.schema-4.2.2_1
py37-zope.i18nmessageid-3.6.1_1:
	py37-zope.configuration-4.1.0
py37-zope.interface-4.6.0:
	py37-mailman-3.3.1
	py37-certbot-1.9.0,1
	py37-lazr.config-2.2.2
	py37-zope.configuration-4.1.0
	py37-flufl.bounce-3.0_1
	py37-zope.component-4.2.2
	py37-lazr.delegates-2.0.4
	py37-zope.schema-4.2.2_1
py37-zope.schema-4.2.2_1:
	py37-zope.configuration-4.1.0

This doesn’t look too well until the ports tree is updated to support zope>=5.0.

As suggested by Dan Langille, I changed /usr/local/lib/python3.7/site-packages/mailman-3.3.1-py3.7.egg-info/requires.txt to accept zope.interface>=4.0:

--- lib/python3.7/site-packages/mailman-3.3.1-py3.7.egg-info/requires.txt.orig	2020-10-15 16:40:14.000000000 +0200
+++ lib/python3.7/site-packages/mailman-3.3.1-py3.7.egg-info/requires.txt	2020-12-02 20:58:48.791696000 +0100
@@ -19,4 +19,4 @@
 zope.component
 zope.configuration
 zope.event
-zope.interface>=5.0
+zope.interface>=4.0

Well, I got a bit further:

# cd /usr/local/mailman
# mailman info
Traceback (most recent call last):
  File "/usr/local/bin/mailman", line 6, in 
    from pkg_resources import load_entry_point
  File "/usr/local/lib/python3.7/site-packages/pkg_resources/__init__.py", line 3251, in 
    @_call_aside
  File "/usr/local/lib/python3.7/site-packages/pkg_resources/__init__.py", line 3235, in _call_aside
    f(*args, **kwargs)
  File "/usr/local/lib/python3.7/site-packages/pkg_resources/__init__.py", line 3264, in _initialize_master_working_set
    working_set = WorkingSet._build_master()
  File "/usr/local/lib/python3.7/site-packages/pkg_resources/__init__.py", line 583, in _build_master
    ws.require(__requires__)
  File "/usr/local/lib/python3.7/site-packages/pkg_resources/__init__.py", line 900, in require
    needed = self.resolve(parse_requirements(requirements))
  File "/usr/local/lib/python3.7/site-packages/pkg_resources/__init__.py", line 786, in resolve
    raise DistributionNotFound(req, requirers)
pkg_resources.DistributionNotFound: The 'gunicorn' distribution was not found and is required by mailman

It looks like I need to add www/py-gunicorn@py37 to the local metaport.

# pkg update -f && pkg upgrade
Updating builder01 repository catalogue...
Fetching meta.conf: 100%    163 B   0.2kB/s    00:01
Fetching packagesite.txz: 100%  228 KiB 233.2kB/s    00:01
Processing entries: 100%
builder01 repository update completed. 845 packages processed.
All repositories are up to date.
Updating builder01 repository catalogue...
builder01 repository is up to date.
All repositories are up to date.
Checking for upgrades (3 candidates): 100%
Processing candidates (3 candidates): 100%
The following 4 package(s) will be affected (of 0 checked):

New packages to be INSTALLED:
        py37-gunicorn: 19.9.0_1
        py37-setproctitle: 1.1.10_1

Installed packages to be UPGRADED:
        libxml2: 2.9.10_1 -> 2.9.10_2
        lists-localbase: 2020113000 -> 2020120200

Number of packages to be installed: 2
Number of packages to be upgraded: 2

The process will require 1 MiB more space.
1 MiB to be downloaded.

Proceed with this action? [y/N]: y
[1/4] Fetching lists-localbase-2020120200.txz: 100%    928 B   0.9kB/s    00:01
[2/4] Fetching libxml2-2.9.10_2.txz: 100%  825 KiB 845.2kB/s    00:01
[3/4] Fetching py37-gunicorn-19.9.0_1.txz: 100%  180 KiB 184.5kB/s    00:01
[4/4] Fetching py37-setproctitle-1.1.10_1.txz: 100%   10 KiB  10.4kB/s    00:01
Checking integrity... done (0 conflicting)
[1/4] Upgrading libxml2 from 2.9.10_1 to 2.9.10_2...
[1/4] Extracting libxml2-2.9.10_2: 100%
[2/4] Installing py37-setproctitle-1.1.10_1...
[2/4] Extracting py37-setproctitle-1.1.10_1: 100%
[3/4] Installing py37-gunicorn-19.9.0_1...
[3/4] Extracting py37-gunicorn-19.9.0_1: 100%
[4/4] Upgrading lists-localbase from 2020113000 to 2020120200...

Let’s try again:

# cd /usr/local/mailman
# mailman info
Traceback (most recent call last):
  File "/usr/local/bin/mailman", line 6, in 
    from pkg_resources import load_entry_point
  File "/usr/local/lib/python3.7/site-packages/pkg_resources/__init__.py", line 3251, in 
    @_call_aside
  File "/usr/local/lib/python3.7/site-packages/pkg_resources/__init__.py", line 3235, in _call_aside
    f(*args, **kwargs)
  File "/usr/local/lib/python3.7/site-packages/pkg_resources/__init__.py", line 3264, in _initialize_master_working_set
    working_set = WorkingSet._build_master()
  File "/usr/local/lib/python3.7/site-packages/pkg_resources/__init__.py", line 583, in _build_master
    ws.require(__requires__)
  File "/usr/local/lib/python3.7/site-packages/pkg_resources/__init__.py", line 900, in require
    needed = self.resolve(parse_requirements(requirements))
  File "/usr/local/lib/python3.7/site-packages/pkg_resources/__init__.py", line 786, in resolve
    raise DistributionNotFound(req, requirers)
pkg_resources.DistributionNotFound: The 'importlib_resources>=1.1.0' distribution was not found and is required by mailman

Righto, more ports to add to the local metaport. This time it’s devel/py-importlib-resources@py37.

# pkg update -f && pkg upgrade
Updating builder01 repository catalogue...
Fetching meta.conf: 100%    163 B   0.2kB/s    00:01
Fetching packagesite.txz: 100%  228 KiB 233.6kB/s    00:01
Processing entries: 100%
builder01 repository update completed. 847 packages processed.
All repositories are up to date.
Updating builder01 repository catalogue...
builder01 repository is up to date.
All repositories are up to date.
Checking for upgrades (2 candidates): 100%
Processing candidates (2 candidates): 100%
The following 2 package(s) will be affected (of 0 checked):

New packages to be INSTALLED:
        py37-importlib-resources: 3.3.0

Installed packages to be UPGRADED:
        lists-localbase: 2020120200 -> 2020120201

Number of packages to be installed: 1
Number of packages to be upgraded: 1

34 KiB to be downloaded.

Proceed with this action? [y/N]: y
[1/2] Fetching lists-localbase-2020120201.txz: 100%    952 B   1.0kB/s    00:01
[2/2] Fetching py37-importlib-resources-3.3.0.txz: 100%   33 KiB  33.6kB/s    00:01
Checking integrity... done (0 conflicting)
[1/2] Installing py37-importlib-resources-3.3.0...
[1/2] Extracting py37-importlib-resources-3.3.0: 100%
[2/2] Upgrading lists-localbase from 2020120200 to 2020120201...

Will it work this time?

# cd /usr/local/mailman
# mailman info
Traceback (most recent call last):
  File "/usr/local/bin/mailman", line 6, in 
    from pkg_resources import load_entry_point
  File "/usr/local/lib/python3.7/site-packages/pkg_resources/__init__.py", line 3251, in 
    @_call_aside
  File "/usr/local/lib/python3.7/site-packages/pkg_resources/__init__.py", line 3235, in _call_aside
    f(*args, **kwargs)
  File "/usr/local/lib/python3.7/site-packages/pkg_resources/__init__.py", line 3264, in _initialize_master_working_set
    working_set = WorkingSet._build_master()
  File "/usr/local/lib/python3.7/site-packages/pkg_resources/__init__.py", line 583, in _build_master
    ws.require(__requires__)
  File "/usr/local/lib/python3.7/site-packages/pkg_resources/__init__.py", line 900, in require
    needed = self.resolve(parse_requirements(requirements))
  File "/usr/local/lib/python3.7/site-packages/pkg_resources/__init__.py", line 786, in resolve
    raise DistributionNotFound(req, requirers)
pkg_resources.DistributionNotFound: The 'authres>=1.0.1' distribution was not found and is required by mailman

More ports to add. mail/py-authres@py37 it is.

# pkg update -f && pkg upgrade
Updating builder01 repository catalogue...
Fetching meta.conf: 100%    163 B   0.2kB/s    00:01
Fetching packagesite.txz: 100%  228 KiB 233.9kB/s    00:01
Processing entries: 100%
builder01 repository update completed. 848 packages processed.
All repositories are up to date.
Updating builder01 repository catalogue...
builder01 repository is up to date.
All repositories are up to date.
Checking for upgrades (2 candidates): 100%
Processing candidates (2 candidates): 100%
The following 2 package(s) will be affected (of 0 checked):

New packages to be INSTALLED:
        py37-authres: 1.2.0

Installed packages to be UPGRADED:
        lists-localbase: 2020120201 -> 2020120202

Number of packages to be installed: 1
Number of packages to be upgraded: 1

31 KiB to be downloaded.

Proceed with this action? [y/N]: y
[1/2] Fetching lists-localbase-2020120202.txz: 100%    968 B   1.0kB/s    00:01
[2/2] Fetching py37-authres-1.2.0.txz: 100%   30 KiB  30.7kB/s    00:01
Checking integrity... done (0 conflicting)
[1/2] Installing py37-authres-1.2.0...
[1/2] Extracting py37-authres-1.2.0: 100%
[2/2] Upgrading lists-localbase from 2020120201 to 2020120202...

Do I dare try again?

# cd /usr/local/mailman
# mailman info
Traceback (most recent call last):
  File "/usr/local/bin/mailman", line 6, in 
    from pkg_resources import load_entry_point
  File "/usr/local/lib/python3.7/site-packages/pkg_resources/__init__.py", line 3251, in 
    @_call_aside
  File "/usr/local/lib/python3.7/site-packages/pkg_resources/__init__.py", line 3235, in _call_aside
    f(*args, **kwargs)
  File "/usr/local/lib/python3.7/site-packages/pkg_resources/__init__.py", line 3264, in _initialize_master_working_set
    working_set = WorkingSet._build_master()
  File "/usr/local/lib/python3.7/site-packages/pkg_resources/__init__.py", line 583, in _build_master
    ws.require(__requires__)
  File "/usr/local/lib/python3.7/site-packages/pkg_resources/__init__.py", line 900, in require
    needed = self.resolve(parse_requirements(requirements))
  File "/usr/local/lib/python3.7/site-packages/pkg_resources/__init__.py", line 786, in resolve
    raise DistributionNotFound(req, requirers)
pkg_resources.DistributionNotFound: The 'authheaders>=0.9.2' distribution was not found and is required by mailman

Will this ever succeed? Oh well, let’s add mail/py-authheaders@py37.

# pkg update -f && pkg upgrade
Updating builder01 repository catalogue...
Fetching meta.conf: 100%    163 B   0.2kB/s    00:01
Fetching packagesite.txz: 100%  229 KiB 234.7kB/s    00:01
Processing entries: 100%
builder01 repository update completed. 852 packages processed.
All repositories are up to date.
Updating builder01 repository catalogue...
builder01 repository is up to date.
All repositories are up to date.
Checking for upgrades (2 candidates): 100%
Processing candidates (2 candidates): 100%
The following 6 package(s) will be affected (of 0 checked):

New packages to be INSTALLED:
        libsodium: 1.0.18
        py37-authheaders: 0.13.0
        py37-dkimpy: 1.0.5
        py37-publicsuffix2: 2.20191221_8
        py37-pynacl: 1.4.0

Installed packages to be UPGRADED:
        lists-localbase: 2020120202 -> 2020120203

Number of packages to be installed: 5
Number of packages to be upgraded: 1

The process will require 3 MiB more space.
563 KiB to be downloaded.

Proceed with this action? [y/N]: y
[1/6] Fetching lists-localbase-2020120203.txz: 100%    984 B   1.0kB/s    00:01
[2/6] Fetching py37-authheaders-0.13.0.txz: 100%   75 KiB  77.1kB/s    00:01
[3/6] Fetching py37-dkimpy-1.0.5.txz: 100%   57 KiB  58.8kB/s    00:01
[4/6] Fetching py37-pynacl-1.4.0.txz: 100%   86 KiB  87.6kB/s    00:01
[5/6] Fetching libsodium-1.0.18.txz: 100%  264 KiB 270.6kB/s    00:01
[6/6] Fetching py37-publicsuffix2-2.20191221_8.txz: 100%   79 KiB  80.9kB/s    00:01
Checking integrity... done (0 conflicting)
[1/6] Installing libsodium-1.0.18...
[1/6] Extracting libsodium-1.0.18: 100%
[2/6] Installing py37-pynacl-1.4.0...
[2/6] Extracting py37-pynacl-1.4.0: 100%
[3/6] Installing py37-dkimpy-1.0.5...
[3/6] Extracting py37-dkimpy-1.0.5: 100%
[4/6] Installing py37-publicsuffix2-2.20191221_8...
[4/6] Extracting py37-publicsuffix2-2.20191221_8: 100%
[5/6] Installing py37-authheaders-0.13.0...
[5/6] Extracting py37-authheaders-0.13.0: 100%
[6/6] Upgrading lists-localbase from 2020120202 to 2020120203...

How about now?

# cd /usr/local/mailman
# mailman info
GNU Mailman 3.3.1 (Tom Sawyer)
Python 3.7.9 (default, Oct 15 2020, 08:08:23)
[Clang 10.0.1 (git@github.com:llvm/llvm-project.git llvmorg-10.0.1-0-gef32c611a
config file: None
db url: sqlite:////usr/local/mailman/data/mailman.db
devmode: DISABLED
REST root url: http://localhost:8001/3.1/
REST credentials: restadmin:restpass

Finally, we are getting somewhere.

I’m beginning to feel that www/py-gunicorn@py37, devel/py-importlib-resources@py37, mail/py-authres@py37, and mail/py-authheaders@py37 should really have been pulled in by mail/mailman3@py37.

It’s getting late, so I’ll continue tomorrow.


2020-12-03

Running mailman info last night populated /usr/local/mailman with directories and files, including /usr/local/mailman/etc/mailman.cfg. The latter has only a few comments:

# AUTOMATICALLY GENERATED BY MAILMAN ON 2020-12-02 22:02:09 UTC
#
# This is your GNU Mailman 3 configuration file.  You can edit this file to
# configure Mailman to your needs, and Mailman will never overwrite it.
# Additional configuration information is available here:
#
# https://mailman.readthedocs.io/en/latest/src/mailman/config/docs/config.html
#
# For example, uncomment the following lines to run Mailman in developer mode.
#
# [devmode]
# enabled: yes
# recipient: your.address@your.domain

Poking around led me to the MAILMAN_CONFIG_FILE environment variable.

# cd /usr/local/mailman
# setenv MAILMAN_CONFIG_FILE /usr/local/mailman/etc/mailman.cfg
# mailman info
GNU Mailman 3.3.1 (Tom Sawyer)
Python 3.7.9 (default, Oct 15 2020, 08:08:23)
[Clang 10.0.1 (git@github.com:llvm/llvm-project.git llvmorg-10.0.1-0-gef32c611a
config file: /usr/local/mailman/etc/mailman.cfg
db url: sqlite:////usr/local/mailman/data/mailman.db
devmode: DISABLED
REST root url: http://localhost:8001/3.1/
REST credentials: restadmin:restpass

Now there’s a slight improvement. Maybe we can now try to start the Mailman service.

# service mailman onestart
Traceback (most recent call last):
  File "/usr/local/bin/mailman", line 11, in 
    load_entry_point('mailman==3.3.1', 'console_scripts', 'mailman')()
  File "/usr/local/lib/python3.7/site-packages/click/core.py", line 829, in __call__
    return self.main(*args, **kwargs)
  File "/usr/local/lib/python3.7/site-packages/click/core.py", line 781, in main
    with self.make_context(prog_name, args, **extra) as ctx:
  File "/usr/local/lib/python3.7/site-packages/click/core.py", line 700, in make_context
    self.parse_args(ctx, args)
  File "/usr/local/lib/python3.7/site-packages/click/core.py", line 1212, in parse_args
    rest = Command.parse_args(self, ctx, args)
  File "/usr/local/lib/python3.7/site-packages/click/core.py", line 1048, in parse_args
    value, args = param.handle_parse_result(ctx, opts, args)
  File "/usr/local/lib/python3.7/site-packages/click/core.py", line 1630, in handle_parse_result
    value = invoke_param_callback(self.callback, ctx, self, value)
  File "/usr/local/lib/python3.7/site-packages/click/core.py", line 123, in invoke_param_callback
    return callback(ctx, param, value)
  File "/usr/local/lib/python3.7/site-packages/mailman/bin/mailman.py", line 94, in initialize_config
    initialize(value)
  File "/usr/local/lib/python3.7/site-packages/mailman/core/initialize.py", line 217, in initialize
    initialize_1(config_path)
  File "/usr/local/lib/python3.7/site-packages/mailman/core/initialize.py", line 122, in initialize_1
    mailman.config.config.load(config_path)
  File "/usr/local/lib/python3.7/site-packages/mailman/config/config.py", line 142, in load
    self.push(filename, user_config.read())
  File "/usr/local/lib/python3.7/site-packages/mailman/config/config.py", line 148, in push
    self._post_process()
  File "/usr/local/lib/python3.7/site-packages/mailman/config/config.py", line 160, in _post_process
    self.ensure_directories_exist()
  File "/usr/local/lib/python3.7/site-packages/mailman/config/config.py", line 262, in ensure_directories_exist
    with Lock(lock_file):
  File "/usr/local/lib/python3.7/site-packages/flufl/lock/_lockfile.py", line 334, in __enter__
    self.lock()
  File "/usr/local/lib/python3.7/site-packages/flufl/lock/_lockfile.py", line 203, in lock
    self._write()
  File "/usr/local/lib/python3.7/site-packages/flufl/lock/_lockfile.py", line 417, in _write
    with open(self._claimfile, 'w') as fp:
PermissionError: [Errno 13] Permission denied: '/usr/local/mailman/locks/mailman-cfg.lck|[REDACTED]|1810|4264959031836525201'

Hum. It looks like ownership of files and directories is an issue.

# cd /usr/local/mailman
# ls -Rl
total 58
drwxr-xr-x   2 root  mailman   2 Dec  2 23:02 archives
drwxr-xr-x   2 root  mailman   2 Dec  2 23:02 cache
drwxr-xr-x   2 root  mailman   3 Dec  2 23:02 data
drwxr-xr-x   2 root  mailman   3 Dec  2 23:02 etc
drwxr-xr-x   2 root  mailman   2 Dec  2 23:02 lists
drwxr-xr-x   2 root  mailman   2 Dec  3 09:22 locks
drwxr-xr-x   2 root  mailman   7 Dec  2 23:02 logs
drwxr-xr-x   2 root  mailman   2 Dec  2 23:02 messages
drwxr-xr-x  14 root  mailman  14 Dec  2 23:02 queue
drwxr-xr-x   2 root  mailman   2 Dec  2 23:02 templates

./archives:
total 0

./cache:
total 0

./data:
total 395
-rw-rw----  1 root  mailman  270336 Dec  2 23:02 mailman.db

./etc:
total 6
-rw-rw----  1 root  mailman  501 Dec  3 09:13 mailman.cfg

./lists:
total 0

./locks:
total 0

./logs:
total 8
-rw-rw----  1 root  mailman     0 Dec  2 23:02 bounce.log
-rw-rw----  1 root  mailman     0 Dec  2 23:02 debug.log
-rw-rw----  1 root  mailman  2738 Dec  3 09:24 mailman.log
-rw-rw----  1 root  mailman     0 Dec  2 23:02 plugins.log
-rw-rw----  1 root  mailman     0 Dec  2 23:02 smtp.log

./messages:
total 0

./queue:
total 6
drwxrwx---  2 root  mailman  2 Dec  2 23:02 archive
drwxrwx---  2 root  mailman  2 Dec  2 23:02 bad
drwxrwx---  2 root  mailman  2 Dec  2 23:02 bounces
drwxrwx---  2 root  mailman  2 Dec  2 23:02 command
drwxrwx---  2 root  mailman  2 Dec  2 23:02 digest
drwxrwx---  2 root  mailman  2 Dec  2 23:02 in
drwxrwx---  2 root  mailman  2 Dec  2 23:02 nntp
drwxrwx---  2 root  mailman  2 Dec  2 23:02 out
drwxrwx---  2 root  mailman  2 Dec  2 23:02 pipeline
drwxrwx---  2 root  mailman  2 Dec  2 23:02 retry
drwxrwx---  2 root  mailman  2 Dec  2 23:02 shunt
drwxrwx---  2 root  mailman  2 Dec  2 23:02 virgin

./queue/archive:
total 0

./queue/bad:
total 0

./queue/bounces:
total 0

./queue/command:
total 0

./queue/digest:
total 0

./queue/in:
total 0

./queue/nntp:
total 0

./queue/out:
total 0

./queue/pipeline:
total 0

./queue/retry:
total 0

./queue/shunt:
total 0

./queue/virgin:
total 0

./templates:
total 0

Only /usr/local/mailman is truly owned by mailman:mailman due to the scripts I use when creating my ZFS pools.

# chown -R mailman:mailman /usr/local/mailman
# cd /usr/local/mailman
# ls -Rl
total 58
drwxr-xr-x   2 mailman  mailman   2 Dec  2 23:02 archives
drwxr-xr-x   2 mailman  mailman   2 Dec  2 23:02 cache
drwxr-xr-x   2 mailman  mailman   3 Dec  2 23:02 data
drwxr-xr-x   2 mailman  mailman   3 Dec  2 23:02 etc
drwxr-xr-x   2 mailman  mailman   2 Dec  2 23:02 lists
drwxr-xr-x   2 mailman  mailman   2 Dec  3 09:22 locks
drwxr-xr-x   2 mailman  mailman   7 Dec  2 23:02 logs
drwxr-xr-x   2 mailman  mailman   2 Dec  2 23:02 messages
drwxr-xr-x  14 mailman  mailman  14 Dec  2 23:02 queue
drwxr-xr-x   2 mailman  mailman   2 Dec  2 23:02 templates

./archives:
total 0

./cache:
total 0

./data:
total 395
-rw-rw----  1 mailman  mailman  270336 Dec  2 23:02 mailman.db

./etc:
total 6
-rw-rw----  1 mailman  mailman  501 Dec  3 09:13 mailman.cfg

./lists:
total 0

./locks:
total 0

./logs:
total 8
-rw-rw----  1 mailman  mailman     0 Dec  2 23:02 bounce.log
-rw-rw----  1 mailman  mailman     0 Dec  2 23:02 debug.log
-rw-rw----  1 mailman  mailman  2738 Dec  3 09:24 mailman.log
-rw-rw----  1 mailman  mailman     0 Dec  2 23:02 plugins.log
-rw-rw----  1 mailman  mailman     0 Dec  2 23:02 smtp.log

./messages:
total 0

./queue:
total 6
drwxrwx---  2 mailman  mailman  2 Dec  2 23:02 archive
drwxrwx---  2 mailman  mailman  2 Dec  2 23:02 bad
drwxrwx---  2 mailman  mailman  2 Dec  2 23:02 bounces
drwxrwx---  2 mailman  mailman  2 Dec  2 23:02 command
drwxrwx---  2 mailman  mailman  2 Dec  2 23:02 digest
drwxrwx---  2 mailman  mailman  2 Dec  2 23:02 in
drwxrwx---  2 mailman  mailman  2 Dec  2 23:02 nntp
drwxrwx---  2 mailman  mailman  2 Dec  2 23:02 out
drwxrwx---  2 mailman  mailman  2 Dec  2 23:02 pipeline
drwxrwx---  2 mailman  mailman  2 Dec  2 23:02 retry
drwxrwx---  2 mailman  mailman  2 Dec  2 23:02 shunt
drwxrwx---  2 mailman  mailman  2 Dec  2 23:02 virgin

./queue/archive:
total 0

./queue/bad:
total 0

./queue/bounces:
total 0

./queue/command:
total 0

./queue/digest:
total 0

./queue/in:
total 0

./queue/nntp:
total 0

./queue/out:
total 0

./queue/pipeline:
total 0

./queue/retry:
total 0

./queue/shunt:
total 0

./queue/virgin:
total 0

./templates:
total 0

One more try.

# service mailman onestart
Starting Mailman's master runner
Generating MTA alias maps
sh: /usr/local/sbin/postmap: not found
sh: /usr/local/sbin/postmap: not found
Traceback (most recent call last):
  File "/usr/local/bin/mailman", line 11, in 
    load_entry_point('mailman==3.3.1', 'console_scripts', 'mailman')()
  File "/usr/local/lib/python3.7/site-packages/click/core.py", line 829, in __call__
    return self.main(*args, **kwargs)
  File "/usr/local/lib/python3.7/site-packages/click/core.py", line 782, in main
    rv = self.invoke(ctx)
  File "/usr/local/lib/python3.7/site-packages/mailman/bin/mailman.py", line 68, in invoke
    return super().invoke(ctx)
  File "/usr/local/lib/python3.7/site-packages/click/core.py", line 1259, in invoke
    return _process_result(sub_ctx.command.invoke(sub_ctx))
  File "/usr/local/lib/python3.7/site-packages/click/core.py", line 1066, in invoke
    return ctx.invoke(self.callback, **ctx.params)
  File "/usr/local/lib/python3.7/site-packages/click/core.py", line 610, in invoke
    return callback(*args, **kwargs)
  File "/usr/local/lib/python3.7/site-packages/click/decorators.py", line 21, in new_func
    return f(get_current_context(), *args, **kwargs)
  File "/usr/local/lib/python3.7/site-packages/mailman/commands/cli_control.py", line 109, in start
    call_name(config.mta.incoming).regenerate()
  File "/usr/local/lib/python3.7/site-packages/mailman/mta/postfix.py", line 133, in regenerate
    raise RuntimeError(NL.join(errors))
RuntimeError: command failure: /usr/local/sbin/postmap /usr/local/mailman/data/postfix_lmtp, 127, Unknown error: 127
command failure: /usr/local/sbin/postmap /usr/local/mailman/data/postfix_domains, 127, Unknown error: 127

# service mailman onestatus
GNU Mailman is running (master pid: 1882)

# service mailman onestop
Shutting down Mailman's master runner

# service mailman onestatus
GNU Mailman is not running

Without any further configuration, Mailman 3 wants to use Postfix as the MTA. Should I add Postfix to the mix or try to convince Mailman to use sendmail from base? Judging by Hooking up your mail server, ditching sendmail in favour of Postfix seems reasonable.

Upon reading D14126, I noticed service mailman info. I decided to empty /usr/local/mailman, add mailman_enable="YES" to /etc/rc.conf and run the command.

# service mailman info
Usage: mailman [OPTIONS] COMMAND [ARGS]...
Try 'mailman -h' for help.

Error: Invalid value for '-C' / '--config': File '/usr/local/mailman/etc/mailman.cfg' does not exist.

Huh? What a tangled web we weave. Onwards!

# mkdir -p /usr/local/mailman/etc
# touch /usr/local/mailman/etc/mailman.cfg
# chown -R mailman:mailman /usr/local/mailman
# service mailman info
GNU Mailman 3.3.1 (Tom Sawyer)
Python 3.7.9 (default, Oct 15 2020, 08:08:23)
[Clang 10.0.1 (git@github.com:llvm/llvm-project.git llvmorg-10.0.1-0-gef32c611a
config file: /usr/local/mailman/etc/mailman.cfg
db url: sqlite:////usr/local/mailman/data/mailman.db
devmode: DISABLED
REST root url: http://localhost:8001/3.1/
REST credentials: restadmin:restpass

Apart from the empty configuration file, everything in /usr/local/mailman are exactly as they were last night. Let’s try to start the service. Note, Postfix isn’t installed yet.

# service mailman start
Starting Mailman's master runner
Generating MTA alias maps
sh: /usr/local/sbin/postmap: not found
sh: /usr/local/sbin/postmap: not found
Traceback (most recent call last):
  File "/usr/local/bin/mailman", line 11, in 
    load_entry_point('mailman==3.3.1', 'console_scripts', 'mailman')()
  File "/usr/local/lib/python3.7/site-packages/click/core.py", line 829, in __call__
    return self.main(*args, **kwargs)
  File "/usr/local/lib/python3.7/site-packages/click/core.py", line 782, in main
    rv = self.invoke(ctx)
  File "/usr/local/lib/python3.7/site-packages/mailman/bin/mailman.py", line 68, in invoke
    return super().invoke(ctx)
  File "/usr/local/lib/python3.7/site-packages/click/core.py", line 1259, in invoke
    return _process_result(sub_ctx.command.invoke(sub_ctx))
  File "/usr/local/lib/python3.7/site-packages/click/core.py", line 1066, in invoke
    return ctx.invoke(self.callback, **ctx.params)
  File "/usr/local/lib/python3.7/site-packages/click/core.py", line 610, in invoke
    return callback(*args, **kwargs)
  File "/usr/local/lib/python3.7/site-packages/click/decorators.py", line 21, in new_func
    return f(get_current_context(), *args, **kwargs)
  File "/usr/local/lib/python3.7/site-packages/mailman/commands/cli_control.py", line 109, in start
    call_name(config.mta.incoming).regenerate()
  File "/usr/local/lib/python3.7/site-packages/mailman/mta/postfix.py", line 133, in regenerate
    raise RuntimeError(NL.join(errors))
RuntimeError: command failure: /usr/local/sbin/postmap /usr/local/mailman/data/postfix_lmtp, 127, Unknown error: 127
command failure: /usr/local/sbin/postmap /usr/local/mailman/data/postfix_domains, 127, Unknown error: 127

Let’s add Postfix to the mix.

# pkg install postfix
Updating builder01 repository catalogue...
builder01 repository is up to date.
All repositories are up to date.
Checking integrity... done (0 conflicting)
The following 1 package(s) will be affected (of 0 checked):

New packages to be INSTALLED:
        postfix: 3.5.8,1

Number of packages to be installed: 1

The process will require 7 MiB more space.

Proceed with this action? [y/N]: y
[1/1] Installing postfix-3.5.8,1...
===> Creating groups.
Using existing group 'mail'.
Creating group 'maildrop' with gid '126'.
Creating group 'postfix' with gid '125'.
===> Creating users
Creating user 'postfix' with uid '125'.
===> Creating homedir(s)
[1/1] Extracting postfix-3.5.8,1: 100%

===============================================================
Postfix was *not* activated in /usr/local/etc/mail/mailer.conf!

To finish installation run the following commands:

  mkdir -p /usr/local/etc/mail
  install -m 0644 /usr/local/share/postfix/mailer.conf.postfix /usr/local/etc/mail/mailer.conf
===============================================================

=====
Message from postfix-3.5.8,1:

--
To use postfix instead of sendmail:
  - clear sendmail queue and stop the sendmail daemons

Run the following commands to enable postfix during startup:
  - sysrc postfix_enable="YES"
  - sysrc sendmail_enable="NONE"

If postfix is *not* already activated in /usr/local/etc/mail/mailer.conf
  - mv /usr/local/etc/mail/mailer.conf /usr/local/etc/mail/mailer.conf.old
  - install -m 0644 /usr/local/share/postfix/mailer.conf.postfix /usr/local/etc/mail/mailer.conf

Disable sendmail(8) specific tasks,
add the following lines to /etc/periodic.conf(.local):
  daily_clean_hoststat_enable="NO"
  daily_status_mail_rejects_enable="NO"
  daily_status_include_submit_mailq="NO"
  daily_submit_queuerun="NO"

If you are using SASL, you need to make sure that postfix has access to read
the sasldb file.  This is accomplished by adding postfix to group mail and
making the /usr/local/etc/sasldb* file(s) readable by group mail (this should
be the default for new installs).

We need to do some housekeeping. I edited /etc/rc.conf and /etc/periodic.conf as instructed.

# less -S /usr/local/etc/mail/mailer.conf
/usr/local/etc/mail/mailer.conf: No such file or directory
# install -m 0644 /usr/local/share/postfix/mailer.conf.postfix /usr/local/etc/mail/mailer.conf
install: /usr/local/etc/mail/mailer.conf: No such file or directory
# mkdir -p /usr/local/etc/mail
# install -m 0644 /usr/local/share/postfix/mailer.conf.postfix /usr/local/etc/mail/mailer.conf

While the correct instructions was displayed initially, we were also presented with some half-baked instructions further down the line.

Anyway, what does Mailman think of this new addition?

# service postfix start
postfix/postfix-script: starting the Postfix mail system
# service mailman restart
Restarting the Mailman runners

This looks very promising, no error messages. Here’s the process tree:

# ps -aduwwx | grep mailman
mailman  5861   0.0  0.9 86196 74100  -  Is   10:22     0:01.31 |-- /usr/local/bin/python3.7 /usr/local/bin/master -C /usr/local/mailman/etc/mailman.cfg
mailman  5873   0.0  1.0 93180 80632  -  S    10:22     0:02.20 | |-- python3.7: gunicorn: master [gunicorn] (python3.7)
mailman  5889   0.0  1.0 93180 80652  -  S    10:22     0:00.10 | | |-- python3.7: gunicorn: worker [gunicorn] (python3.7)
mailman  5890   0.0  1.0 93180 80664  -  S    10:22     0:00.09 | | `-- python3.7: gunicorn: worker [gunicorn] (python3.7)
mailman 10689   0.0  0.9 86476 74212  -  I    11:08     0:01.26 | |-- /usr/local/bin/python3.7 /usr/local/bin/runner -C /usr/local/mailman/etc/mailman.cfg --runner=retry:0:1
mailman 10690   0.0  0.9 86476 74212  -  S    11:08     0:01.76 | |-- /usr/local/bin/python3.7 /usr/local/bin/runner -C /usr/local/mailman/etc/mailman.cfg --runner=archive:0:1
mailman 10691   0.0  0.9 88216 76016  -  I    11:08     0:01.46 | |-- /usr/local/bin/python3.7 /usr/local/bin/runner -C /usr/local/mailman/etc/mailman.cfg --runner=bounces:0:1
mailman 10692   0.0  0.9 86476 74212  -  S    11:08     0:01.86 | |-- /usr/local/bin/python3.7 /usr/local/bin/runner -C /usr/local/mailman/etc/mailman.cfg --runner=in:0:1
mailman 10694   0.0  0.9 86476 74212  -  S    11:08     0:01.76 | |-- /usr/local/bin/python3.7 /usr/local/bin/runner -C /usr/local/mailman/etc/mailman.cfg --runner=digest:0:1
mailman 10695   0.0  0.9 86476 74300  -  S    11:08     0:01.76 | |-- /usr/local/bin/python3.7 /usr/local/bin/runner -C /usr/local/mailman/etc/mailman.cfg --runner=out:0:1
mailman 10696   0.0  0.9 86476 74216  -  S    11:08     0:01.76 | |-- /usr/local/bin/python3.7 /usr/local/bin/runner -C /usr/local/mailman/etc/mailman.cfg --runner=nntp:0:1
mailman 10697   0.0  0.9 86476 74216  -  S    11:08     0:01.77 | |-- /usr/local/bin/python3.7 /usr/local/bin/runner -C /usr/local/mailman/etc/mailman.cfg --runner=virgin:0:1
mailman 10698   0.0  0.9 86476 74212  -  S    11:08     0:01.74 | |-- /usr/local/bin/python3.7 /usr/local/bin/runner -C /usr/local/mailman/etc/mailman.cfg --runner=pipeline:0:1
mailman 10699   0.0  0.9 88692 74364  -  S    11:08     0:01.43 | |-- /usr/local/bin/python3.7 /usr/local/bin/runner -C /usr/local/mailman/etc/mailman.cfg --runner=lmtp:0:1
mailman 10700   0.0  0.9 86476 74240  -  S    11:08     0:01.77 | `-- /usr/local/bin/python3.7 /usr/local/bin/runner -C /usr/local/mailman/etc/mailman.cfg --runner=command:0:1

Let’s create the initial list.

# mailman create --language no --owner postmaster@[REDACTED] -no-notify mailman
Error: Illegal list name: mailman
# mailman create --language no --owner postmaster@[REDACTED] -no-notify mailman@lists.[REDACTED]
Created mailing list: mailman@lists.[REDACTED]

Note, the name of the list is its posting address, and there is only one leading hyphen in -no-notify. The latter must be a bug in Mailman 3.3.1.

These are more or less the defaults as shown by mailman conf:

[ARC] authserv_id:
[ARC] dkim: yes
[ARC] dmarc: yes
[ARC] domain:
[ARC] enabled: no
[ARC] privkey:
[ARC] selector:
[ARC] sig_headers: From, Sender, Reply-To, Subject, Date, Message-ID, To, Cc, MIME-Version, Content-Type, Content-Transfer-Encoding, Content-ID, Content-Description, Resent-Date, Resent-From, Resent-Sender, Resent-To, Resent-Cc, Resent-Message-ID, In-Reply-To, References, List-Id, List-Help, List-Unsubscribe, List-Subscribe, List-Post, List-Owner, List-Archive
[ARC] trusted_authserv_ids:
[antispam] header_checks:
[antispam] jump_chain: hold
[archiver.mail_archive] class: mailman.archiving.mailarchive.MailArchive
[archiver.mail_archive] clobber_date: maybe
[archiver.mail_archive] clobber_skew: 1d
[archiver.mail_archive] configuration: python:mailman.config.mail_archive
[archiver.mail_archive] enable: no
[archiver.master] class:
[archiver.master] clobber_date: maybe
[archiver.master] clobber_skew: 1d
[archiver.master] configuration: changeme
[archiver.master] enable: no
[archiver.mhonarc] class: mailman.archiving.mhonarc.MHonArc
[archiver.mhonarc] clobber_date: maybe
[archiver.mhonarc] clobber_skew: 1d
[archiver.mhonarc] configuration: python:mailman.config.mhonarc
[archiver.mhonarc] enable: no
[archiver.prototype] class: mailman.archiving.prototype.Prototype
[archiver.prototype] clobber_date: maybe
[archiver.prototype] clobber_skew: 1d
[archiver.prototype] configuration: changeme
[archiver.prototype] enable: no
[bounces] register_bounces_every: 15m
[database] class: mailman.database.sqlite.SQLiteDatabase
[database] debug: no
[database] url: sqlite:///$DATA_DIR/mailman.db
[devmode] enabled: no
[devmode] recipient:
[devmode] testing: no
[devmode] wait: 60s
[digests] mime_digest_keep_headers:
Date From To Cc Subject Message-ID Keywords
In-Reply-To References Content-Type MIME-Version
Content-Transfer-Encoding Precedence Reply-To
Message List-Post
[digests] plain_digest_keep_headers:
Message Date From
Subject To Cc
Message-ID Keywords
Content-Type
[dmarc] cache_lifetime: 7d
[dmarc] org_domain_data_url: https://publicsuffix.org/list/public_suffix_list.dat
[dmarc] resolver_lifetime: 5s
[dmarc] resolver_timeout: 3s
[language.ar] charset: utf-8
[language.ar] description: Arabic
[language.ar] enabled: yes
[language.ast] charset: iso-8859-1
[language.ast] description: Asturian
[language.ast] enabled: yes
[language.ca] charset: utf-8
[language.ca] description: Catalan
[language.ca] enabled: yes
[language.cs] charset: iso-8859-2
[language.cs] description: Czech
[language.cs] enabled: yes
[language.da] charset: iso-8859-1
[language.da] description: Danish
[language.da] enabled: yes
[language.de] charset: utf-8
[language.de] description: German
[language.de] enabled: yes
[language.el] charset: iso-8859-7
[language.el] description: Greek
[language.el] enabled: yes
[language.es] charset: iso-8859-1
[language.es] description: Spanish
[language.es] enabled: yes
[language.et] charset: iso-8859-15
[language.et] description: Estonian
[language.et] enabled: yes
[language.eu] charset: iso-8859-15
[language.eu] description: Euskara
[language.eu] enabled: yes
[language.fi] charset: iso-8859-1
[language.fi] description: Finnish
[language.fi] enabled: yes
[language.fr] charset: utf-8
[language.fr] description: French
[language.fr] enabled: yes
[language.gl] charset: utf-8
[language.gl] description: Galician
[language.gl] enabled: yes
[language.he] charset: utf-8
[language.he] description: Hebrew
[language.he] enabled: yes
[language.hr] charset: iso-8859-2
[language.hr] description: Croatian
[language.hr] enabled: yes
[language.hu] charset: iso-8859-2
[language.hu] description: Hungarian
[language.hu] enabled: yes
[language.ia] charset: iso-8859-15
[language.ia] description: Interlingua
[language.ia] enabled: yes
[language.it] charset: iso-8859-1
[language.it] description: Italian
[language.it] enabled: yes
[language.ja] charset: euc-jp
[language.ja] description: Japanese
[language.ja] enabled: yes
[language.ko] charset: euc-kr
[language.ko] description: Korean
[language.ko] enabled: yes
[language.lt] charset: iso-8859-13
[language.lt] description: Lithuanian
[language.lt] enabled: yes
[language.nl] charset: iso-8859-1
[language.nl] description: Dutch
[language.nl] enabled: yes
[language.no] charset: iso-8859-1
[language.no] description: Norwegian
[language.no] enabled: yes
[language.pl] charset: iso-8859-2
[language.pl] description: Polish
[language.pl] enabled: yes
[language.pt] charset: iso-8859-1
[language.pt] description: Protuguese
[language.pt] enabled: yes
[language.pt_BR] charset: iso-8859-1
[language.pt_BR] description: Protuguese (Brazil)
[language.pt_BR] enabled: yes
[language.ro] charset: iso-8859-2
[language.ro] description: Romanian
[language.ro] enabled: yes
[language.ru] charset: koi8-r
[language.ru] description: Russian
[language.ru] enabled: yes
[language.sk] charset: utf-8
[language.sk] description: Slovak
[language.sk] enabled: yes
[language.sl] charset: iso-8859-2
[language.sl] description: Slovenian
[language.sl] enabled: yes
[language.sr] charset: utf-8
[language.sr] description: Serbian
[language.sr] enabled: yes
[language.sv] charset: iso-8859-1
[language.sv] description: Swedish
[language.sv] enabled: yes
[language.tr] charset: iso-8859-9
[language.tr] description: Turkish
[language.tr] enabled: yes
[language.uk] charset: utf-8
[language.uk] description: Ukrainian
[language.uk] enabled: yes
[language.vi] charset: utf-8
[language.vi] description: Vietnamese
[language.vi] enabled: yes
[language.zh_CN] charset: utf-8
[language.zh_CN] description: Chinese
[language.zh_CN] enabled: yes
[language.zh_TW] charset: utf-8
[language.zh_TW] description: Chinese (Taiwan)
[language.zh_TW] enabled: yes
[logging.archiver] datefmt: %b %d %H:%M:%S %Y
[logging.archiver] format: %(asctime)s (%(process)d) %(message)s
[logging.archiver] level: info
[logging.archiver] path: mailman.log
[logging.archiver] propagate: no
[logging.bounce] datefmt: %b %d %H:%M:%S %Y
[logging.bounce] format: %(asctime)s (%(process)d) %(message)s
[logging.bounce] level: info
[logging.bounce] path: bounce.log
[logging.bounce] propagate: no
[logging.config] datefmt: %b %d %H:%M:%S %Y
[logging.config] format: %(asctime)s (%(process)d) %(message)s
[logging.config] level: info
[logging.config] path: mailman.log
[logging.config] propagate: no
[logging.database] datefmt: %b %d %H:%M:%S %Y
[logging.database] format: %(asctime)s (%(process)d) %(message)s
[logging.database] level: warn
[logging.database] path: mailman.log
[logging.database] propagate: no
[logging.debug] datefmt: %b %d %H:%M:%S %Y
[logging.debug] format: %(asctime)s (%(process)d) %(message)s
[logging.debug] level: info
[logging.debug] path: debug.log
[logging.debug] propagate: no
[logging.error] datefmt: %b %d %H:%M:%S %Y
[logging.error] format: %(asctime)s (%(process)d) %(message)s
[logging.error] level: info
[logging.error] path: mailman.log
[logging.error] propagate: no
[logging.fromusenet] datefmt: %b %d %H:%M:%S %Y
[logging.fromusenet] format: %(asctime)s (%(process)d) %(message)s
[logging.fromusenet] level: info
[logging.fromusenet] path: mailman.log
[logging.fromusenet] propagate: no
[logging.http] datefmt: %b %d %H:%M:%S %Y
[logging.http] format: %(t)s "%(r)s" %(s)s %(b)s "%(f)s" "%(a)s"
[logging.http] level: info
[logging.http] path: mailman.log
[logging.http] propagate: no
[logging.locks] datefmt: %b %d %H:%M:%S %Y
[logging.locks] format: %(asctime)s (%(process)d) %(message)s
[logging.locks] level: info
[logging.locks] path: mailman.log
[logging.locks] propagate: no
[logging.mischief] datefmt: %b %d %H:%M:%S %Y
[logging.mischief] format: %(asctime)s (%(process)d) %(message)s
[logging.mischief] level: info
[logging.mischief] path: mailman.log
[logging.mischief] propagate: no
[logging.plugins] datefmt: %b %d %H:%M:%S %Y
[logging.plugins] format: %(asctime)s (%(process)d) %(message)s
[logging.plugins] level: info
[logging.plugins] path: plugins.log
[logging.plugins] propagate: no
[logging.root] datefmt: %b %d %H:%M:%S %Y
[logging.root] format: %(asctime)s (%(process)d) %(message)s
[logging.root] level: info
[logging.root] path: mailman.log
[logging.root] propagate: no
[logging.runner] datefmt: %b %d %H:%M:%S %Y
[logging.runner] format: %(asctime)s (%(process)d) %(message)s
[logging.runner] level: info
[logging.runner] path: mailman.log
[logging.runner] propagate: no
[logging.smtp] datefmt: %b %d %H:%M:%S %Y
[logging.smtp] every: $msgid smtp to $listname for $recip recips, completed in $time seconds
[logging.smtp] failure: $msgid delivery to $recip failed with code $smtpcode, $smtpmsg
[logging.smtp] format: %(asctime)s (%(process)d) %(message)s
[logging.smtp] level: info
[logging.smtp] path: smtp.log
[logging.smtp] propagate: no
[logging.smtp] refused: $msgid post to $listname from $sender, $size bytes, $refused failures
[logging.smtp] success: $msgid post to $listname from $sender, $size bytes
[logging.subscribe] datefmt: %b %d %H:%M:%S %Y
[logging.subscribe] format: %(asctime)s (%(process)d) %(message)s
[logging.subscribe] level: info
[logging.subscribe] path: mailman.log
[logging.subscribe] propagate: no
[logging.vette] datefmt: %b %d %H:%M:%S %Y
[logging.vette] format: %(asctime)s (%(process)d) %(message)s
[logging.vette] level: info
[logging.vette] path: mailman.log
[logging.vette] propagate: no
[mailman] cache_life: 7d
[mailman] default_language: en
[mailman] email_commands_max_lines: 10
[mailman] filtered_messages_are_preservable: no
[mailman] html_to_plain_text_command: /usr/local/bin/lynx -dump $filename
[mailman] layout: home
[mailman] listname_chars: [-_.0-9a-z]
[mailman] noreply_address: noreply
[mailman] pending_request_life: 3d
[mailman] post_hook:
[mailman] pre_hook:
[mailman] sender_headers: from from_ reply-to sender
[mailman] site_owner: changeme@example.com
[mta] configuration: python:mailman.config.postfix
[mta] delivery_retry_period: 5d
[mta] incoming: mailman.mta.postfix.LMTP
[mta] lmtp_host: 127.0.0.1
[mta] lmtp_port: 8024
[mta] max_autoresponses_per_day: 10
[mta] max_delivery_threads: 0
[mta] max_recipients: 500
[mta] max_sessions_per_connection: 0
[mta] outgoing: mailman.mta.deliver.deliver
[mta] remove_dkim_headers: no
[mta] smtp_host: localhost
[mta] smtp_pass:
[mta] smtp_port: 25
[mta] smtp_secure_mode: smtp
[mta] smtp_user:
[mta] smtp_verify_cert: yes
[mta] smtp_verify_hostname: yes
[mta] verp_confirm_format: $address+$cookie
[mta] verp_confirm_regexp: ^(.*<)?(?P[^+]+?)\+(?P[^@]+)@.*$
[mta] verp_confirmations: no
[mta] verp_delimiter: +
[mta] verp_delivery_interval: 0
[mta] verp_format: ${bounces}+${local}=${domain}
[mta] verp_personalized_deliveries: no
[mta] verp_probe_format: $bounces+$token@$domain
[mta] verp_probe_regexp: ^(?P[^+]+?)\+(?P[^@]+)@.*$
[mta] verp_probes: no
[mta] verp_regexp: ^(?P[^+]+?)\+(?P[^=]+)=(?P[^@]+)@.*$
[nntp] host:
[nntp] password:
[nntp] port:
[nntp] remove_headers:
nntp-posting-host nntp-posting-date x-trace
x-complaints-to xref date-received posted
posting-version relay-version received
[nntp] rewrite_duplicate_headers:
To X-Original-To
CC X-Original-CC
Content-Transfer-Encoding X-Original-Content-Transfer-Encoding
MIME-Version X-MIME-Version
[nntp] user:
[passwords] configuration: python:mailman.config.passlib
[passwords] password_length: 8
[plugin.master] class:
[plugin.master] component_package:
[plugin.master] configuration:
[plugin.master] enabled: no
 banner: Welcome to the GNU Mailman shell
Use commit() to commit changes.
Use abort() to discard changes since the last commit.
Exit with ctrl+D does an implicit commit() but exit() does not.
 history_file:
 prompt: >>>
 use_ipython: no
[styles] default: legacy-default
[webservice] admin_pass: restpass
[webservice] admin_user: restadmin
[webservice] api_version: 3.1
[webservice] configuration: python:mailman.config.gunicorn
[webservice] hostname: localhost
[webservice] port: 8001
[webservice] show_tracebacks: yes
[webservice] use_https: no
[webservice] workers: 2

These are the settings that must be changed or emphasized in my opinion:

  • [mailman] default_language: no
  • [mailman] layout: home
  • [mailman] site_owner: postmaster@[REDACTED]
  • [mta] remove_dkim_headers: yes
    (I hope FreeBSD’s future mailling list system will strip away DKIM headers.)

In other words, /usr/local/mailman/etc/mailman.cfg should end up looking like this:

[mailman]
default_language: no
layout: home
site_owner: postmaster@[REDACTED]

[mta]
remove_dkim_headers: yes

Postfix must be able to relay received mail over to Mailman. Thus, /usr/local/etc/postfix/main.cf must be changed accordingly:

--- etc/postfix/main.cf.sample	2020-11-26 14:02:06.000000000 +0100
+++ etc/postfix/main.cf	2020-12-03 14:28:43.652690000 +0100
@@ -401,6 +401,8 @@
 #alias_maps = hash:/etc/aliases
 #alias_maps = hash:/etc/aliases, nis:mail.aliases
 #alias_maps = netinfo:/aliases
+#alias_maps = hash:/etc/mail/aliases
+alias_maps = hash:/etc/mail/aliases, hash:/usr/local/mailman/data/postfix_lmtp
 
 # The alias_database parameter specifies the alias database(s) that
 # are built with "newaliases" or "sendmail -bi".  This is a separate
@@ -410,6 +412,7 @@
 #alias_database = dbm:/etc/aliases
 #alias_database = hash:/etc/aliases
 #alias_database = hash:/etc/aliases, hash:/opt/majordomo/aliases
+#alias_database = hash:/etc/mail/aliases, hash:/usr/local/mailman/data/postfix_lmtp
 
 # ADDRESS EXTENSIONS (e.g., user+foo)
 #
@@ -501,6 +504,7 @@
 #fallback_transport = lmtp:unix:/file/name
 #fallback_transport = cyrus
 #fallback_transport =
+fallback_transport = lmtp:127.0.0.1:8024
 
 # The luser_relay parameter specifies an optional destination address
 # for unknown recipients.  By default, mail for unknown@$mydestination,

I admit I’m in unknown territory. Maybe simply the format of /usr/local/mailman/data/postfix_lmtp is foreign to Postfix. At this point I’m able to send and receive mail. The next step is the web interface(s) for easier administration and for mail archives.


2020-12-04

It sure helps to follow the MTA guide, so this is how Postfix’ main.cf configuration file differ from the sample file.

--- etc/postfix/main.cf.sample	2020-11-26 14:02:06.000000000 +0100
+++ etc/postfix/main.cf	2020-12-04 17:14:23.339697000 +0100
@@ -220,7 +220,7 @@
 # In the left-hand side, specify a bare username, an @domain.tld
 # wild-card, or specify a user@domain.tld address.
 #.
-#local_recipient_maps = unix:passwd.byname $alias_maps
+local_recipient_maps = unix:passwd.byname $alias_maps hash:/usr/local/mailman/data/postfix_lmtp
 #local_recipient_maps = proxy:unix:passwd.byname $alias_maps
 #local_recipient_maps =
 
@@ -397,9 +397,9 @@
 # It will take a minute or so before changes become visible.  Use
 # "postfix reload" to eliminate the delay.
 #
-#alias_maps = dbm:/etc/aliases
-#alias_maps = hash:/etc/aliases
-#alias_maps = hash:/etc/aliases, nis:mail.aliases
+#alias_maps = dbm:/etc/mail/aliases
+alias_maps = hash:/etc/mail/aliases
+#alias_maps = hash:/etc/mail/aliases, nis:mail.aliases
 #alias_maps = netinfo:/aliases
 
 # The alias_database parameter specifies the alias database(s) that
@@ -407,9 +407,9 @@
 # configuration parameter, because alias_maps (see above) may specify
 # tables that are not necessarily all under control by Postfix.
 #
-#alias_database = dbm:/etc/aliases
-#alias_database = hash:/etc/aliases
-#alias_database = hash:/etc/aliases, hash:/opt/majordomo/aliases
+#alias_database = dbm:/etc/mail/aliases
+#alias_database = hash:/etc/mail/aliases
+#alias_database = hash:/etc/mail/aliases, hash:/opt/majordomo/aliases
 
 # ADDRESS EXTENSIONS (e.g., user+foo)
 #
@@ -420,7 +420,7 @@
 # Basically, the software tries user+foo and .forward+foo before
 # trying user and .forward.
 #
-#recipient_delimiter = +
+recipient_delimiter = +
 
 # DELIVERY TO MAILBOX
 #
@@ -501,6 +501,7 @@
 #fallback_transport = lmtp:unix:/file/name
 #fallback_transport = cyrus
 #fallback_transport =
+#fallback_transport = lmtp:127.0.0.1:8024
 
 # The luser_relay parameter specifies an optional destination address
 # for unknown recipients.  By default, mail for unknown@$mydestination,
@@ -675,3 +676,10 @@
 inet_protocols = ipv4
 meta_directory = /usr/local/libexec/postfix
 shlib_directory = /usr/local/lib/postfix
+
+# https://docs.mailman3.org/projects/mailman/en/latest/src/mailman/docs/mta.html
+owner_request_special = no
+transport_maps = hash:/usr/local/mailman/data/postfix_lmtp
+relay_domains = hash:/usr/local/mailman/data/postfix_domains
+
+# EOF

I wonder if I can find a description of the Python object representation of each list. Right now I feel I’m ready to edit some settings.

The to-do list include:

  1. Get Postorius up & running.
  2. Get some kind of archiver, maybe HyperKitty, up & running.
  3. Get rid of footer_uri (previously known as msg_footer).
  4. Adjust other parameters of each list.
  5. Add members to each list.
  6. Inform the users on how to proceed from here.

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes:

<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>