GuacamoleとADFSでSAML連携

## はじめに
GuacamoleはLDAP連携できるので、わざわざADFSと連携させることはあまりないと思います。ADFSを初めて扱ったので試してみました。

## 試した構成
ざっくりこんな構成です。実際には全部１台のPCに仮想マシンとして構築しました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3636209/3ac95778-1638-c4fd-22f8-9a6a6114cd17.png)

* ドメインやFQDNはデタラメに設定しましたが、動作には影響なさそうでした。
* 認証はブラウザとGuacamole、ブラウザとADFS間で行われるため、GuacamoleとADFSは直接通信は必要ないと思います。

## ADFSの設定
Windows Serverの役割と機能の追加で、ADFSはインストールされた状態です。
途中でADFSのFQDNのTLS証明書が必要になったので、自己署名証明書を設定しました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3636209/c4798480-41b5-cd3c-b0d5-ed05fa23d2e3.png)

ADFSの管理ツールで、Guacamole用の証明書利用者信頼を追加します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3636209/42afc65e-8caa-6447-1094-7ca4639a8217.png)

証明書利用者の識別子を追加します。これは後の工程でGuacamoleに設定するsaml-entity-idの値と同じにしておく必要があります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3636209/c4d1012b-73a7-8451-c3e0-d0012466e9eb.png)

SAMLアサーションコンシューマーエンドポイントを追加します。GuacamoleのアプリのURLを登録します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3636209/ac440753-7be2-763f-3520-1e348b0f6b77.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3636209/895786e9-220c-4b8f-e0f1-84e27dc7fd82.png)

次に、要求発行ポリシーを登録します。ADFSがブラウザからのログイン情報を受け取ってから、ブラウザに返す情報をどうやって取得するかの定義だと思います。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3636209/d1d58079-d7d2-1cea-04c4-1649edc7cbd8.png)

認証したユーザをActiveDirectoryで検索し、電子メールアドレスのフィールドのデータを取得する設定です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3636209/a05343a0-a93f-a01c-da92-80c5060e333f.png)

取得したデータをGuacamoleのログインIDとして使用できるように出力するための変換と思います。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3636209/941a4645-28b6-a70d-5990-a2c901c36134.png)

ADのユーザーの電子メールに値を登録します。最初これを登録しなかったためか、ADFS認証OK→Guacamoleへリダイレクト、ログイン失敗→ADFSへリダイレクトを繰り返す状態になってしまいました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3636209/9109cc7f-0b81-8afd-3a2f-f5d07c74a79f.png)

## Guacamoleの設定
docker-composeを使用すると、設定が便利でした。いろいろなサイトを参考にさせて頂きました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3636209/10c6b3f8-e9dc-e278-f2dd-2a231fff2d76.png)

guacamoleのホームディレクトリの直下にextensionsディレクトリを作成し、その中にsamlモジュールを配置します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3636209/e7c4cea1-2cff-27b6-388f-6f942370f33e.png)

```yaml:docker-compose.yaml
version: "3"
services:

  postgres:
    image: postgres:latest
    restart: unless-stopped
    environment:
      PGDATA: /var/lib/postgresql/data/guacamole
      POSTGRES_DB: guacamole_db
      POSTGRES_PASSWORD: guacamole123456789
      POSTGRES_USER: guacamole_user
    volumes:
      - ./pginit:/docker-entrypoint-initdb.d
      - ./pgdata:/var/lib/postgresql/data

  guacd:
    image: guacamole/guacd:latest
    restart: unless-stopped

  guacamole:
    image: guacamole/guacamole:latest
    restart: unless-stopped
    ports:
      - "8080:8080"
    environment:
      GUACD_HOSTNAME: guacd
      POSTGRES_DATABASE: guacamole_db
      POSTGRES_HOSTNAME: postgres
      POSTGRES_PASSWORD: guacamole1234567890
      POSTGRES_USER: guacamole_user
      GUACAMOLE_HOME: /etc/guacamole
    volumes:
      - ./guacamole:/etc/guacamole
    depends_on:
      - postgres
      - guacd

  nginx:
    image: nginx:latest
    restart: unless-stopped
    ports:
      - "443:443"
    volumes:
      - ./nginx/conf.d:/etc/nginx/conf.d
      - ./nginx/ssl:/etc/nginx/ssl:ro
```

SAML関連の設定について、環境変数からは設定が有効にならなかったため、guacamoleホームディレクトリに、guacamole.propertiesを配置し、その中で設定を行いました。
また、ADFSからメタデータをダウンロードし、設定ファイルから参照できる位置に配置しました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3636209/57d66577-92a1-41aa-4533-4e2f3b7bc58e.png)

```test:guacamole.properties
# SAML Metadata URL from IdP (Azure AD, Okta, etc)
saml-idp-metadata-url: file:///etc/guacamole/FederationMetadata.xml

# Alternative: Single Sign On URL
# saml-idp-url: https://www.test.com/adfs/ls/IdpInitiatedSignOn.aspx

# These must match what was entered into Azure AD or Okta
saml-entity-id: https://guac.company.com
saml-callback-url: https://guac.company.com/guacamole

# Change this to true if SSO is not working
saml-debug: true

# Automatically redirect to SSO portal for sign-on
extension-priority: saml

# Alternative: Go to Guacamole local login before SSO
# extension-priority: *, saml

# SAML attribute/claim for group membership
saml-group-attribute: groups

# If SAML extension fails, default back to local login
skip-if-unavailable: saml
saml-strict: false
```

nginxに自己署名証明書を導入
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3636209/1b226d3a-8cfe-6f0e-a747-8d18faf8b3ae.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3636209/c5db288a-5f51-e338-2347-719d8ece487e.png)

```test:default.conf
server {
    listen 443 ssl;
    server_name  localhost;
    ssl_certificate      /etc/nginx/ssl/nginx.pem;
    ssl_certificate_key  /etc/nginx/ssl/nginx.key;

    location /guacamole/ {
        proxy_pass http://guacamole:8080/guacamole/;
        proxy_buffering off;
        proxy_http_version 1.1;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection $http_connection;
        client_max_body_size 10g;
        access_log off;
    }

    error_page   500 502 503 504  /50x.html;
    location = /50x.html {
        root   /usr/share/nginx/html;
    }
}
```

## Guacamoleにアクセス
docker compose up -dを実行して、ブラウザからアクセスします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3636209/65b1b0b9-ec99-159c-f6bc-2ee4a193856f.png)

ADFSのログイン画面へリダイレクトされました。この画面はカスタマイズできるみたいです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3636209/92d7cf71-502a-ad99-d482-a6abab5d7fc1.png)

ADのユーザとパスワードでサインイン後
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3636209/2908b32b-21ce-1638-aa4e-4bb1603d2347.png)

無事アクセスできました。

## 途中でハマったこと

* ActiveDirectoryのユーザーのメールアドレスを設定していないと、GuacamoleとAFDSのサインインの画面をループした。