Schleuder3 setup

| Schleuder is an email hub for groups. It has also been described as a gpg-enabled mailinglist with remailer-capabilities. It is designed to serve as a tool for group communication: subscribers can communicate encrypted (and pseudonymously) among themselves, receive emails from non-subscribers and send emails to non-subscribers via the list.

Schleuder3 has been setup by dkg on for testing. dkg describes this setup as "a minimally-configured debian stretch machine, using the schleuder and schleuder-cli packages from unstable. I removed basically every package that I could, and configured it entirely with systemd where possible. This means no ifupdown, no cron, no rsyslog, no ntp, no acpid, no isc-dhcp-client, systemd-resolved as a local DNS caching stub resolver, etc."

The following is an attempt to document the steps necessary to recreate the same conditions and configuration currently active in herman. These are not the exact steps and order taken during the original install.

Debian install

Start from an initial install of Debian stretch (currently debian testing) with NONE of the tasksel tasks activated. Make sure to deselect both the standard and ssh-server tasks during install. Login through the console to complete the initial steps manually.

No recommends

Make sure all subsequent package installs with apt default to using the --no-install-recommends preference by adding a line to apt.conf

0 herman:~# echo 'APT::Install-Recommends "0";' >> /etc/apt/apt.conf

Installing packages

Install the following packages. Some of these packages are essential to the application and others appear to be utilities that were used for debugging during the configuration process of schleuder. It may be possible to reduce this list.

0 herman:~# apt install openssh-server systemd-cron deborphan psmisc postfix tor cryptsetup libpam-systemd initscripts dbus dbus-user-session publicsuffix haveged screen less file lsof man-db manpages acl iputils-ping libcap2-bin bsd-mailx sqlite3 bash-completion

Removing packages

Remove the following unnecessary or undesired packages.

0 herman:~# apt purge rsyslog logrotate cron tasksel installation-report wamerican console-setup keyboard-configuration kbd isc-dhcp-client isc-dhcp-common discover laptop-detect ifupdown dmidecode eject netcat-traditional traceroute usbutils iptables pciutils reportbug os-prober gcc-5-base

The following step is subject to change in the future.

0 herman:~# apt purge linux-image-4.8.0-2-amd64

Autoremove and purge automatically installed packages that are no longer necessary.

0 herman:~# apt autoremove --purge

Purge any orphaned packages. Repeat this next step as many times as necessary until there are no longer any results.

0 herman:~# apt purge $(deborphan)

Install packages from unstable repository

The use of these packages from the unstable repository is subject to change in the future.

Add the unstable repository.

0 herman:~# echo "deb unstable main" > /etc/apt/sources.list.d/unstable.list
0 herman:~# echo -e "Package: *\nPin: release a=unstable\nPin-Priority: 200" >> /etc/apt/preferences.d/limit-unstable
0 herman:~# apt update

Install the unstable version of the following packages. dkg's patch to schleuder to avoid a dependency on cron has already been applied in newest unstable releases.

0 herman:~# apt install gnupg/unstable schleuder-cli/unstable schleuder/unstable

At this point the above setup should give us a package list very close to what is on herman now with the exception of the emacs-nox package and its dependencies.


Since ifupdown has been removed and will be used instead. A new .network file should created as /etc/systemd/network/

The contents of this file look something like this, although the original ip numbers have been removed here.



Notice the interface is not named eth0. The version of Systemd in Debian Stretch now uses | Predictable Network Interface Names , which automatically assigns static names to network devices. udev is responsible for | which device gets which name . You can get a list of available network interfaces using commands ls /sys/class/net or ip link

After creating the new .network file you need to restart systemd-networkd

  systemctl restart systemd-networkd

At this point the server should be accessible over ssh.

No logging to disk

When both journald and rsyslog are installed, the default is that journald spits out all its messages to syslog and syslog writes them to disk. Without syslog, journald is responsible for writing whatever needs to be written. See configuration choices for /etc/systemd/journald.conf with man journald.conf

By default Storage=auto is set which basically means "if /var/log/journal is present, then write the logs to disk; if it's not, then write them to /run" note that /run is a tmpfs, meaning it's ephemeral, and disappears when the machine loses power so since we've avoided placing a permanent journal everything is being logged in /run

Some services implement their own mechanisms for writing logs, we need to deal with these individually. The following command will show any processes still holding open files in /var/log

find /var/log/ -mount -type f -print0 | xargs -0 lsof

The only listing that should still appear at this point is the debian-tor user writing to /var/log/tor/log . This can be stopped by uncomment the following line in /etc/tor/torrc

  Log notice syslog

Why syslog? syslog is just an interface -- something listening on a socket at /dev/log where journald also listens

After making the above change to /etc/tor/torrc restart tor.

  systemctl restart tor

The tor daemon should log to syslog by default in | future versions so the above fix is temporary.

tmpfs for /tmp

Setup the /tmp directory with temporary file storage facility so that all writes to /tmp are written to volatile memory and not to disk. This has the following benefits:

  • Having globally-writable directories in filesystems opens up a few different classes of vulnerabilities and bugs based on hardlinks and undeleted data. Separating the /tmp filesystem from the root filesystem avoids those classes.
  • /tmp is expected to be cleared automatically at boot. Using memory as the backing store does that automatically.
  • using memory reduces the amount of disk I/O

Add the following line to the end of /etc/fstab

tmpfs /tmp tmpfs mode=1777,nosuid,nodev 0 0


Add these 2 lines to the end of /etc/postfix/

schleuder  unix  -       n       n       -       -       pipe
  flags=DRhu user=schleuder argv=/usr/bin/schleuder work ${recipient}

This says "if the transport is for schleuder, then pipe the message to a process owned by user "schleuder" that runs "/usr/bin/schleuder work ${recipient}"

In /etc/postfix/ edit the myhostname and mydestination variables appropriately.

myhostname =
mydestination = $myhostname,

Then at the bottom of /etc/postfix/ add the following lines

virtual_transport = schleuder
virtual_mailbox_domains =
virtual_alias_maps = hash:/etc/postfix/virtual_aliases
virtual_mailbox_maps = sqlite:/etc/postfix/
schleuder_destination_recipient_limit = 1
compatibility_level = 2

The above portion of the config is devoted to figuring out when to trigger this transport. First: dedicate postfix's "virtual transport" to schleuder itself and tell postfix that anything coming to should be handled by the virtual transport

The contents of the file /etc/postfix/virtual_aliases is a list of first-pass "catch-all" addresses, forwarding the usual things to their counterparts.

virtual_mailbox_maps is what postfix uses to look up whether an address is valid. If it's not in that list, it declines the message at attempted SMTP delivery time. This directive tells postfix "look up the incoming address to see whether schleuder is willing to handle it; if it is not, then don't even bother feeding it to schleuder". The contents of /etc/postfix/ look like this:

dbpath = /var/lib/schleuder/db.sqlite

query = select 'present' where '%s' in (
  select email from lists union
  select replace(email, '@', '-bounces@') from lists union
  select replace(email, '@', '-owner@') from lists union
  select replace(email, '@', '-request@') from lists union
  select replace(email, '@', '-sendkey@') from lists)

The above schleuder/postfix sqlite integration is now | shipping upstream:

In the this line schleuder_destination_recipient_limit = 1 means, "if a message comes in headed for the schleuder transport and it is headed for multiple recipients, feed it to each of them separately, one at a time." compatibility_level = 2 just disables backwards compatibility.

Schleuder configuration

Lots of useful information in Schleuder3's official documentation.

The following commands and file edits should be performed under the dedicated schleuder user created by the installer.

0 root@herman:~# su - schleuder -s /bin/bash

Schleuder reads its basic settings from a file that it by default expects at /etc/schleuder/schleuder.yml The only initial change from the default settings that has been made there on herman is to comment out the default keyserver. #keyserver:

The Schleuder API is provided by schleuder-api-daemon. Configuration clients (schleuder-web, schleuder-cli) use it to access information about lists, subscriptions, and keys. schleuder-api-daemon uses transport encyrption (TLS) for all connections. The required TLS-certifcates should have been generated during the setup (schleuder install) but can be regenerated if necessary by running the following command as schleuder user: schleuder cert generate

In order to verify the connection, each client needs to know the fingerprint of the API-certificate. Execute the following command to receive the fingerprint of the current cert.

schleuder@herman:~$ schleuder cert fingerprint

The Schleuder API uses API-keys to authenticate clients. To enable a client to connect, their API-key must be added to the section valid_api_keys in Schleuder’s configuration file. You can generate new API-keys by executing the following command:

schleuder@herman:~$ schleuder new_api_key

All current schleuder api keys should remain private. Always use secure channels to transport this information.

Add the new api key to file /etc/schleuder/schleuder.yml below the directive valid_api_keys:

  - abcdef...
  - zyxwvu...

Restart the schleuder api daemon as root to apply changes.

0 root@herman:~# systemctl restart schleuder-api-daemon

Dedicated user for schleuder-cli

Create a separate "schleuder-manager" user that is able to talk to the API but doesn't have r/w access to the sqlitedb

adduser schleuder-manager --gecos 'Schleuder Manager,,,' --disabled-password

Become the new user, create a new configuration file and ensure that only the schleuder-manager user has read and write access to this file.

0 root@herman:~# su - schleuder-manager -s /bin/bash
schleuder-manager@herman:~$ touch /home/schleuder-manager/.schleuder-cli/schleuder-cli.yml
schleuder-manager@herman:~$ chmod 600 /home/schleuder-manager/.schleuder-cli/schleuder-cli.yml

Edit this file with details for the connection of the schleuder-cli client to the schleuder-api-daemon including the tls fingerprint and api key retrieved above.

host: localhost
port: 4443
tls_fingerprint: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
api_key: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

The api_key has been removed in this example and should not be public. Always use secure and authenticated channels to transport this information to ensure both the confidentiality of the api_key and the integrity of the tls_fingerprint

Check that the schleuder-cli client is able to establish a connection to the api.

schleuder-manager@herman:~$ schleuder-cli version -r

Create an .ssh/authorized_keys file to allow authorized members of the support team to connect remotely and use the schleuder-cli.

We need to document the preferred method for adding new keys to to schleuder-manager user's .ssh/authorized_keys

List creation and management

Review the output of schleuder-cli help.

Key management

Adding or updating keys. Schleuder does not pull in key from keyservers.

If you are an admin for a list and still have access to the list you can use the e-mail commands documented here:

Alternatively if you have access to schleuder-manager user on herman you can use the schleuder-cli client o add new or update keys to a list.


Export your public key and copy to herman

jaimev@mayday:~$ gpg --armor --export >
jaimev@mayday:~$ scp

Then as schleuder-manager user on herman import the key to the appropriate list.

schleuder-manager@herman:~$ schleuder-cli keys import /home/schleuder-manager/
schleuder-manager@herman:~$ rm
Last modified 4 years ago Last modified on Jan 7, 2018, 12:41:49 PM