Mailman のメーリングリスト管理画面の認証を Shibboleth 対応にする

## setgid wrapper の導入による影響（2021/9/24 追記）

[コメント](https://qiita.com/tsuchm/items/a794a57acf549a654599#comment-5e41393721ba2c80ad21)でご指摘いただいたとおり、Mailman 2.1.25 では再コンパイルが必要です。

## 背景

Mailman のメーリングリスト管理をウェブ経由で行う場合、通常はリスト管理者のパスワードが必要となります。しかし、リスト管理者のパスワードを個別に発行・管理するのは、かなりの手間が発生します。

リスト管理者パスワードの認証に代わって、LDAP などの外部のユーザ認証基盤を用いて行う方法は http://kame-t.hatenablog.jp/entry/20090526/1243322568 で解説されているとおりです。
このページで紹介されている方法は、/etc/mailman/mm_cfg.py に以下のように記述しておくことによって、Mailman の CGI の認証を**完全に無効化**しておき、その代替として Apache のレイヤーで認証と認可を行うという方法です。

```py
UnAuthorized = 3
```

しかし、この方法では、Apache のレイヤーで認証・認可されたユーザは全員が Mailman のメーリングリスト管理者として振る舞うことができます。一般ユーザも広く認証・認可される場合には、

* 一般ユーザには、メーリングリスト一覧 http://example.net/mailman/listinfo を見せても良いが、メーリングリスト管理一覧 http://example.net/mailman/admin は見せてはいけない
* 管理者ユーザには、メーリングリスト管理一覧 http://example.net/mailman/admin も見せても良い

というように、Mailman の CGI 毎にかなり細かく認可規則を書く必要があります。

言い換えれば、Apache のレイヤーで認証・認可されるユーザが管理者ユーザのみに限定されている場合には適用できますが、一般ユーザも広く認証・認可されるようなサイトにおいては適用が困難です。

## 代替案

細かく認可規則を書くのは大変なので、代替案を考えました。Mailman の CGI は、FastCGI や PSGI を使っていないので、設定ファイル /etc/mailman/mm_cfg.py はウェブサーバへのアクセスの度に再評価されます。したがって、Mailman の CGI を何らかの方法で認証管理下に置いておき、以下のように環境変数 REMOTE_USER をチェックして、Mailman による認証を有効化・無効化するようにすれば良いようです。

```py
def admincheck():
    import os
    adminusers = ['foo', 'bar']
    user = os.environ.get('REMOTE_USER')
    if user:
        if user in adminusers:
            return 3
    return 0

UnAuthorized = admincheck()
```

実際には、以下のように LDAP で管理者として登録されているかどうかを調べるようにしています。

```py
def admincheck():
    import os
    import ldap
    server = 'ldap://ldap.example.net/'
    binddn = 'cn=mailman,dc=example,dc=net'
    bindpw = '0123456789'
    basedn = 'ou=admin,dc=example,dc=net'
    user = os.environ.get('REMOTE_USER')
    if user:
        try:
            l = ldap.initialize(server)
            l.simple_bind_s(binddn, bindpw)
            if len(l.search_s(basedn, ldap.SCOPE_SUBTREE, '(uid=%s)' % user)) > 0:
                return 3
        except:
            pass
    return 0

UnAuthorized = admincheck()
```

これで、Mailman の CGI を Shibboleth 管理下[^1]に置いておけば、Mailman のメーリングリスト管理画面の認証をメーリングリスト個別のパスワードで行う代わりに、ユーザ認証基盤のユーザ名・パスワードでメーリングリスト管理画面の認証を行うことができるようになります。

[^1]: 筆者の環境では、実際には [mod_auth_tkt を使って Single Sign On を行う](http://qiita.com/tsuchm/items/74b964626f6fec234d0d)方法で認証管理下に置いています。

CGI が呼び出される度に admincheck() 関数が実行されて、LDAP サーバに対するアクセスが発生してしまうので、あまりサーバに対して優しい実装とは言えません。ですから、適切な認証管理下に置いておかないと、DOS 攻撃などが可能になってしまうと思われますので、ご注意ください。

## 制限

本稿の方法で、通常のメーリングリスト管理については、ユーザ認証基盤による認証・認可によって制御することができます。しかし、ウェブサーバ経由でメーリングリストを作成するには、相変わらずメーリングリストのリスト作成者パスワード（またはサイト管理者パスワード）が必要です。
Mailman/Cgi/create.py のメーリングリスト作成部分の該当コードでは、以下のようにパスワードチェックがハードコードされているため、設定などで変更することができません。

```py
    auth = cgidata.getvalue('auth', '').strip()
    （中略）
    ok = 0
    if auth:
        ok = Utils.check_global_password(auth, 0)
        if not ok:
            ok = Utils.check_global_password(auth)
    if not ok:
        request_creation(
            doc, cgidata,
            _('You are not authorized to create new mailing lists'))
        return
```

リスト作成者パスワードは、メーリングリストサーバで以下のように直接コマンドを実行して設定する必要があります。

```shell-session
$ mmsitepass -c
```