親記事はこちら
AWS ALB で LDAP 認証連携に対応する
https://qiita.com/batatch/items/fbc405e04edba75325ef
ALB による LDAP 認証連携を以下のように構築します。
OIDC-LDAP 変換の dexidp はシンプルに EC2 上の docker で動かします。
LDAP 連携のため、社内システムを想定していますが、親記事に書いたように dexidp は正規の証明書を設定した HTTPS でアクセスさせる必要があるため、アプリ用と dexidp 用で ALB を分けています。
なお、dexidp から LDAP 接続するため、AWS と社内ネットワーク等がつながっていることとします。
dexidp を起動する
まずは、EC2 上で dexidp を立ち上げます。
以下のように Dockerfile と docker-compose.yml を用意して docker で実行します。
Dockerfile
FROM golang:1.13-alpine AS build-env
RUN apk add --no-cache --update alpine-sdk
RUN mkdir -p /go/src/github.com/dexidp \
&& cd /go/src/github.com/dexidp \
&& git clone https://github.com/dexidp/dex.git -b v2.21.0 --depth 1
RUN cd /go/src/github.com/dexidp/dex && make release-binary
FROM alpine:3.10
RUN apk add --update ca-certificates openssl sqlite
USER root
COPY --from=build-env /go/bin/dex /usr/local/bin/dex
COPY --from=build-env /go/src/github.com/dexidp/dex/web /web
VOLUME /dex
WORKDIR /
ENTRYPOINT ["dex"]
docker-compose.yml
version: "3.3"
services:
dexidp:
image: dexidp
build:
context: .
command: >
serve /dex/config.yaml
volumes:
- /data/dexidp:/dex
ports:
- "5554:5554"
- "5556:5556"
- "5558:5558"
dexidp の実行環境は以下のようになっていて、設定ファイルとストレージの SQLite ファイルを外部ボリューム参照にしています。
/usr/local/bin/dex : 実行ファイル
/web/ : 認証画面のソース
/dex/ --> ホスト:/data/ へマウント
config.yaml : 設定ファイル
dex.db : ストレージファイル(SQLite)
設定ファイルは LDAP 接続用に connectors の type に ldap を指定します。(LDAP 設定は環境に合わせて要確認)
また、issuer、staticClients.redirectURIs はそれぞれの ALB に割り当てるドメインに合わせて設定します。
- issuer : dexidp の前段に置く ALB のドメイン(正規の HTTPS 必須)
- staticClients.redirectURIs : アプリの前段に置く ALB のドメイン
/dex/config.yaml
issuer: https://oidc.example.com/dex
storage:
type: sqlite3
config:
file: dex/dex.db
web:
http: 0.0.0.0:5556
connectors:
- type: ldap
name: OpenLDAP
id: ldap
config:
host: ldap.example.com:389
insecureNoSSL: true
bindDN: cn=admin,dc=example,dc=org
bindPW: admin
usernamePrompt: User ID
userSearch:
baseDN: ou=People,dc=example,dc=com
username: mail
idAttr: DN
emailAttr: mail
nameAttr: cn
groupSearch:
baseDN: ou=Groups,dc=example,dc=com
userAttr: DN
groupAttr: member
nameAttr: cn
staticClients:
- id: app-id
redirectURIs:
- 'https://app.example.com/oauth2/idpresponse'
name: 'App'
secret: ZXhhbXBsZS1hcHAtc2VjcmV0
各種ファイルを用意したら、docker をビルドして起動します。
$ docker-compose build
$ docker-compose up
下記の URL へアクセスし、OIDC の接続情報が返ってきたら dexidp の起動完了です。
接続情報は ALB への認証連携の設定で利用します。
$ curl localhost:5556/dex/.well-known/openid-configuration
{
"issuer": "https://oidc.example.com/dex",
"authorization_endpoint": "https://oidc.example.com/dex/auth",
"token_endpoint": "https://oidc.example.com/dex/token",
"jwks_uri": "https://oidc.example.com/dex/keys",
"userinfo_endpoint": "https://oidc.example.com/dex/userinfo",
"response_types_supported": [
"code"
],
"subject_types_supported": [
"public"
],
"id_token_signing_alg_values_supported": [
"RS256"
],
"scopes_supported": [
"openid",
"email",
"groups",
"profile",
"offline_access"
],
"token_endpoint_auth_methods_supported": [
"client_secret_basic"
],
"claims_supported": [
"aud",
"email",
"email_verified",
"exp",
"iat",
"iss",
"locale",
"name",
"sub"
]
}
ターゲットグループを作成する
dexidp 向けのターゲットグループを作成します。
AWS コンソール >
EC2 > ターゲットグループ > ターゲットグループの作成
- ターゲットグループ名: tg-oidc
- ターゲットの種類: インスタンス
- プロトコル: HTTP
- ポート: 5556
- VPC: ****
- ヘルスチェック プロトコル: HTTP
- ヘルスチェック パス: /dex/.well-known/openid-configuration
ターゲット > 編集から、対象の EC2 インスタンスを登録します。
この時点ではヘルスチェックは行われないので、ステータスは unused となります。
後ほど、ターゲットグループを ALB へ割り当てるとヘルスチェックが実施され、ステータスが healthy になります。
ターゲットが healthy にならないときは、EC2 のセキュリティグループで TCP 5556 ポートへのインバウンドが許可されていない可能性があります。
アプリ側も同様にターゲットグループを登録しておきます。
ALB を作成する
dexidp 用の ALB を作成します。
こちらはパブリックなドメインと正規の SSL 証明書が必要になるので、Route53 等でドメインの登録、ACM で証明書の用意などを済ませておきます。
ここでは、以下のドメインを oidc.example.com とします。
AWS コンソール >
EC2 > ロードバランサ― > ロードバランサ―の作成 > Application Load Balancer の作成
- 名前: alb-oidc
- スキーム: 内部
- IPアドレスタイプ: ipv4
- リスナーの追加 ロードバランサ―のプロトコル: HTTPS
- リスナーの追加 ロードバランサ―のプロトコル: 443
- アベイラビリティゾーン VPC: ****
HTTPS のリスナーを登録するので、証明書のタイプ、名前、セキュリティポリシーの選択などが続きます。
ルーティングの設定で、先ほど作成したターゲットグループを割り当てます。
- ターゲットグループ: 既存のターゲットグループ
- 名前: tg-oidc
- ターゲットの種類: インスタンス
- プロトコル: HTTP
- ポート: 5556
- VPC: ****
- ヘルスチェック プロトコル: HTTP
- ヘルスチェック パス: /dex/.well-known/openid-configuration
ALB を作成したら、詳細画面の「リスナー」タブにて転送ルールの設定を行います。
dexidp 用は以下のルールとなります。
- IF: パスが /dex または /dex/*
- THEN:
- 転送先: tg-app
ルールを設定したら、ALB の個別ドメイン経由で dexidp へアクセスできることを確認しておきます。
(今回は内部 ALB なので、VPC 内の EC2 などから確認します)
$ curl https://internal-alb-oidc-0000000000.ap-northeast-1.elb.amazonaws.com/dex/.well-known/openid-configuration
ALB 経由でのアクセスができたら、DNS(Route53等) にてパブリックドメイン→個別ドメインの CNAME レコードを登録し、パブリックドメイン指定で dexidp へアクセスできることを確認します。
oidc.example.com. CNAME internal-alb-oidc-0000000000.ap-northeast-1.elb.amazonaws.com
$ curl https://oidc.example.com/dex/.well-known/openid-configuration
アプリ ALB へ認証設定を追加する
アプリ側 ALB のリスナーのルールへ認証設定を追加します。
アプリ側の ALB はあらかじめ用意されているとし、dexidp とは別ドメイン(app.example.local) を設定していることとします。
アプリのパスは /app/* とします。
リスナールールの設定を開いて以下のルールを設定します。設定値は dexidp の /dex/.well-known/openid-configuration の結果、および設定ファイル config.yaml を参考にします。
- IF: パスが /app または /app/*
- THEN:
- 認証: OIDC
- 発行者: https://oidc.example.com/dex
- 認証エンドポイント: https://oidc.example.com/dex/auth
- トークンエンドポイント: https://oidc.example.com/dex/token
- ユーザ情報エンドポイント: https://oidc.example.com/dex/userinfo
- クライアントID: alb-id
- クライアントのシークレット: ZXhhbXBsZS1hcHAtc2VjcmV0
- セッション Cookie 名: AWSELBAuthSessionCookie ※任意
- セッションタイムアウト: 60 ※任意
- スコープ: openid+profile+email
- 認証されていないリクエストのアクション: 認証
- 転送先: tg-app
- 認証: OIDC
ブラウザでアプリの URL へアクセスし、dexidp のログインフォーム画面へリダイレクトされることを確認します。フォームから LDAP 認証アカウントを指定してログインができたら認証連携の成功です。
https://app.example.local/app
アプリで認証済のアカウント名を取得する
ALB の認証連携を経てアプリに届いたリクエストには以下のヘッダが設定されています。
- x-amzn-oidc-data
- x-amzn-oidc-identity
- x-amzn-oidc-accesstoken
x-amzn-oidc-data と x-amzn-oidc-accesstoken は JWT 形式のトークンとなっていて、"." で 3つのトークンに区切られます。
以下のサイトでデコードすることができます。
デコードしてみると、3つのトークンはそれぞれ HEADER、PAYLOAD、SIGNATURE に分割され、前者の 2つは JSON 文字列ということが分かります。
HEADER はアルゴリズムなどエンコード方法に関する情報で、アカウントやメールアドレスなどの属性情報は 2つめの PAYLOAD に格納されています。
自分で JWT をデコードする場合は、トークンを "." で分割し、2つめの PAYLOAD のトークンを Base64 デコードすれば JSON 形式で取得できます。
{
"iss": "https://oidc.example.com/dex",
"sub": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
"aud": "alb-id",
"exp": 9999999999,
"iat": 9999999999,
"at_hash": "XXXXXXXXXXXXXXXXXXXXXX",
"email": "user001@example.com",
"email_verified": true,
"name": "user001"
}
PAYLOAD の JSON 項目は設定次第になりますが、今回の LDAP 認証の場合は name としてアカウント名を参照できます。
今回は ALB で作成・設定された JWT をそのまま信用して利用しますが、本来は SIGNATURE を評価して JWT 自体の信頼性を検証します。