From c7ac121482299ba98c68a21c4632628f76f789a3 Mon Sep 17 00:00:00 2001 From: Russ Garrett <russ@garrett.co.uk> Date: Wed, 1 Nov 2023 10:36:50 +0000 Subject: [PATCH] Add README to document configuration --- README.md | 187 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 187 insertions(+) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 0000000..e9103ed --- /dev/null +++ b/README.md @@ -0,0 +1,187 @@ +# Postorius (Mailman 3) LDAP Membership Management + +This Mailman 3 extension enables single-sign-on and list membership synchronisation with [uffd](https://git.cccv.de/uffd/uffd), using [uffd-ldapd](https://git.cccv.de/uffd/uffd-ldapd) and [uffd-nginxauth](https://git.cccv.de/uffd/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](https://git.cccv.de/infra/ansible/roles/mailman/) 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`: + +```nginx +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`: + +```python +# 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](https://django-auth-ldap.readthedocs.io) 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: + +```python +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. + +```python +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. \ No newline at end of file -- GitLab