Apache + OneLogin SSO with Shibboleth

#背景
急にSAMLと仲良くしなきゃいけなくなった。
SAMLまわりの情報はなぜかネットに乏しく、環境がマイナーなのでさらに情報がない。
まる２日ほど七転八倒しながらなんとか動くようにした。

関連記事：[Django + Shibboleth with RemoteUserMiddleware](https://qiita.com/hiro-jp/items/9d5a20baebc1c7d8f911)

#環境
- Windows Server 2016
- Python 3.7.7（[後々のために](https://qiita.com/hiro-jp/items/1a15ac51165f5af395f3)、3.8系は避けること！）
- Apache 2.4 + mod_wsgi + mod_shib
- SP: Shibboleth Service Provider 3.x
- IdP: [OneLogin](https://www.onelogin.com/)

#アプローチ
##①アプリサイド（例えばDjangoアプリ）でSAML認証を実装する
[django-saml2-auth](https://github.com/fangli/django-saml2-auth)というのがあるが、これはdjango 1.9～2.1くらいで更新が止まっており、要求されるpythonのバージョンも古く、依存関係の解決が難しい。

##②サーバーサイド（例えばApache）でSAML認証を実装する
どうやらこちらが一般的な様子。特定のリソースをShibboleth管理下に置き、Shibboleth認証が通ったらID情報を環境変数REMOTE_USERにいれ、djangoに渡す。**今回はこちらを採用する。**

##参考
[Shibboleth Wiki - AttributeAccess](https://wiki.shibboleth.net/confluence/display/SP3/AttributeAccess)

##前提
- Apacheはインストールしてあるものとする。
- PKI暗号キーペアは作成してあるものとする。
- SAMLにおけるSPとIdPの概念は既知とする。

#手順
##1. Shibboleth SPのインストール
###1-1. ダウンロード
下記サイトより、
[Install Shibboleth Service Provider 3.x on Windows and IIS](https://wiki.cac.washington.edu/display/infra/Install+Shibboleth+Service+Provider+3.x+on+Windows+and+IIS)
![ss03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/117020/b21821d7-d6fc-7e51-b152-c83f8c86f935.png)

win64/shibboleth-sp-3.1.0.1-win64.msiをダウンロード。
![ss05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/117020/9a1a5518-e21b-3047-f6c5-cfe82001a4bd.png)
![ss04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/117020/d3ee568c-d75d-2e7f-feb5-57f926ec5338.png)

###1-2. インストール
ダウンロードしたshibboleth-sp-3.1.0.1-win64.msiを実行。基本的にデフォルトでインストールすることを推奨。IISを使わないので、「Configure IIS7 module」はチェック不要。
![ss06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/117020/0ae78845-ec08-0acb-0fbb-2e4832427d5b.png)

##2. Apache httpd.confの設定
Apacheのバージョンに合ったmod_shibを読み込む。
Apache2.4系の場合は`mod_shib_24.so`

```httpd.conf
LoadModule mod_shib "c:/opt/shibboleth-sp/lib64/shibboleth/mod_shib_24.so"
```

Shibbolethハンドラのロケーションを設定。

関連記事：[Shibbolethハンドラ]

```httpd.conf
<Location /Shibboleth.sso>
  SetHandler shib
</Location>
```

Shibbolethの管理下に置きたいロケーションを設定。

```httpd.conf
<Location />
    AuthType shibboleth
    ShibRequestSetting requireSession true
    Require shibboleth
    Require shib-session
</Location>
```

##3. Shibboleth2.xmlの設定
Shibbolethにおけるメインの設定ファイルはShibboleth2.xmlである。
`C:\opt\shibboleth-sp\etc\shibboleth`あたりにある。

まずIIS専用設定をコメントアウトする。

```xml:shibboleth2.xml
    <!--
    The InProcess section contains settings affecting web server modules.
    Required for IIS, but can be removed when using other web servers.
    -->
<!--    <InProcess>-->
<!--        <ISAPI normalizeRequest="true" safeHeaderNames="true">-->
<!--            &lt;!&ndash;-->
<!--            Maps IIS Instance ID values to the host scheme/name/port. The name is-->
<!--            required so that the proper <Host> in the request map above is found without-->
<!--            having to cover every possible DNS/IP combination the user might enter.-->
<!--            &ndash;&gt;-->
<!--            <Site id="1" name="sp.example.org"/>-->
<!--            &lt;!&ndash;-->
<!--            When the port and scheme are omitted, the HTTP request's port and scheme are used.-->
<!--            If these are wrong because of virtualization, they can be explicitly set here to-->
<!--            ensure proper redirect generation.-->
<!--            &ndash;&gt;-->
<!--            &lt;!&ndash;-->
<!--            <Site id="42" name="virtual.example.org" scheme="https" port="443"/>-->
<!--            &ndash;&gt;-->
<!--        </ISAPI>-->
<!--    </InProcess>-->
```

Host nameをよしなに変更する。Apacheのホスト名を書いておけばよい。

```xml:shibboleth2.xml
    <RequestMapper type="Native">
        <RequestMap>
            <!--
            The example requires a session for documents in /secure on the containing host with http and
            https on the default ports. Note that the name and port in the <Host> elements MUST match
            Apache's ServerName and Port directives or the IIS Site name in the <ISAPI> element above.
            -->
            <Host name="localhost">
                <Path name="secure" authType="shibboleth" requireSession="true"/>
            </Host>
            <!-- Example of a second vhost mapped to a different applicationId. -->
            <!--
            <Host name="admin.example.org" applicationId="admin" authType="shibboleth" requireSession="true"/>
            -->
        </RequestMap>
    </RequestMapper>
```

ApplicationDefaultsのentityIDをよしなに変更する。REMOTE_USERは、今は使わないので放っておいてよいが後々アプリ側の認証で使うことになると思う。

```xml:shibboleth2.xml
    <ApplicationDefaults entityID="https://localhost/"
        REMOTE_USER = "givenName"
        cipherSuites="DEFAULT:!EXP:!LOW:!aNULL:!eNULL:!DES:!IDEA:!SEED:!RC4:!3DES:!kRSA:!SSLv2:!SSLv3:!TLSv1:!TLSv1.1">
```

CredentialResolverにあらかじめ作成しておいたPKI暗号キーペアを登録する。

```xml:shibboleth2.xml
        <CredentialResolver type="File" key="C:/Apache24/conf/server.key" certificate="C:/Apache24/conf/server.crt"/>
```

そのほか、`SSO entityID`や`MetadataProvider`も変更しなければならないが、IdPからの情報が必要なのでIdPの設定を行う。


##4. OneLoginアカウント作成
https://www.onelogin.com/
OneLoginのTOPページからDEVELOPERSを選択。
![ss07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/117020/745268a7-1d24-f404-8e9d-15a9ebf7762d.png)

https://developers.onelogin.com/
ここに飛ぶのでGET A DEVELOPER ACCOUNTを押す。
![ss08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/117020/daea9b63-207c-0a94-ab10-47d6f4fa882a.png)

必要事項を記入してGET STARTEDを押す。
![ss09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/117020/856d1691-2150-486d-d369-62164b5a1332.png)
**備考**："Work email"は、なんと**Gmailが使えない。**Yahoo!Japanのフリーメール「～～@yahoo.co.jp」はいけた。
**注意**："Your sitename"はログイン時に必要になるため、忘れないようにする。

アカウントを作成したらログインし、Administration > Applications > Applicationsを選択。
![ss10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/117020/5f806c84-fa2f-cdac-aae3-3e66f54be045.png)

Add Appを押す。
![ss11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/117020/bdf52727-b77c-760e-37a9-a098a1cd8989.png)

検索窓に「SAML」と入力して、「SAML Test Connector (SP Shibboleth)」を見つけてくる。
![ss12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/117020/4e2a2767-03c0-53dc-3857-4e7e0fa20dbb.png)

表示名を好きなように変え（変えなくてもよい）、SAVEする。
![ss13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/117020/64a2a9d9-eacf-b6cb-31b4-a805f0c6777c.png)

Configurationの各項目を次のように書く。
![ss14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/117020/d9035a4f-33b9-0320-6c5c-1e65368dd694.png)

More ActionsからSAML Metadataを押してIdP Metadataをダウンロード。
![ss15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/117020/be2862a9-75b1-8132-553b-91e9e05e694d.png)

ファイル名を`idp-metadata.xml`などとして、好きな場所に保存。自分の場合、`C:\opt\shibboleth-sp`に入れた。
これでIdPの設定と必要情報の入手は完了。

##5. Shibboleth2.xmlの設定（続き）
`idp-metadata.xml`より、`entityID="https://app.onelogin.com/saml/metadata/～～～"`を見つけて`SSO entityID`に入力する。

```xml:shibboleth2.xml
            <SSO entityID="https://app.onelogin.com/saml/metadata/e5ca0b3b-d8e7-4551-8884-64b59e16c482"
                 discoveryProtocol="SAMLDS" discoveryURL="https://ds.example.org/DS/WAYF">
              SAML2
            </SSO>
```
discoveryProtocolおよびdiscoveryURLは使わないのでそのままでよい。

先ほど保存した`idp-metadata.xml`をMetadataProviderに設定する

```xml:shibboleth2.xml
        <MetadataProvider type="XML" validate="true" path="C:/opt/shibboleth-sp/idp-metadata.xml"/>
```

##【参考】SP-Metadataの取得
今回は使わなかったが、IdPによってはSPのメタデータを要求してくる場合がある。OneLoginのConfigurationでLogin URLとかACSとかを入力したが、それの代用。IdPにSPを登録する際、管理者に提出するのが普通。

```
https://localhost/Shibboleth.sso/Metadata/
```
からダウンロード（というか生成）できる（ただし、IdPによっては必要な情報がなかったり、不要な情報が含まれたりするので、ダウンロードしたMetadataをそのまま管理者に送り付けないこと）。