暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

Keycloak:轻松实现开放式身份验证和授权

k8s技术圈 2023-04-24
4185

作者:王金鑫,中国移动磐基 PaaS 平台中间件专业服务项目组成员

Keycloak[1] 核心概念和术语

Keycloak 是 Web 应用程序和 RESTful Web 服务的单点登录解决方案。Keycloak 的目标是使安全变得简单,从而使应用程序开发人员能够轻松地保护他们的应用程序和服务。开发人员通常要求编写的安全功能是开箱即用的,并且很容易根据组织的要求进行定制化。Keycloak 为登录、注册、管理和帐户管理提供可定制的用户界面。你也可以把 Keycloak 作为一个集成平台,将其连接到现有的 LDAP 和 Active Directory 服务器。您还可以将身份验证委托给第三方身份提供商,例如 Github 和 Google。

在尝试使用 Keycloak 来保护您的 Web 应用程序和 REST 服务之前,请考虑这些核心概念和术语。

  • realms 是指认证和授权的子域,用于管理和验证用户身份。每个 realm 都有自己的身份验证和授权配置。
  • clients 是指使用 Keycloak 作为 API 客户端的程序或服务。Keycloak 提供了多种 API(例如 HTTP API、JSON API 和 Websocket API) 供客户端使用。客户端可以使用 Keycloak 提供的 API 访问 Keycloak 后端服务,以获得访问令牌 (access tokens) 或其他授权资源所需的权限。
  • groups 是指一组具有相同权限的用户或客户端。用户可以加入多个 group,每个 group 都具有特定的权限范围。group 可以是基于用户的,也可以是基于客户端的。例如,在一个企业中,可以创建一个名为“admin”的 group,并将所有管理员添加到这个 group 中,以便他们访问系统中的所有权限。
  • users 是指使用 Keycloak 的用户。Keycloak 提供了多种方式来验证用户的身份,例如密码、社会安全号码、护照等。用户可以在 Keycloak 中创建、修改或删除,并且可以使用 Keycloak 提供的 API 进行身份验证和授权。
  • Client Scopes 是指用于授权客户端访问 Keycloak 后端服务的权限。client scope 可以定义一组权限,这些权限可以授予客户端,以便它们可以访问 Keycloak 后端服务。例如,可以创建一个名为“read”的 client scope,并将其授予可以读取特定资源的客户端。这样可以确保只有具有足够权限的客户端可以访问系统中的资源。

部署和配置 Keycloak

首先,我们将使用 Helm 快速部署 Keycloak ,最后使用 terraform 配置它。

部署 Keycloak Helm chart

helm repo add bitnami https://charts.bitnami.com/bitnami
helm upgrade --install --namespace keycloak --create-namespace \
  keycloak bitnami/keycloak --version 14.1.0 \
  --reuse-values --values - <<EOF
auth:
  createAdminUser: true
  adminUser: admin
  adminPassword: admin
  managementUser: manager
  managementPassword: manager
tls:
  enabled: true
  autoGenerated: true
service:
  type: NodePort
EOF

在 values 中,我们定义了自动创建管理用户和账户密码,并配置了 autoGenerated 自动生成私有证书的功能。

我们将自动生成的 CA 证书保存下来,以便在未来的章节中用于关键数据的安全传输和验证。

kubectl exec -n keycloak keycloak-0 \
-- cat /opt/bitnami/keycloak/certs/ca.crt > /tmp/keycloakca.crt

使用 terraform 配置 Keycloak

我们将使用 terraform[2] 在 Keycloak 中创建 clients、用户和组,以便更好地了解 Keycloak 的字段信息。通过使用 API,我们可以更直观地了解 Keycloak 的配置细节。

cat <<'EOF' > keycloak.tf
terraform {
  required_providers {
    keycloak = {
      source  = "mrparkers/keycloak"
      version = "4.2.0"
    }
  }
}
# configure keycloak provider
provider "keycloak" {
  client_id                = "admin-cli"
  username                 = "admin"
  password                 = "admin"
  url                      = "https://keycloak.keycloak.svc.cluster.local:20443"
  tls_insecure_skip_verify = true
}



locals {
  groups   = ["harbor-dev", "harbor-admin"]
  user_groups = {
    user-dev   = ["harbor-dev"]
    user-admin = ["harbor-admin"]
  }
}

resource "keycloak_realm" "realm" {
  realm   = "harbor"
  enabled = true
}

#
 create groups
resource "keycloak_group" "groups" {
  realm_id = keycloak_realm.realm.id
  for_each = toset(local.groups)
  name     = each.key
}

#
 create users
resource "keycloak_user" "users" {
  for_each       = local.user_groups
  realm_id       = keycloak_realm.realm.id
  username       = each.key
  enabled        = true
  email          = "${each.key}@domain.com"
  email_verified = true
  first_name     = each.key
  last_name      = each.key
  initial_password {
    value = each.key
  }
}
# configure use groups membership
resource "keycloak_user_groups" "user_groups" {
  for_each  = local.user_groups
  realm_id  = keycloak_realm.realm.id
  user_id   = keycloak_user.users[each.key].id
  group_ids = [for g in each.value : keycloak_group.groups[g].id]
}


#
 create groups openid client scope
resource "keycloak_openid_client_scope" "groups" {
  realm_id               = keycloak_realm.realm.id
  name                   = "groups"
  include_in_token_scope = true
  gui_order              = 1
}
resource "keycloak_openid_group_membership_protocol_mapper" "groups" {
  realm_id        = keycloak_realm.realm.id
  client_scope_id = keycloak_openid_client_scope.groups.id
  name            = "groups"
  claim_name      = "groups"
  full_path       = false
}
# create harbor openid client
resource "keycloak_openid_client" "harbor" {
  realm_id                     = keycloak_realm.realm.id
  client_id                    = "harbor"  #定义的client id
  name                         = "harbor"
  enabled                      = true
  access_type                  = "CONFIDENTIAL"
  client_secret                = "harbor-client-secret"  #此id的密码
  standard_flow_enabled        = true
  implicit_flow_enabled        = false
  login_theme                  = "keycloak"
  direct_access_grants_enabled = false
  oauth2_device_authorization_grant_enabled = true
  valid_redirect_uris = [
    "http://harbor.10086cloud.com/c/oidc/callback"
  ]
  valid_post_logout_redirect_uris = [
    "http://harbor.10086cloud.com"
  ]

  web_origins = [
    "http://harbor.10086cloud.com"
  ]
  admin_url   = "http://harbor.10086cloud.com"
  root_url    = "http://harbor.10086cloud.com"
  base_url    = "http://harbor.10086cloud.com"
}

#
 configure harbor openid client default scopes
resource "keycloak_openid_client_default_scopes" "harbor" {
  realm_id  = keycloak_realm.realm.id
  client_id = keycloak_openid_client.harbor.id
  default_scopes = [
    "profile",
    "email",
    "roles",
    keycloak_openid_client_scope.groups.name,
  ]
}
EOF

terraform init && terraform apply -auto-approve

运行上述脚本将创建一个 harbor realm、harbor clients、两个组(harbor-dev、harbor-admin)和两个用户:

  • user-dev 是 harbor-dev 组的成员
  • user-admin 是 harbor-admin 组的成员

在 harbor 中配置 Keycloak

Harbor OIDC 认证流程

Harbor 可以与支持 OIDC 的 OAuth 服务提供商集成来进行用户认证,并通过授权码方式获取令牌


(1)用户通过浏览器访问 Harbor 的登录页面,并单击“通过 OIDC 提供商登录”按钮,该按钮在 Harbor 使用 OIDC 认证时才会显示。

(2)用户被重定向到 OIDC 提供商的身份验证页面。(本文为公众号亨利笔记原创文章)

(3)在用户经过身份验证后,OIDC 提供商将使用授权代码重定向至 Harbor。

(4)Harbor 将与 OIDC 提供商交换此授权代码以获得访问令牌。

(5)Harbor 使用访问令牌请求 UserInfo 接口获取用户信息。

(6)Harbor 在系统中创建或更新用户账户并将用户重定向到 Harbor 的门户首页

快速部署 Harbor 演示集群

首先使用 Kubernetes 和 Helm,快速轻松地搭建一个 Harbor 集群。

helm repo add harbor https://helm.goharbor.io

helm upgrade --install --namespace harbor --create-namespace \
  harbor harbor/harbor --version 1.12.0 \
  --reuse-values --values - <<EOF
expose:
  type: nodePort
  tls:
    enabled: false
  nodePort:
    ports:
      http:
        nodePort: 80
externalURL: http://harbor.10086cloud.com
EOF

配置 OIDC 提供身份验证[3]

如果您选择 OpenID Connect (OIDC) 认证,用户将通过 OIDC 单点登录 (SSO) 提供商 KeyCloak 登录到 Harbor 界面。在这种情况下,你不能在 Harbor 中创建用户账户。

开始配置 harbor oidc

1、用管理用户 admin 和默认管理员密码 Harbor12345 登录到 Keycloak 控制台。

2、系统管理——>配置管理——>安全认证——> 认证模式下拉改为 OIDC

3、输入您的 KeyCloak 配置信息。

  • OIDC Provider Name:OIDC 提供程序的名称
  • OIDC Provider Endpoint:OIDC 提供程序的 URL 连接地址
  • OIDC Client ID:harbor 向 OIDC 提供程序注册的客户端 ID
  • OIDC Client Secret:客户端 ID 的密钥
  • OIDC Scope:一个逗号分割的字符串,列出身份验证期间要使用的作用域

通过 OIDC 验证登录

作为 Harbor 用户,请单击通过 OIDC 提供商登录按钮。

这会将您重定向到 OIDC 提供程序进行身份验证。输入提前在 Keycloak 中创建的用户:user-dev 密码:user-dev

如果这是您第一次使用 OIDC 登录 Harbor,请指定 Harbor 与您的 OIDC 用户名关联的用户名。

也可以在最开始的时候配置 ”自动登录“(Automatic onboarding), 跳过创建 Harbor 用户名

使用 shell 脚本自动配置 harbor oidc

创建自定义 habor2.8_config.json 配置文件

{
  "auth_mode""oidc_auth",
  "oidc_name""Keycloak",
  "oidc_endpoint""https://192.168.123.50:20443/realms/harbor",
  "oidc_client_id""harbor",
  "oidc_client_secret""harbor-client-secret",
  "oidc_scope""openid,profile,email",
  "oidc_groups_claim""groups",
  "oidc_auto_onboard"true,
  "oidc_user_claim""preferred_username"
}

curl -X PUT -u "admin:Harbor12345" \
  -H "Content-Type: application/json" -ki 192.168.123.50/api/v2.0/configurations \
  -d @habor2.8_config.json

从 docker 或 Helm CLI 使用 OIDC

Docker 和 Helm CLI 由于无法处理 OIDC 的重定向,因此 Harbor 提供了一个 CLI 密钥,以便在从 Docker 或 Helm 登录时使用。这仅在 Harbor 使用 OIDC 身份验证时可用。

1、使用 OIDC 用户帐户登录 Harbor。

2、点击屏幕右上角顶部的用户名,选择用用户设置

3、点击 CLI 密码可复制,... 图标以显示用于自动生成或手动创建新 CLI 密钥的按钮。一个用户只能有一个 CLI 密钥,因此当生成或创建新密钥时,旧密钥将变为无效。

4、使用 docker 或 Helm CLI 登录 Harbor 时使用您的 CLI 密钥作为密码。

docker login -u **** -p *****  http://harbor.10086cloud.com

参考资料

[1]

Keycloak: https://www.keycloak.org/documentation

[2]

terraform: https://developer.hashicorp.com/terraform/downloads

[3]

配置 OIDC 提供身份验证: https://goharbor.io/docs/1.10/administration/configure-authentication/oidc-auth/


文章转载自k8s技术圈,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论