IK.AM

@making's tech note


HashiCorp VaultをOpenID Connect Providerとして使用する

🗃 {Dev/SecretManagement/Vault}
🏷 Vault 🏷 OIDC 🏷 LDAP 
🗓 Updated at 2023-01-12T01:46:56Z  🗓 Created at 2023-01-11T03:04:05Z   🌎 English Page

Vault 1.9からVault自身がOpenID Connect Providerとして利用できるようになっていました。 Self HostedなOIDC Providerが必要な時、Vaultを既に運用していれば、DexやKeycloakなどを新規で構築しなくても既存のVaultをOIDC Providerとして利用できます。

Vaultはdefaultという名前のproviderが初めから提供されています。Issuer URLは${VAULT_ADDR}/v1/identity/oidc/provider/defaultです。

次のVault環境で検証してみます。

$ vault version
Vault v1.12.2 (415e1fe3118eebd5df6cb60d13defdc01aa17b03), built 2022-11-23T12:53:46Z

$ vault status
Key             Value
---             -----
Seal Type       shamir
Initialized     true
Sealed          false
Total Shares    5
Threshold       3
Version         1.12.2
Build Date      2022-11-23T12:53:46Z
Storage Type    s3
Cluster Name    vault-cluster-d5eb8945
Cluster ID      76979525-c584-407c-6b91-2671b591bf04
HA Enabled      false

この環境ではLDAP Auth Methodが有効になっています (こちらの記事で設定)。また、HTTPSで https://vault.maki.lol というURLで公開されているとします。

まずはOpenID Provider Configuration (/.well-known/openid-configuration)にアクセスしてみます。

$ curl -s https://vault.maki.lol/v1/identity/oidc/provider/default/.well-known/openid-configuration | jq .
{
  "issuer": "/v1/identity/oidc/provider/default",
  "jwks_uri": "/v1/identity/oidc/provider/default/.well-known/keys",
  "authorization_endpoint": "/ui/vault/identity/oidc/provider/default/authorize",
  "token_endpoint": "/v1/identity/oidc/provider/default/token",
  "userinfo_endpoint": "/v1/identity/oidc/provider/default/userinfo",
  "request_parameter_supported": false,
  "request_uri_parameter_supported": false,
  "id_token_signing_alg_values_supported": [
    "RS256",
    "RS384",
    "RS512",
    "ES256",
    "ES384",
    "ES512",
    "EdDSA"
  ],
  "response_types_supported": [
    "code"
  ],
  "scopes_supported": [
    "openid"
  ],
  "claims_supported": [],
  "subject_types_supported": [
    "public"
  ],
  "grant_types_supported": [
    "authorization_code"
  ],
  "token_endpoint_auth_methods_supported": [
    "none",
    "client_secret_basic",
    "client_secret_post"
  ]
}

各キーの値がURLではなく、パスだけになっています。

次のコマンドでdefault providerの設定を確認すると確かにissuerがURL形式になっていません。このままだとアプリがエンドポイントを検出する際に問題になります。

$ vault read identity/oidc/provider/default
Key                   Value
---                   -----
allowed_client_ids    [*]
issuer                /v1/identity/oidc/provider/default
scopes_supported      []

次のコマンドでdefault providerのissuerを変更します。

vault write identity/oidc/provider/default issuer=https://vault.maki.lol

これでOpenID Provider Configurationも次のようにURLを返すようになります。

$ curl -s https://vault.maki.lol/v1/identity/oidc/provider/default/.well-known/openid-configuration | jq .
{
  "issuer": "https://vault.maki.lol/v1/identity/oidc/provider/default",
  "jwks_uri": "https://vault.maki.lol/v1/identity/oidc/provider/default/.well-known/keys",
  "authorization_endpoint": "https://vault.maki.lol/ui/vault/identity/oidc/provider/default/authorize",
  "token_endpoint": "https://vault.maki.lol/v1/identity/oidc/provider/default/token",
  "userinfo_endpoint": "https://vault.maki.lol/v1/identity/oidc/provider/default/userinfo",
  "request_parameter_supported": false,
  "request_uri_parameter_supported": false,
  "id_token_signing_alg_values_supported": [
    "RS256",
    "RS384",
    "RS512",
    "ES256",
    "ES384",
    "ES512",
    "EdDSA"
  ],
  "response_types_supported": [
    "code"
  ],
  "scopes_supported": [
    "openid"
  ],
  "claims_supported": [],
  "subject_types_supported": [
    "public"
  ],
  "grant_types_supported": [
    "authorization_code"
  ],
  "token_endpoint_auth_methods_supported": [
    "none",
    "client_secret_basic",
    "client_secret_post"
  ]
}

動作検証用のOIDCクライアントとしてDexのexample-appを使用します。

https://github.com/dexidp/dex/tree/master/examples/example-app

次のコマンドでこのOIDCクライアントの情報をVaultに登録します。

vault write identity/oidc/client/example-app \
  redirect_uris="http://127.0.0.1:5555/callback" \
  assignments="allow_all"

CLIENT_IDとCLIENT_SECRETを次のコマンドで取得します。

CLIENT_ID=$(vault read -field client_id identity/oidc/client/example-app)
CLIENT_SECRET=$(vault read -field client_secret identity/oidc/client/example-app)

example-appを起動します。

docker run --rm -p 5555:5555 obitech/dex-example-app --debug --issuer https://vault.maki.lol/v1/identity/oidc/provider/default --listen http://0.0.0.0:5555 --client-id ${CLIENT_ID} --client-secret ${CLIENT_SECRET}

example-appのURL http://127.0.0.1:5555 にアクセスしてログインボタンをクリック

image

Vault UIのログイン画面にリダレクトされ、ログイン済みならそのまま次の画面にリダイレクトされます。

image

デコードされたJWTが表示されます。基本的にはこれでOKなのですが、JWTのペイロードをよく見ると、ユーザー名が含まれていません。

LDAP Authを使用しているからかどうかわかりませんが、ユーザー名を含めたいので次のように、OIDCのScopeを追加して、LDAP Authのnameの項目をusername Claimに含めるようにします。

MOUNT_ACCESOR=$(vault read -field accessor sys/auth/ldap)
PROFILE_SCOPE_TEMPLATE="{\"username\": {{identity.entity.aliases.$MOUNT_ACCESOR.name}}}"
vault write identity/oidc/scope/profile template="$(echo ${PROFILE_SCOPE_TEMPLATE} | base64 -)"

default prodiverに追加したScope (profile)をサポートさせます。

vault write identity/oidc/provider/default scopes_supported=profile

次のコマンドでprofileスコープがサポートされたことを確認できます。

$ vault read identity/oidc/provider/default
Key                   Value
---                   -----
allowed_client_ids    [*]
issuer                https://vault.maki.lol/v1/identity/oidc/provider/default
scopes_supported      [email profile]

OpenID Provider Configurationを確認するとprofile scopeが含まれます。

$ curl -s https://vault.maki.lol/v1/identity/oidc/provider/default/.well-known/openid-configuration | jq .
{
  "issuer": "https://vault.maki.lol/v1/identity/oidc/provider/default",
  "jwks_uri": "https://vault.maki.lol/v1/identity/oidc/provider/default/.well-known/keys",
  "authorization_endpoint": "https://vault.maki.lol/ui/vault/identity/oidc/provider/default/authorize",
  "token_endpoint": "https://vault.maki.lol/v1/identity/oidc/provider/default/token",
  "userinfo_endpoint": "https://vault.maki.lol/v1/identity/oidc/provider/default/userinfo",
  "request_parameter_supported": false,
  "request_uri_parameter_supported": false,
  "id_token_signing_alg_values_supported": [
    "RS256",
    "RS384",
    "RS512",
    "ES256",
    "ES384",
    "ES512",
    "EdDSA"
  ],
  "response_types_supported": [
    "code"
  ],
  "scopes_supported": [
    "profile",
    "openid"
  ],
  "claims_supported": [],
  "subject_types_supported": [
    "public"
  ],
  "grant_types_supported": [
    "authorization_code"
  ],
  "token_endpoint_auth_methods_supported": [
    "none",
    "client_secret_basic",
    "client_secret_post"
  ]
}

これで再度ログインするとJWTのusername ClaimにLDAPのcnが含まれました。

image

LDAP Authだとuserattrで指定した項目(e.g. cn or mail)1つしかJWTのclaimに含められなさそうなのが少し使い勝手が悪いですが、項目1つで済むレベルの使い方であれば新規でDexを立てるよりは楽で良いですね。


✒️️ Edit  ⏰ History  🗑 Delete