🐙

Keycloak で Argo CD の SSO を構築する方法

に公開

クラウドエース 北野です。

Argo CD の SSO を Keycloak のアカウントで実現する方法を紹介します。

概要

Keycloak と Argo CD を次の様に設定して、Keycloak を外部 ID プロバイダとして構築し、Keycloak のアカウントで Argo CD でログインします。

  • Keycloak での設定
    • OpenID Connect のクライアント作成
    • クライアントスコープ group の作成
  • Argo CD で OIDC プロバイダの設定

Keycloak での認証は次の2つの方法があります。

  • クライアント認証
  • PKCE

クライアント認証で SSO するための Keycloak の設定は次の通りです。

client.tf
resource "keycloak_openid_client" "main" {
  client_id = "argocd"

  realm_id                        = keycloak_realm.main.id
  name                            = "ArgoCD"
  access_type                     = "CONFIDENTIAL"
  service_accounts_enabled        = false
  valid_redirect_uris             = [
    "https://<ARGOCD HOSTNAME>/auth/callback",
  ]
  valid_post_logout_redirect_uris = [
    "https://<ARGOCD HOSTNAME>/applications"
  ]
  web_origins                     = [
    "https://<ARGOCD HOSTNAME>"
  ]
  root_url                        = "https://<ARGOCD HOSTNAME>"
  admin_url                       = "https://<ARGOCD HOSTNAME>"
  base_url                        = "/applications"
  standard_flow_enabled           = true
  implicit_flow_enabled           = false
  direct_access_grants_enabled    = true
  client_session_max_lifespan     = 604800
  access_token_lifespan           = 180
}

resource "keycloak_openid_client_optional_scopes" "main" {
  client_id = keycloak_openid_client.main.id

  realm_id        = keycloak_realm.main.id
  optional_scopes = [
    keycloak_openid_client_scope.main["groups"].name
  ]
}

resource "keycloak_openid_client_scope" "main" {
  name = "groups"

  realm_id               = keycloak_realm.main.id
  include_in_token_scope = true
}

resource "keycloak_openid_group_membership_protocol_mapper" "main" {
  name = "groups"

  realm_id            = keycloak_realm.main.id
  client_scope_id     = keycloak_openid_client_scope.main.id
  claim_name          = "groups"
  full_path           = false
  add_to_id_token     = true
  add_to_access_token = true
  add_to_userinfo     = true
}

Argo CD の設定は次の通りです。

argocd-cm.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: argocd-cm
data:
  url: https://<ARGOCD HOSTNAME>
  oidc.config: |
    name: Keycloak
    issuer: https://<KEYCLOAK HOSTNAME>/realms/<REALM>
    clientID: argocd
    clientSecret: <CLIENT SECRET>
    requestedScopes: ["openid", "profile", "email", "groups"]

次に PKCE による SSO の Keycloak の設定は次の通りです。

client.tf
resource "keycloak_openid_client" "main" {
  client_id = "argocd"

  realm_id                        = keycloak_realm.main.id
  name                            = "ArgoCD"
  access_type                     = "PUBLIC"
  service_accounts_enabled        = false
  valid_redirect_uris             = [
    "http://localhost:8085/auth/callback",
    "https://<ARGOCD HOSTNAME>/auth/callback",
    "https://<ARGOCD HOSTNAME>/pkce/verify"
  ]
  valid_post_logout_redirect_uris = [
    "https://<ARGOCD HOSTNAME>/applications"
  ]
  web_origins                     = [
    "https://<ARGOCD HOSTNAME>"
  ]
  root_url                        = "https://<ARGOCD HOSTNAME>"
  admin_url                       = "https://<ARGOCD HOSTNAME>"
  base_url                        = "/applications"
  standard_flow_enabled           = true
  implicit_flow_enabled           = false
  direct_access_grants_enabled    = true
  client_session_max_lifespan     = 604800
  access_token_lifespan           = 180
  pkce_code_challenge_method      = "S256"
}

resource "keycloak_openid_client_optional_scopes" "main" {
  client_id = keycloak_openid_client.main.id

  realm_id        = keycloak_realm.main.id
  optional_scopes = [
    keycloak_openid_client_scope.main.name
  ]
}

resource "keycloak_openid_client_scope" "main" {
  name = "groups"

  realm_id               = keycloak_realm.main.id
  include_in_token_scope = true
}

resource "keycloak_openid_group_membership_protocol_mapper" "main" {
  name = "groups"

  realm_id            = keycloak_realm.main.id
  client_scope_id     = keycloak_openid_client_scope.main.id
  claim_name          = "groups"
  full_path           = false
  add_to_id_token     = true
  add_to_access_token = true
  add_to_userinfo     = true
}

Argo CD の設定は次の通りです。

cm-argcd.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: argocd-cm
  labels:
    app.kubernetes.io/name: argocd-cm
    app.kubernetes.io/part-of: argocd
data:
  admin.enabled: "false"
  url: https://<ARGOCD HOSTNAME>
  oidc.config: |
    name: Keycloak
    issuer: https://<KEYCLOAK HOSTNAME>/realms/<REALM>
    clientID: argocd
    enablePKCEAuthentication: true
    requestedScopes: ["openid", "profile", "groups" ,"email"]

この記事では、上記の設定を以下の様な Google Kubernetes Engine 上に構築した Argo CD と Keycloak を使って説明します。

はじめに

以前、Google アカウントで Argo CD の SSO を実現する方法を紹介しました。しかし、企業のセキュリティ要件などにより、プライベート環境に構築した認証基盤で SSO させる必要があるかと思います。
そこで GKE 上に構築した Keycloak で Argo CD の SSO を実現する方法を紹介します。

https://zenn.dev/cloud_ace/articles/argocd_google_accout_sso

Argo CD での SSO の実現方法

Argo CD では、次の2種類の SSO のプロバイダがあります。

  • Argo CD にインストールされている Dex OIDC プロバイダ
  • 既存の OIDC プロバイダ (Okta, OneLogin, Auth0, Microsoft, Keycloak)

SSO の設定は、Argo CD の Configmap argocd-cm に設定します。
Dex と既存の OIDC プロバイダのそれぞれの設定は以下のようにします。

  • Dex の SSO 設定
cm-argocd.yaml
data:
  dex.config: |
    connectors:
      - type: <TYPE>
        id: <ID>
        name: <NAME>
        config: <CONECTION INFO>
  • 既存のプロバイダの SSO 設定
cm-argocd.yaml
data:
  oidc.config: |
    type: <TYPE>
    id: <ID>
    name: <NAME>
    issuer: <IDP URL>

前回、Google アカウントで SSO を実現する方法の記事で Dex について簡単に説明しているので、Dex について気になる方は、そちらをご参照ください。

https://zenn.dev/cloud_ace/articles/argocd_google_accout_sso#sso-の実現方法

この記事では、既存プロバイダに分類される Keycloak を使って、SSO を実現する方法を紹介します。

Keycloak での SSO

Keycloak での SSO の実現は、次の2種類の認証方法があります。

  • クライアント認証
  • Proof Key for Code Exchange (以降 PKCE と呼びます)

この記事では、クライアント認証と PKCE によるそれぞれの SSO の実現方法を両方紹介します。
以下で説明しているコードの <> で囲まれている箇所は、構築する環境に合わせて読みかえてください。

事前準備

Google Cloud 上に以下のようなシステムを構築します。

Argo CD と Keycloak を Google Cloud の Google Kubernetes Engine (以降 GKE と呼びます)上に構築します。
Keycloak のデータベースは Cloud SQL を使い、Argo CD のシークレット情報を Secret Manager に登録し、External Secret を使って参照します。Argo CD と Keycloak の インターネットへの公開には、Gateway を使い公開します。

それぞれの構築方法の詳細は以下の記事をご確認ください。
https://zenn.dev/cloud_ace/articles/argocd_on_gke
https://zenn.dev/cloud_ace/articles/gke-keycloak

Keycloak の設定

今回、platform という realm に作り、以下の Keycloak のアカウントで SSO します。
このとき、権限はアカウントが所属する以下のグループに権限を付与します。

アカウントの設定
ユーザー名 sample
email sample@example.com
first name sample
last name test
所属グループ administrators

platform の realm を次の様に Terraform で作成します。

realm.tf
resource "keycloak_realm" "main" {
  realm = "platform"
  
  display_name                   = "platform"
  registration_allowed           = false
  registration_email_as_username = true
  login_with_email_allowed       = true
  verify_email                   = false
  reset_password_allowed         = true
  sso_session_idle_timeout       = "336h"
  sso_session_max_lifespan       = "720h"
}

ユーザーとグループの作成および、グループへのユーザーの追加を次の様に Terraform で作成します。

user.tf
resource "keycloak_group" "main" {
  name = "administrators"

  realm_id   = keycloak_realm.main.id
}

resource "keycloak_user" "main" {
  username = "sample"

  realm_id = keycloak_realm.main.id
  enabled  = true

  email      = "sample@example.com"
  first_name = "sample"
  last_name  = "test"

  initial_password {
    value     = "sample"
    temporary = true
  }
}

resource "keycloak_group_memberships" "main" {
  group_id = keycloak_group.main.id
  
  realm_id = keycloak_realm.main.id
  members = [
    keycloak_user.main.username
  ]
}

Argo CD 側には、作成したグループ (administrators) に admin 権限を設定します。
設定は次のように ConfigMap argocd-rbac-cm に権限を定義します。

cm-argocd-rbac.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: argocd-rbac-cm
data:
  policy.csv: |
    g, administrators, role:admin

Keycloak アカウントでの SSO

Keycloak クライアント認証と PKCE によって、Argo CD に SSO する方法を説明します。
今回、Keycloak のグループに権限を付与します。そのため、まず、グループに権限を付与するために必要な Keycloak のクライアントスコープの設定をします。
続いてクライアント認証と PKCE で Keycloak と Argo CD の設定を紹介して、Keycloak のアカウントログインできるかを確認します。

Keycloak のクライアントスコープの設定

Keycloak のグループで認証させるためにクライアントスコープ groups を以下の設定で作成します。

設定項目 設定内容
name groups
include in token scope true

またクライアントスコープ groups の Mappers を次のように設定します。

設定項目 設定内容
Mapper Type Group Membership
Name groups
Token Claim Name groups
Add to ID token true
Add to access token true
Add to userinfo true
Full group path false

これらを次の様に Terraform で設定します。

client_scope.tf
resource "keycloak_openid_client_scope" "main" {
  name = "groups"

  realm_id               = keycloak_realm.main.id
  include_in_token_scope = true
}

resource "keycloak_openid_group_membership_protocol_mapper" "main" {
  name = "groups"

  realm_id            = keycloak_realm.main.id
  client_scope_id     = keycloak_openid_client_scope.main.id
  claim_name          = "groups"
  full_path           = false
  add_to_id_token     = true
  add_to_access_token = true
  add_to_userinfo     = true
}

Keycloak のクライアント認証による SSO

Keycloak のクライアント認証による Argo CD の SSO では、以下を実施します。

  • Keycloak: OpenID Connect のクライアントの作成
  • Google Cloud: Keycloak のクライアントのシークレットを保存する Secret Manager の作成
  • Kubernetes: Keycloak のクライアントシークレットの情報を Secret Manager から取得する External Secret の作成
  • Argo CD: oidc の設定

まず、Keycloak 側で Open ID Connect のクライアントを以下の設定で作成します。

設定項目
Client type OpenID Connect
Client ID argocd
Client authentication true
Authorization false
Standard flow true
Direct access grants true
Implicit flow false
Service accounts roles false
OAuth 2.0 Device Authorization Grant false
OIDC CIBA Grant false
Root URL http://<ARGOCD HOSTNAME>
Home URL /Applications
Valid redirect URIs https://<ARGOCD HOSTNAME>/auth/callback
Valid post logout redirect URIs https://<ARGOCD HOSTNAME>/applications
Web origins http://<ARGOCD HOSTNAME>

上記の Keycloak のクライアントを次の様に Terraform で作成します。

client.tf
resource "keycloak_openid_client" "main" {
  client_id = "argocd"

  realm_id                        = keycloak_realm.main.id
  name                            = "ArgoCD"
  access_type                     = "CONFIDENTIAL"
  service_accounts_enabled        = false
  valid_redirect_uris             = [
    "https://<ARGOCD HOSTNAME>/auth/callback",
  ]
  valid_post_logout_redirect_uris = [
    "https://<ARGOCD HOSTNAME>/applications"
  ]
  web_origins                     = [
    "https://<ARGOCD HOSTNAME>"
  ]
  root_url                        = "https://<ARGOCD HOSTNAME>"
  admin_url                       = "https://<ARGOCD HOSTNAME>"
  base_url                        = "/applications"
  standard_flow_enabled           = true
  implicit_flow_enabled           = false
  direct_access_grants_enabled    = true
  client_session_max_lifespan     = 604800
  access_token_lifespan           = 180
}

resource "keycloak_openid_client_optional_scopes" "main" {
  client_id = keycloak_openid_client.main.id

  realm_id        = keycloak_realm.main.id
  optional_scopes = [
    keycloak_openid_client_scope.main.name
  ]
}

作成したクライアント argocd のクライアントシークレットを Google Cloud の Secret Manager に登録し、Kubernetes の External Secret を使って、Secret に連携します。
それぞれ、次のようにリソースを作成します。

リソースの種類 リソース名
Secret Manager argocd-keycloak-oidc-client-secret
Secret argocd-oicd-client.keycloak-oidc-client-secret

次の Terraform コードで Google Cloud 上に Secret Manager を作成して、Keycloak のクライアントシークレットを登録します。

secret_manager.tf
resource "google_secret_manager_secret" "main" {
  secret_id = "argocd-keycloak-oidc-client-secret"

  project = var.project
  replication {
    user_managed {
      replicas {
        location = "asia-northeast1"
      }
    }
  }
}

resource "google_secret_manager_secret_version" "main" {
  secret      = google_secret_manager_secret.main.id
  secret_data = keycloak_openid_client.main.client_secret
}

Secret Manager の情報を次のマニフェストで作成される External Secret で Secret に連携します。

es-argocd-oicd-client.yaml
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
  name: es-argocd-oicd-client
  namespace: argocd
  labels:
    app.kubernetes.io/part-of: argocd
spec:
  refreshInterval: 1h
  secretStoreRef:
    name: argocd-css
    kind: ClusterSecretStore
  target:
    name: argocd-oicd-client
    creationPolicy: Owner
  data:
    - secretKey: keycloak-oidc-client-secret
      remoteRef:
        key: argocd-keycloak-oidc-client-secret
        version: latest

最後に argocd-cm の ConfigMap に Keycloak の SSO を次の様に設定します。

argocd-cm.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: argocd-cm
data:
  url: https://<ARGOCD HOSTNAME>
  oidc.config: |
    name: Keycloak
    issuer: https://<KEYCLOAK HOSTNAME>/realms/platform
    clientID: argocd
    clientSecret: $argocd-oicd-client:keycloak-oidc-client-secret
    requestedScopes: ["openid", "profile", "email", "groups"]

Argo CD にアクセスすると、LOG IN VIA KEYCLOAK のボタンが表示されます。

ボタンをクリックすると、Keycloak のログイン画面にリダイレクトされます。

作成したアカウントでログインすると、パスワードのアップデートを促されるので、アップデートします。

パスワードをアップデートすると、ログインに成功し Application の一覧画面が表示されます。

最後にログインしたユーザーの情報を確認すると、次のようになっています。
ユーザーは Keycloak で作成した sample@example.com ユーザーでログインしており、administrators のグループで権限を付与されていることが分かります。

Keycloak の PKCE よる SSO

PKCE は OAuth 2.0 の認可コードフローのセキュリティを強固にします。クライアントシークレットを安全に保存できない場合などに利用されています。PKCE は RFC7636 に定義されているので詳細はそちらをご確認ください。

https://datatracker.ietf.org/doc/html/rfc7636

Argo CD も Keycloak の PKCE を使った SSO に対応しているので、その方法を紹介します。Keycloak の PKCE による Argo CD の SSO は Keycloak と Argo CD にそれぞれ次の様な設定をします。

  • Keycloak: OpenID Connect のクライアントの作成
  • Argo CD: oidc の設定

Keycloak の OpenID Connect クライアントを以下の設定で作成します。

設定項目
Client type OpenID Connect
Client ID argocd
Client authentication false
Authorization false
Standard flow true
Direct access grants true
Implicit flow false
Service accounts roles false
OAuth 2.0 Device Authorization Grant false
OIDC CIBA Grant false
Proof Key for Code Exchange Code Challenge Method S256
Root URL http://<ARGOCD HOSTNAME>
Home URL /applications
Valid redirect URIs https://<ARGOCD HOSTNAME>/auth/callback, http://localhost:8085/auth/callback, https://<ARGOCD HOSTNAME>/pkce/verify
Valid post logout redirect URIs https://<ARGOCD HOSTNAME>/applications
Web origins http://<ARGOCD HOSTNAME>

上記の内容を次の様に Terraform で作成します。

client.tf
resource "keycloak_openid_client" "main" {
  client_id = "argocd"

  realm_id                        = keycloak_realm.main.id
  name                            = "ArgoCD"
  access_type                     = "PUBLIC"
  service_accounts_enabled        = false
  valid_redirect_uris             = [
    "http://localhost:8085/auth/callback",
    "https://<ARGOCD HOSTNAME>/auth/callback",
    "https://<ARGOCD HOSTNAME>/pkce/verify"
  ]
  valid_post_logout_redirect_uris = [
    "https://<ARGOCD HOSTNAME>/applications"
  ]
  web_origins                     = [
    "https://<ARGOCD HOSTNAME>"
  ]
  root_url                        = "https://<ARGOCD HOSTNAME>"
  admin_url                       = "https://<ARGOCD HOSTNAME>"
  base_url                        = "/applications"
  standard_flow_enabled           = true
  implicit_flow_enabled           = false
  direct_access_grants_enabled    = true
  client_session_max_lifespan     = 604800
  access_token_lifespan           = 180
  pkce_code_challenge_method      = "S256"
}

resource "keycloak_openid_client_optional_scopes" "main" {
  client_id = keycloak_openid_client.main.id

  realm_id        = keycloak_realm.main.id
  optional_scopes = [
    keycloak_openid_client_scope.main.name
  ]
}

次に argocd-cm の ConfigMap に Keycloak の SSO を次の様に設定します。

cm-argocd.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: argocd-cm
  labels:
    app.kubernetes.io/name: argocd-cm
    app.kubernetes.io/part-of: argocd
data:
  admin.enabled: "false"
  url: https://<ARGOCD HOSTNAME>
  oidc.config: |
    name: Keycloak
    issuer: https://<KEYCLOAK HOSTNAME>/realms/platform
    clientID: argocd
    enablePKCEAuthentication: true
    requestedScopes: ["openid", "profile", "groups" ,"email"]

Argo CD にアクセスすると、LOG IN VIA KEYCLOAK のボタンが表示されます。

ボタンをクリックすると、Keycloak のログイン画面にリダイレクトされます。

作成したアカウントでログインすると、パスワードのアップデートを促されるので、アップデートします。

パスワードをアップデートすると、ログインに成功し Application の一覧画面が表示されます。

最後にログインしたユーザーの情報を確認すると、次のようになっています。
ユーザーは Keycloak で作成した sample@example.com ユーザーでログインしており、administrators のグループで権限を付与されていることが分かります。

さいごに

Argo CD の SSO を Keycloak で実現する方法を紹介しました。
オンプレミスの環境で Argo CD を運用などされている場合、活用してみてください。

Discussion