docker-composeでletsencrypt+SAML認証のリバースプロキシサーバーを構築する


はじめに

前回redhat6のapache2.2にmod_auth_mellonをインストールして大変苦労しましたが、今回はdockerで簡単に構築出来たので手順を共有します。

環境

項目 内容
SP Apache mod_auth_mellon
IdP Microsoft Azure Active Directory
  • シングルサインオンさせたいサーバーはリバースプロキシのバックエンドにいてサブドメインでアクセスするものとします。

dockerの設定

  • hogeディレクトリ以下に構築するものとします。
ディレクトリ構成
hoge
├── certs
├── dhparam
├── docker-compose.yml
├── html
├── proxy
│   └── log
├── saml
│   ├── proxy.conf.template
│   ├── saml_idp.xml
│   ├── saml_sp.cert
│   ├── saml_sp.key
│   └── saml_sp.xml
└── vhost
docker-compose.yml
version: '3'
services:
    saml-proxy:
        image: "mpar/saml-proxy"
        environment:
            - BACKEND="http://www.yahoo.co.jp" #テスト用の定義
            - VIRTUAL_HOST=hoge.example.com
            - VIRTUAL_PORT=8080
            - LETSENCRYPT_HOST=hoge.example.com
            - [email protected]
        ports:
            - "8080:80"
        volumes:
            - ./saml/saml_idp.xml:/etc/httpd/conf.d/saml_idp.xml
            - ./saml/saml_sp.key:/etc/httpd/conf.d/saml_sp.key
            - ./saml/saml_sp.cert:/etc/httpd/conf.d/saml_sp.cert
            - ./saml/saml_sp.xml:/etc/httpd/conf.d/saml_sp.xml
            - ./saml/proxy.conf.template:/etc/httpd/conf.d/proxy.conf.template
    nginx-proxy:
        image: jwilder/nginx-proxy
        container_name: nginx-proxy
        ports:
            - "80:80"
            - "443:443"
        restart: unless-stopped
        volumes:
            - ./proxy/log:/var/log/nginx
            - ./html:/usr/share/nginx/html
            - ./dhparam:/etc/nginx/dhparam
            - ./vhost:/etc/nginx/vhost.d
            - ./certs:/etc/nginx/certs:ro
            - /var/run/docker.sock:/tmp/docker.sock:ro
        environment:
            TZ: Asia/Tokyo
        labels:
            - "com.github.jrcs.letsencrypt_nginx_proxy_companion.nginx_proxy"

    letsencrypt:
        image: jrcs/letsencrypt-nginx-proxy-companion
        container_name: nginx-proxy-lets-encrypt
        depends_on:
            - "nginx-proxy"
        volumes:
            - ./certs:/etc/nginx/certs:rw
            - ./vhost:/etc/nginx/vhost.d
            - ./html:/usr/share/nginx/html
            - /var/run/docker.sock:/var/run/docker.sock:ro

mod_auth_mellonの設定

mellon_create_metadata.shの取得

mellon_create_metadata.shの実行

hoge/saml ディレクトリで以下を実行します。

$ chmod 755 mellon_create_metadata.sh
$ sudo ./mellon_create_metadata.sh \
  -k saml_sp.key \
  -c saml_sp.cert \
  -m saml_sp.xml \
  -e https://hoge.example.com/mellon/metadata \
  -b https://hoge.example.com/mellon

このコマンド実行後に出来た、

saml_sp.xml

をAzure ADの「メタデータファイルをアップロードする」でアップロードします。

アップロードして保存したら[フェデレーション メタデータ XML]をダウンロードして hoge/saml ディレクトリに

saml_idp.xml

というファイル名で保存します。

proxy.conf.templateの編集

  • hoge/saml ディレクトリに proxy.conf.template を追加し自分の環境に合わせて編集します。
  • このサンプルは https://hoge.example.com/fuga へのアクセスをローカルのバックエンドサーバー(192.168.1.11)に送る例です。
  • サブドメインごとバックエンドに送る場合はこのファイルは不要ですので docker-compose.yml で proxy.conf.template のボリュームをマウントしている行を削除して下さい。
proxy.conf.template
ServerName $SCHEMA://$HOST

<Location />
    # Add mod_auth_mellon info to all contexts
    MellonEnable "info"

    # Auth redirects will be located under /$MELLON_PATH
    MellonEndpointPath /$MELLON_PATH

    # service provider metadata, cert, and key
    MellonSPPrivateKeyFile /etc/httpd/conf.d/saml_sp.key
    MellonSPCertFile       /etc/httpd/conf.d/saml_sp.cert
    MellonSPMetadataFile   /etc/httpd/conf.d/saml_sp.xml

    # idp metadata
    MellonIdPMetadataFile /etc/httpd/conf.d/saml_idp.xml
</Location>

<LocationMatch "/fuga/.*">
    # Protect with auth
    MellonEnable "auth"

    # Proxy to backend once authenticated
    #ProxyPass $BACKEND
    ProxyPass        http://192.168.1.11
    ProxyPassReverse http://192.168.1.11

    <If "-z env('REMOTE_USER_SAML_ATTRIBUTE')">
       # Set the Remote-User header to the value of the authenticated username
       RequestHeader set Remote-User %{MELLON_NAME_ID}e env=MELLON_NAME_ID
    </If>
    <Else>
       # Use a custom attribute as the remote username by setting $REMOTE_USER_SAML_ATTRIBUTE to a custom attribute name
       # Require a value in the attribute we're going to use:
       MellonCond ${REMOTE_USER_SAML_ATTRIBUTE} .+ [REG]
       # Map the long attribute name to a nice short one
       MellonSetEnv user ${REMOTE_USER_SAML_ATTRIBUTE}
       # Set the Remote-User header to the value of the mapped envvar:
       RequestHeader set Remote-User %{MELLON_user}e env=MELLON_user
    </Else>

    <If "-n env('REMOTE_USER_NAME_SAML_ATTRIBUTE')">
       MellonSetEnv name ${REMOTE_USER_NAME_SAML_ATTRIBUTE}
       RequestHeader set Remote-User-Name %{MELLON_name}e env=MELLON_name
    </If>

    <If "-n env('REMOTE_USER_EMAIL_SAML_ATTRIBUTE')">
       MellonSetEnv email ${REMOTE_USER_EMAIL_SAML_ATTRIBUTE}
       RequestHeader set Remote-User-Email %{MELLON_email}e env=MELLON_email
    </If>

    <If "-n env('REMOTE_USER_PREFERRED_USERNAME_SAML_ATTRIBUTE')">
       MellonSetEnv preferred_username ${REMOTE_USER_PREFERRED_USERNAME_SAML_ATTRIBUTE}
       RequestHeader set Remote-User-Preferred-Username %{MELLON_preferred_username}e env=MELLON_preferred_username
    </If>

    ${REQUEST_HEADERS}
</LocationMatch>

テスト

Azure ADのユーザーをエンタープライズ アプリケーションに追加して
https://hoge.example.com/fuga
にアクセスし、Azure ADのログイン画面を経てバックエンドサーバーのfugaディレクトリの中身が見られれば完了です。

まとめ

前回の大ハマりと違って今回は特に問題なくサーバー構築が出来ました。
なお、ディレクトリ毎にリバースプロキシ先を変えたりアクセス制限をかけたい場合などは、1コンテナに詰め込むのではなく単機能のコンテナを多段に組み合わせて構築した方が良さそうです。