Vault 1.9からVault自身がOpenID Connect Providerとして利用できるようになっていました。 Self HostedなOIDC Providerが必要な時、Vaultを既に運用していれば、DexやKeycloakなどを新規で構築しなくても既存のVaultをOIDC Providerとして利用できます。
- https://developer.hashicorp.com/vault/docs/secrets/identity/oidc-provider
- https://developer.hashicorp.com/vault/tutorials/auth-methods/oidc-identity-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 にアクセスしてログインボタンをクリック

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

デコードされた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
が含まれました。

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