Skip to content
Snippets Groups Projects
Julian's avatar
Julian authored
Add README to document configuration

See merge request !2
bfe7cd92
History

Postorius (Mailman 3) LDAP Membership Management

This Mailman 3 extension enables single-sign-on and list membership synchronisation with uffd, using uffd-ldapd and uffd-nginxauth.

User accounts and mailing list memberships are periodically synchronised with uffd group membership. Authentication is handled using Django's PersistentRemoteUserMiddleware, which only requires the login URLs to be proxied through the authentication proxy.

The CCCV ansible role may also be helpful when configuring this.

Installing

These examples assume that Mailman 3, uffd-ldapd, and uffd-nginxauth are installed as Debian packages. You should have a basic working Mailman 3 setup before attempting to install this. Nginx should be used as the reverse proxy, and uffd-ldapd and uffd-nginxauth should be installed and configured.

The python3-django-auth-ldap package should also be installed from apt.

This postorius_ldap_membership_management package needs to be accessible by the Mailman installation. This is (currently) possible by checking out the git repo and running the following command as root to install it in the global python packages:

# python3 ./setup.py install

Configuration

We assume your HTTP hostname is lists.example.org and your uffd-ldapd DN is set to dc=example,dc=org.

Nginx Configuration

This should probably be in /etc/nginx/sites-enabled/lists.example.org.conf:

location /mailman3/static/favicon.ico {
    alias /var/lib/mailman3/web/static/postorius/img/favicon.ico;
    index     index.html index.htm index.php;
}

location /mailman3/static {
    alias /var/lib/mailman3/web/static;
    index     index.html index.htm index.php;
}

location /oauthproxy/ {
    rewrite  ^/oauthproxy/(.*) /$1 break;
    include /etc/nginx/uwsgi_params;
    # The OAuth client credentials must match those configured in uffd.
    uwsgi_param HTTP_X_REDIRECT_URI "https://lists.example.org/oauthproxy/callback";
    uwsgi_param HTTP_X_CLIENT_ID "<uffd_client_id>";
    uwsgi_param HTTP_X_CLIENT_SECRET "<uffd_client_secret>";
    # those options are needed to have working POST requests
    uwsgi_param HTTP_CONTENT_LENGTH "";
    uwsgi_param REQUEST_METHOD "GET";
    uwsgi_pass_request_body off;

    uwsgi_pass unix:///run/uwsgi/app/uffd-nginxauth/socket;
}

location @error401 {
    return 302 https://lists.example.org/oauthproxy/login?rawurl=https://lists.example.org$request_uri;
}

location ~ ^/(accounts|admin)/login/ {
    error_page 401 = @error401;
    auth_request /oauthproxy/auth;
    auth_request_set $auth_header $upstream_http_OAUTH_USER_NICKNAME;
    uwsgi_param REMOTE_USER $auth_header;
    include /etc/nginx/uwsgi_params;
    uwsgi_pass unix:///run/mailman3-web/uwsgi.sock;
}

location / {
    include /etc/nginx/uwsgi_params;
    uwsgi_pass unix:///run/mailman3-web/uwsgi.sock;
}

You can check https://lists.example.org/oauthproxy/status to confirm that uffd-nginxauth is configured successfully.

Mailman3 Configuration

The following changes need to be made to the Mailman web configuration at /etc/mailman3/mailman-web.py:

# Append the app to the list of installed apps:
INSTALLED_APPS = (
    ...
    'postorius_ldap_membership_management'
)

# Copy the MIDDLEWARE statement from /usr/share/mailman3-web/settings.py and append:
MIDDLEWARE = (
    ...
    'django.contrib.auth.middleware.PersistentRemoteUserMiddleware',
    'postorius_ldap_membership_management.middleware.SessionTimeoutMiddleware',
)

# Replace AUTHENTICATION_BACKENDS
AUTHENTICATION_BACKENDS = (
    'postorius_ldap_membership_management.backends.LdapRemoteUserBackend',
)

# Set session expiry and disable account email verification
SESSION_EXPIRE_SECONDS=3600
ACCOUNT_EMAIL_VERIFICATION = 'none'

Now we configure the LDAP settings in the same file. These configuration options are from the django-auth-ldap module.

In this example, list_users is the uffd group which allows access to the mailing list interface, and list_admin provides Mailman admin privileges:

from django_auth_ldap.config import *

AUTH_LDAP_SERVER_URI = "ldap://localhost"
AUTH_LDAP_BIND_DN = "cn=service,ou=system,dc=example,dc=org"
# Bind password set in uffd-ldapd config
AUTH_LDAP_BIND_PASSWORD = "<bind_password>"

AUTH_LDAP_CONNECTION_OPTIONS = {}
AUTH_LDAP_USER_DN_TEMPLATE = "uid=%(user)s,ou=users,dc=example,dc=org"
AUTH_LDAP_USER_SEARCH = None
AUTH_LDAP_USER_SEARCH_ALL_NAME = LDAPSearch(
    "ou=users,dc=example,dc=org",
    ldap.SCOPE_ONELEVEL,
    "(&(objectClass=posixAccount)(memberOf=cn=list_users,ou=groups,dc=example,dc=org))",
    ["uid"]
)
AUTH_LDAP_USER_ATTR_MAP = {"first_name": "cn", "email": "mail"}

AUTH_LDAP_GROUP_SEARCH = LDAPSearch(
    "ou=groups,dc=example,dc=org",
    ldap.SCOPE_ONELEVEL,
    "(objectClass=groupOfUniqueNames)"
)
AUTH_LDAP_GROUP_TYPE = GroupOfUniqueNamesType()

AUTH_LDAP_ALWAYS_UPDATE_USER = True
AUTH_LDAP_CACHE_TIMEOUT = 0

AUTH_LDAP_FIND_GROUP_PERMS = True
AUTH_LDAP_REQUIRE_GROUP = "cn=list_users,dc=example,dc=org"
AUTH_LDAP_DENY_GROUP = ""
AUTH_LDAP_USER_FLAGS_BY_GROUP = {
    "is_active": "cn=list_users,ou=groups,dc=example,dc=org",
    "is_staff": "cn=list_admin,ou=groups,dc=example,dc=org",
    "is_superuser": "cn=list_admin,ou=groups,dc=example,dc=org",
}

Periodic jobs

At this point, the periodic jobs can be set up. These two commands should be run periodically:

/usr/bin/mailman-web syncldapusers
/usr/bin/mailman-web syncldapmemberships

Configuring Mailing List Membership

Now we can configure synchronisation of mailing list membership. Note that the mailing list must be created in the Mailman web interface first.

This is also configured in mailman-web.py. In this example, the list mygroup@lists.example.org is configured. Members of mygroup are added as members of the list, mygroup_moderators are moderators and mygroup_owners are owners.

LDAP_MEMBERSHIP_SYNC = {
    'mygroup@lists.example.org': {
        'ldap': {
            'member': {
                'enabled': True,
                'dn': 'ou=users,dc=example,dc=org',
                'filter': "(&(objectClass=posixAccount)(memberOf=cn=mygroup,ou=groups,dc=example,dc=org))",
                'username_attr': 'uid'
            },
            'moderator': {
                'enabled': True,
                'dn': 'ou=users,dc=example,dc=org',
                'filter': "(&(objectClass=posixAccount)(memberOf=cn=mygroup_moderators,ou=groups,dc=example,dc=org))",
                'username_attr': 'uid'
            },
            'owner': {
                'enabled': True,
                'dn': 'ou=users,dc=example,dc=org',
                'filter': "(&(objectClass=posixAccount)(memberOf=cn=mygroup_owners,ou=groups,dc=example,dc=org))",
                'username_attr': 'uid'
            }
        }
    }
}

Running the mailman-web syncldapmemberships command should now synchronise the membership of this list.