python (pysaml2) から cybozu.com の SAML 認証を確認してみる

# ０．はじめに

python (pysaml2) から cybozu.com の SAML 認証を確認してみます。

# １．Amazon Linux の EC2 インスタンスを作成する
1. E2 コンソールページから、Amazon Linux の AMI を選択し、ポチポチと EC2 インスタンスを作成する。　
    * AMI ID : 
        * amzn-ami-hvm-2017.03.0.20170417-x86_64-gp2 (ami-923d12f5)
    * Instance Type : 
        * t2.micro
    * Security Groups :
        * SSH
        * HTTPS
<br><br>
1. その後、Elastic IP を割り当てます。

# ２．初期設定やセキュリティ設定を設定する

1. 一般的な初期設定やセキュリティ設定などを行います。
    * root ユーザのパスワードの設定
    * ホスト名の変更
        * /etc/sysconfig/network
        * /etc/hosts
        * `hostname`
    * 日本語設定
        * /etc/sysconfig/i18n
    * タイムゾーンの設定
        * /etc/cloud/cloud.cfg
        * /etc/sysconfig/clock
        * /etc/localtime
    * SSH の設定
    * iptables の設定
        * SSH
        * HTTPS

# ３．pysaml2 をインストールする

1. 以下のリンクから zip ファイルをダウンロードします。
    * [rohe/pysaml2: Python implementation of SAML2](https://github.com/rohe/pysaml2)

    ```bash
    wget https://github.com/rohe/pysaml2/archive/master.zip
    ```
<br>
1. ダウンロードした zip ファイルを解凍し、解凍されたディレクトリに移動します。

    ```bash
    unzip master.zip
    cd ./pysaml2-master
    ```
<br>
1. pysaml2 をインストールします。

    ```bash
    sudo pip install --upgrade pip
    sudo pip install repoze.who
    sudo yum install libffi libffi-devel
    sudo yum install gcc
    sudo python setup.py install
    ```

# ４．サンプルプログラムを起動する

1. サンプルプログラムディレクトリに移動します。

    ```bash
    cd ./pysaml2-master/example
    ```
<br>
1. 必要なライブラリをインストールします。

    ```bash
    sudo pip install mako
    sudo pip install "cherrypy==7.1.0"
    sudo yum install xmlsec1 xmlsec1-openssl
    sudo pip install pycryptodomex
    ```
<br>
1. サンプルプログラムを実行します。

    ```bash
    ./all.sh start
    ```

    以下のメッセージが表示されたら、多分OK

    ```bash
    $ SP listening on localhost:8087
    $ IDP listening on localhost:8088
    ```

    サンプルプログラムを終了します。

    ```bash
    ./all.sh stop
    ```

# ５．外部接続用の設定をする

1. iptables に以下の設定を追加します。

    ```bash
    -A INPUT -p tcp -m tcp --dport 8087 -j ACCEPT
    -A INPUT -p tcp -m tcp --dport 8088 -j ACCEPT
    ```

    ```bash
    sudo service iptables restart
    ```
<br>
1. セキュリティグループ に以下の設定を追加します。　
    * TCP　8087　0.0.0.0/0
    * TCP　8088　0.0.0.0/0
<br><br>
1. HTTPS 接続用に、適当な場所にサーバー証明書ファイル(公開鍵、秘密鍵)を格納します。
    * サーバー証明書の公開鍵 : *.cert
    * サーバー証明書の秘密鍵 : *.key
<br><br>
1. 認証用の証明書を新たに作成します。

    ```bash
    ./create_key.sh
    
    Generating a new test key and certificate.  To change the defaults offered
    by openssl, edit your openssl.cnf, such as /etc/ssl/openssl.cnf
    
    Generating RSA private key, 1024 bit long modulus
    ..................++++++
    ...........++++++
    e is 65537 (0x10001)
    You are about to be asked to enter information that will be incorporated into your certificate request.
    What you are about to enter is what is called a Distinguished Name or a DN.
    There are quite a few fields but you can leave some blank
    For some fields there will be a default value,
    If you enter '.', the field will be left blank.
    -----
    Country Name (2 letter code) [XX]:JP
    State or Province Name (full name) []:[都道府県]
    Locality Name (eg, city) [Default City]:[市区町村]
    Organization Name (eg, company) [Default Company Ltd]:[企業名称]
    Organizational Unit Name (eg, section) []:
    Common Name (eg, your name or your server s hostname) []:[FQDN]
    Email Address []:[メールアドレス]
    
    Please enter the following 'extra' attributes
    to be sent with your certificate request
    A challenge password []:[パスワード]
    An optional company name []:[企業名称]
    Signature ok
    subject=/C=JP/ST=[都道府県]/L=[市区町村]/O=[企業名称]/CN=[FQDN]/emailAddress=[メールアドレス]
    Getting Private key
    
    Now to enable these new keys, do:
    
        cp server.key idp2/pki/mykey.pem
        cp server.crt idp2/pki/mycert.pem
    
        cp server.key sp-wsgi/pki/mykey.pem
        cp server.crt sp-wsgi/pki/mycert.pem
    ```

    作成した証明書をコピーします。

    ```bash
    cp server.key idp2/pki/mykey.pem
    cp server.crt idp2/pki/mycert.pem
    
    cp server.key sp-wsgi/pki/mykey.pem
    cp server.crt sp-wsgi/pki/mycert.pem
    ```
<br>
1. サンプルプログラムを修正します。
<br>
    * ./pysaml2-master/example/idp2/idp_conf.py

    ```bash
    29c29
    < HOST = 'localhost'
    ---
    > HOST = '0.0.0.0'
    35c35
    <     BASE = "https://%s:%s" % (HOST, PORT)
    ---
    >     BASE = "https://%s:%s" % ("[FQDN]", PORT)
    37c37
    <     BASE = "http://%s:%s" % (HOST, PORT)
    ---
    >     BASE = "http://%s:%s" % ("[FQDN]", PORT)
    40,41c40,41
    < SERVER_CERT = "pki/mycert.pem"
    < SERVER_KEY = "pki/mykey.pem"
    ---
    > SERVER_CERT = "[サーバー証明書の公開鍵の相対パス]"
    > SERVER_KEY = "[サーバー証明書の秘密鍵の相対パス]"
    ```
    * ./pysaml2-master/example/sp-wsgi/service_conf.py

    ```bash
    4c4
    < HOST = 'localhost'
    ---
    > HOST = '0.0.0.0'
    20,21c20,21
    < SERVER_CERT = "pki/mycert.pem"
    < SERVER_KEY = "pki/mykey.pem"
    ---
    > SERVER_CERT = "[サーバー証明書の公開鍵の相対パス]"
    > SERVER_KEY = "[サーバー証明書の秘密鍵の相対パス]"
    ```
    * ./pysaml2-master/example/sp-wsgi/sp_conf.py

    ```bash
    18c18
    < BASE = "http://localhost:8087"
    ---
    > BASE = "http://[FQDN]:8087"
    21c21
    <     "entityid": "%s/%ssp.xml" % (BASE, ""),
    ---
    >     "entityid": "%s/%sp.xml" % (BASE, ""),
    ```
<br>
1. サンプルプログラムを実行します。

    ```bash
    ./all.sh start
    ```
<br>
1. 以下のサイトへアクセスし、ログインします。
<br>
    * http://[FQDN]:8087/
    * ログイン情報は、`./pysaml2-master/example/idp2/idp.py` (528行目) に記載。
    * `daev0001` は、エラーになるので使わないこと!!

    ![FireShot Capture 46 - IDP test login_ - https___pysaml2.genbacloud.com_8088_sso_redirect.png](https://qiita-image-store.s3.amazonaws.com/0/161939/ca8fae4f-c025-b648-f536-fd5afbd0c1a9.png)

    ![FireShot Capture 45 -  - http___pysaml2.genbacloud.com_8087_.png](https://qiita-image-store.s3.amazonaws.com/0/161939/7739181d-b6d4-6cfb-84a0-cdf2d1c5ab5b.png)

    サンプルプログラムを終了します。

    ```bash
    ./all.sh stop
    ```

# ６．pysaml2 側に連携用アカウントを設定する

1. サンプルプログラムを修正します。
<br>
    * ./pysaml2-master/example/idp2/idp.py

    ```bash
    528a538
    >     "[メールアドレス]": "[パスワード]",
    ```
    * ./pysaml2-master/example/idp2/idp_conf.py

    ```bash
    37a38,60
    >     "[メールアドレス]": {
    >         "sn": "Testsson",
    >         "givenName": "Test",
    >         "eduPersonAffiliation": "student",
    >         "eduPersonScopedAffiliation": "student@example.com",
    >         "eduPersonPrincipalName": "test@example.com",
    >         "uid": "[メールアドレス]",
    >         "eduPersonTargetedID": "one!for!all",
    >         "c": "SE",
    >         "o": "Example Co.",
    >         "ou": "IT",
    >         "initials": "P",
    >         "co": "co",
    >         "mail": "mail",
    >         "noreduorgacronym": "noreduorgacronym",
    >         "schacHomeOrganization": "example.com",
    >         "email": "[メールアドレス]",
    >         "displayName": "Test Testsson",
    >         "labeledURL": "http://www.example.com/test My homepage",
    >         "norEduPersonNIN": "SE199012315555",
    >         "postaladdress": "postaladdress",
    >         "cn": "cn"
    >     },
    ```
<br>
1. サンプルプログラムを実行し、サイトへのログインを確認します。

# ７．cybozu.com 側の設定をする
cybozu.com 側の設定は、以前の記事に同様の記載があるので、それを参考に行います。

* [OpenAM と cybozu\.com の SAML 認証を確認してみる \- Qiita](http://qiita.com/kusokamayarou/items/94df119cd5e821c8876f)
<br><br>

1. cybozu.com に SP の設定をする
    * SAML 認証を有効にする : ☑︎
    * Identity ProviderのSSOエンドポイントURL（HTTP-Redirect） :
https://[FQDN]:8088/sso/redirect
    * cybozu.comからのログアウト後に遷移するURL :
http://[FQDN]:8087/logout?goto=http://[FQDN]:8087/
    * Identity Providerが署名に使用する公開鍵の証明書 :
作成した証明書(server.crt)をアップロードします。
    * Service Providerメタデータのダウンロード
        * pysaml2 の SP の設定に必要なので、リンクをクリックしファイルをダウンロードしておきます。 (spmetadata.xml)
<br><br>
1. cybozu.com に連携ユーザーを追加する
    * ※手順通り

# ８．pysaml2 側に cybozu.com の SP の設定をする
1. ダウンロードした cybozu.com の Service Provider のメタデータ (spmetadata.xml) を適当な場所に格納します。
<br><br>
1. サンプルプログラムを修正します。
<br>
    * ./pysaml2-master/example/idp2/idp.py

    ```bash
    48a49,50
    > from saml2.saml import NameID
    > from saml2.saml import NAMEID_FORMAT_TRANSIENT
    341a344,345
    >                 nameid = NameID(
    >                     format=NAMEID_FORMAT_TRANSIENT, text=identity["uid"])
    344a349,350
    >                     name_id=nameid,
    >                     sign_response=True,
    ```
    * ./pysaml2-master/example/idp2/idp_conf.py

    ```bash
    117c117,124
    <         "local": [full_path("../sp-wsgi/sp.xml")],
    ---
    >         "local": [full_path("../sp-wsgi/sp.xml"), full_path("[cybozu.com の Service Provider のメタデータの相対パス]")],
    >         #"inline":
    >         #"remote": [
    >         #    {"url": "",
    >         #     "cert": ""}],
    >         #"mdfile":
    >         #"loader":
    >         #"mdq":
    ```
<br>
1. サンプルプログラムを実行し、cybozu.com のサイトへアクセスし、ログインを確認します。

pysaml2 のログイン画面からログインした後、 cybozu.com の画面が表示されたら、OK!!

# ９９．ハマりポイント

* まず pysaml2 のインストールですが、`sudo python setup.py install` を実行して、エラーが出たらログを確認しつつ、必要なライブラリを一つずつインストールしていく感じです。
<br><br>
* サンプルプログラムの実行でも同様です。`./all.sh start` を実行しては、必要なライブラリのインストールをしていきました。
    * cherrypy は、7.1.0 じゃないとダメっぽいので、お気を付けを。
    * pycryptodomex は、pycryptodome じゃ無くて、pycryptodomex ですので、そこもお気を付けを。
<br><br>
* で、やはりもっともハマったというか、よくわからなかったのが、「7．pysaml2 側に cybozu.com の SP の設定をする」のところでした。
* `./pysaml2-master/example/idp2/idp.py` がそのままだと、以下のエラーが表示され、

    ![FireShot Capture 48 - Assertion consumer service - https___3ejeh.cybozu.com_saml_acs.png](https://qiita-image-store.s3.amazonaws.com/0/161939/2c450b96-2b02-d2ee-ea0f-5a4b8faffa50.png)

    `create_authn_response` の引数に `sign_response=True,` を追加して対処したら…

* 次に以下のエラーが表示され、

    ![FireShot Capture 49 - Assertion consumer service - https___3ejeh.cybozu.com_saml_acs.png](https://qiita-image-store.s3.amazonaws.com/0/161939/de73206e-399c-0662-df1e-12ae8eff97ff.png)

    `name_id=nameid,` を追加して連携できた!!って感じででした。
* どちらのエラーの時も、まずはログを見て、その後、`./pysaml2-master/tests/` 配下のテストプログラムを確認したりしながら、修正していきました。SAML の仕様を理解しながらの対応だったので、時間が掛かりました。終わってみれば数行の追加でしたが…。
* こちらのサイトも参考にさせて頂きました。
    * [pysaml2\.pdf](https://media.readthedocs.org/pdf/pysaml2/latest/pysaml2.pdf)
    * [Running the pysaml2 example](http://www.dangtrinh.com/2014/04/running-pysaml2-example.html)
    * [SAML認証ができるまで \- Cybozu Inside Out \| サイボウズエンジニアのブログ](http://blog.cybozu.io/entry/4224)
    * [SAML認証のトラブルシューティング \| cybozu\.com共通管理 ヘルプ](https://help.cybozu.com/ja/general/admin/saml_errors.html#validation)

# ＸＸ．まとめ

とりあえず、確認できて良かったです。
もう少し、SAML の仕様について理解を深める様、勉強しないといけないのかも…。