Shibboleth IdP v3.0を今更導入してみた

# はじめに
私は現在、主に認証を担当しています。
昨今ではSSO(シングルサインオン)を導入されるお客様も多く、単純なID/パスワード方式を始めとする独自の認証だけでは要件を満たすことは非常に難しくなってきています。
SSOには様々な手法がありますが、このSaaS時代においてはほぼSAMLの独壇場と言っても過言ではないでしょう。（もちろん5～10年後には廃れていてもおかしくないですが）

そんな中、最近Shibboleth(学術認証基盤)の案件があり、教育機関のお客様への対応として今後も継続的にShibbolethが必要だと感じたため、社内に導入してみました。
この記事はその際の記録です。
少しでも、似たようなシステム連携業務に携わる方の力になればと思います。

# そもそもShibbolethとは
Shibbolethとは、IDフェデレーションを実現するための認証・認可基盤アーキテクチャです。
高等教育機関や学術出版社で用いられ（というかほぼそういうとこでしか利用されてない）、日本の大学・国立情報学研究所が共同で運営している「学術認証フェデレーション」通称『学認』は、このShibbolethを基盤として動いています。
（『学認』という名称は私も学生時代何度か耳にし、実際PubMedやKULASISなんかにアクセスするときに使っていたのでしょうが、その中身について意識したことはありませんでした。）

このShibbolethを基盤にした学認を利用することで、ユーザーは論文を読むことができたり、学内システムで交通費を申請したり、顕微鏡を予約したり、果ては別の大学でWi-fiに接続出来たりもするわけです。
素晴らしいですね。へー。


# Shibbolethの構成
Shibbolethは以下2つのパーツによって構成されています。

- IdP
- SP

Shibbolethは全体として広義の「アイデンティティプロバイダ」としての役割を果たしますが、その内部ではSAML的な意味で「IdP」として働くサーバーと、IdPからアサーションを受け取って他のアプリケーションへ情報を受け渡す「SP」というサーバーが存在します。
SAML的には確かにIdPとSPではあるものの多分にミドルウェア的な役割を担っているので、今までWebアプリケーションとの直接的な連携ばかりを見てきた人間としては理解に時間が掛かりました。
（また余談ですが、日本の教育機関の場合、学認に参加している＝他のIdPに切り替える選択肢がほぼ無いため、「Shibboleth IdP」ではなく単に「IdP」と呼ばれてしまうこともあるようで、「IdPってどこの何…？」となりかねず、その点も意思疎通の妨げになりかねないなぁと感じています。）

今回は、この内「IdP」と自社製品を連携することを目標にしました。

# 導入してみて分かった、Shibbolethの良いところ
- かなりカスタマイズできる。高機能な印象。
- ドキュメントが整備されている。

# Shibbolethの問題点
### GUIがねぇ！！！

これに尽きます。vimは友達。
yumで簡単にインストール出来るのはありがたいんですが、設定方法がはっきり言って分かりづらい。
メタデータを渡さないと設定できないのはなかなか不親切な設計。
あと細かいところだとGoogleやMSでも利用されてないtransientがNameIDのデフォルト設定になっているのはやはりこう、ビジネス・コミュニティとの乖離を感じました。

# 実際の導入手順
IdPはLDAPとtomcatで構成されますので、その二つを立てていきます。

## LDAP
同一インスタンス上に、LDAPを立てます。
基本的に[こちら](https://meatwiki.nii.ac.jp/confluence/pages/viewpage.action?pageId=63873235)の通りです。

- OpenLDAP

yumでインストールし、

```
# yum install openldap-clients openldap-servers
```

パスワードを作成

```
# slappasswd -h {CRYPT} -c '$6$%s$' -s {任意のパスワード}
{CRYPT}パスワード文字列
```

test-idp.ldifを以下の内容で作成し

```
dn: olcDatabase={1}monitor,cn=config
changetype: modify
replace: olcAccess
olcAccess: {0}to *  by dn.base="gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth" read  by dn.base="cn={任意},o={任意},dc={任意},c=JP" read  by * none

dn: olcDatabase={2}hdb,cn=config
changetype: modify
replace: olcSuffix
olcSuffix: o={任意},dc={任意},c=JP
-
replace: olcRootDN
olcRootDN: cn={任意},o={任意},dc={任意},c=JP
-
add: olcRootPW
olcRootPW: {CRYPT}パスワード文字列
```

ドメイン情報を登録

```
# ldapmodify -Y EXTERNAL -H ldapi:// -f test-idp.ldif
```

edupersonファイルを以下の内容で作成

```
include /etc/openldap/schema/eduperson.schema
```

eduperson.ldifを作成

```
# slapcat -f eduperson -F /tmp -n0 -s "cn={0}eduperson,cn=schema,cn=config" > /etc/openldap/schema/eduperson.ldif
```

作成されたeduperson.ldifを手順通りに修正( `ファイルの後方にある「structuralObjectClass: olcSchemaConfig」以降を全て削除` も忘れずに！)

```
    dn: cn=eduperson,cn=schema,cn=config
    objectClass: olcSchemaConfig
    cn: eduperson
```

edupersonスキーマを登録。その際、必要な他のスキーマ(cosine, inetorgperson)も一緒に登録する。

```
# ldapadd -Y EXTERNAL -H ldapi:// -f /etc/openldap/schema/cosine.ldif
# ldapadd -Y EXTERNAL -H ldapi:// -f /etc/openldap/schema/inetorgperson.ldif
# ldapadd -Y EXTERNAL -H ldapi:// -f /etc/openldap/schema/eduperson.ldif
```

`3. LDAPのテストデータ作成` の通り、test.ldifファイルを作成する。この時、オブジェクト名などは先に作成したtest-idp.ldifと一致させる必要がある。
そしてテストデータを登録する。

```
# ldapadd -x -h localhost -D "cn={任意},o={任意},dc={任意},c=JP" -w {任意のパスワード} -f test.ldif
```

## tomcat
以下の構成。
- CentOS7 (VirtualBox上)
- openjdk 1.8.0
- tomcat 7

基本的な手順は[こちら](https://meatwiki.nii.ac.jp/confluence/pages/viewpage.action?pageId=63873203)を参考にしています。

まず[ここ](http://shibboleth.net/downloads/identity-provider/latest3/)からパッケージ(執筆時点で3.4.7)をダウンロードし、

```
# tar xzvf shibboleth-identity-provider-3.?.?.tar.gz
# cd shibboleth-identity-provider-3.?.?
# ./bin/install.sh -Didp.conf.filemode=640
```

でインストール。EntityIDは参考リンクにある通り、 `https://ホスト名/idp/shibboleth` にしました。
`3. パーミッションの調整` も行い、 `4. jakarta-taglibs-core.jar と jakarta-taglibs-standard.jar の配置 ` でidp.warをリビルドします。
`5. idp.war の登録` でtomcatにwarを配置。

事前に

```
# chown root:tomcat /opt/shibboleth-idp/credentials/server.key
# chmod 640 /opt/shibboleth-idp/credentials/server.key
```

でtomcatユーザに鍵ファイルへの権限を与えておき、idp.propertiesを編集していきます。
[こちら](https://meatwiki.nii.ac.jp/confluence/pages/viewpage.action?pageId=63873237)を参考のこと。

そして先に立てた[LDAPと接続するための設定](https://meatwiki.nii.ac.jp/confluence/pages/viewpage.action?pageId=63873238)をしていきます。
ldap.propertiesを編集。

```
idp.authn.LDAP.ldapURL = ldap://localhost
idp.authn.LDAP.useStartTLS = false
(省略)
idp.authn.LDAP.baseDN = o={任意},dc={任意},c=JP
idp.authn.LDAP.subtreeSearch = true
idp.authn.LDAP.userFilter = (uid={user})
# bind search configuration
# for AD: idp.authn.LDAP.bindDN=adminuser@domain.com
idp.authn.LDAP.bindDN = cn={任意},o={任意},dc={任意},c=JP
idp.authn.LDAP.bindDNCredential = {任意のパスワード}
```

これで、

```
systemctl restart tomcat
```

するととりあえずLDAPと繋がったIdPが立ち上がります。

## SPの登録
SPを登録するためには、SPのmetadataが必要です。
用意したmetadataファイルを `/opt/shibboleth-idp/metadata` 辺りに **sp-metadata.xml** として配置し、 `/opt/shibboleth-idp/conf/metadata-providers.xml` に以下を追記。

```
<MetadataProvider id="LocalMetadata(任意の名称)"  xsi:type="FilesystemMetadataProvider" metadataFile="/opt/shibboleth-idp/metadata/sp-metadata.xml"/>
```

## Attributeの設定
差し当たり、接続確認したいだけなので以下のようにしたい。

- AssertionはEncryptしない
- AttributeStatementにユーザーIDを渡す

そのため、 `relaying-party.xml` で id="shibboleth.DefaultRelyingParty" を以下の様に設定。

```
    <bean id="shibboleth.DefaultRelyingParty" parent="RelyingParty">
        <property name="profileConfigurations">
            <list>
                <bean parent="Shibboleth.SSO" />
                <ref bean="SAML1.AttributeQuery" />
                <ref bean="SAML1.ArtifactResolution" />
                <bean parent="SAML2.SSO" p:includeAttributeStatement="true" p:encryptAssertions="never" p:encryptNameIDs="never" />
                <ref bean="SAML2.ECP" />
                <ref bean="SAML2.Logout" />
                <ref bean="SAML2.AttributeQuery" />
                <ref bean="SAML2.ArtifactResolution" />
                <ref bean="Liberty.SSOS" />
            </list>
        </property>
    </bean>
```

`attribute-filter.xml` で以下のように設定。

```
    <!-- Release an additional attribute to an SP. -->
    <AttributeFilterPolicy id="filter1">
        <PolicyRequirementRule xsi:type="Requester" value="{SPのEntityID}" />
        <AttributeRule attributeID="uid" permitAny="true" />
    </AttributeFilterPolicy>
```

完了後、tomcatを再起動。SP側に設定を渡し、SSOを行うと・・・
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/655411/b9af85ba-c2a6-d23c-379e-507d8cb12272.png)

# おわりに
とりあえずGUIだけでもなんとかしてくれないかなぁ。
設定を色々弄れるのでやりたいようにやれるっぽいのですが、少し複雑なことをやろうとすると途端に取っつきにくくなる感じですね…。
~~あれ？これどっかで聞いたような~~

SAMLは設定方法や項目名がIdPによってバラバラで、仕様もそれなりにバラつきがあり苦労させられますが、
実際ちゃんと設定できれば利便性は非常に高いもので、SPを何個も同一IdPに連携して運用すると戻れません。
ユーザー認証周りの利便性はユーザーエクスペリエンスと明確に繋がっているので、そういう意味で非常にやりがいがあります。
みんなもっと認証に携わろうぜ！

了