一、keycloak概述
Keycloak 是一款开源的身份和访问管理(IAM)工具,支持多种协议,包括 OIDC(OpenID Connect)、OAuth 2.0、SAML 等。它简化了身份认证与授权流程,提供了集中管理用户、角色、权限等功能,常被用作企业或微服务体系下的统一认证中心。
二、Keycloak中的相关概念
-
keycloak
实现身份与访问的一整套解决方案。具体而言,它是搭建在某台服务器上的一个服务实例,可以通过管理控制台进行管理和配置。 -
realm 领域
领域是keycloak中专门用来管理项目的工作区,每个Keycloak实例可以包含一个或多个领域,每个领域是一个独立的安全域,不同领域之间的资源相互隔离。领域分为两类,一类是master领域,由Keycloak刚启动时创建,用于管理admin账号以及创建其他的领域;另一类是用户自己创建的普通领域,用于管理具体的项目和用户。 -
client 客户端
客户端在领域中创建,一个领域可以创建多个客户端,例如一个企业可以创建两个客户端:销售人员客户端、研发人员客户端。在具体的客户端中管理具体的应用程序。 -
application 应用程序
一个客户端可以配置一至多个应用程序,同一客户端下的多个应用程序可以实现单点登录。一个应用程序可以是SpringBoot应用、JavaScript应用、Android应用等。 -
adapter 适配器
适配器,用来实现客户端与Keycloak之间的集成。具体而言,同一客户端下的应用共用一个客户端适配器的配置,这也确保了单点登录的实现。
三、在k8s集群上部署keycloak
3.1 部署keycloak
1)下载官方提供的yaml文件
wget https://raw.githubusercontent.com/keycloak/keycloak-quickstarts/refs/heads/main/kubernetes/keycloak.yaml
2)yaml文件清单如下
root@k8s-master01:~/keycloak# cat keycloak.yaml
apiVersion: v1
kind: Service
metadata:
name: keycloak
labels:
app: keycloak
spec:
ports:
- protocol: TCP
port: 80
targetPort: http
name: http
selector:
app: keycloak
type: LoadBalancer
---
apiVersion: v1
kind: Service
metadata:
labels:
app: keycloak
# Used to
name: keycloak-discovery
spec:
selector:
app: keycloak
# Allow not-yet-ready Pods to be visible to ensure the forming of a cluster if Pods come up concurrently
publishNotReadyAddresses: true
clusterIP: None
type: ClusterIP
---
apiVersion: apps/v1
# Use a stateful setup to ensure that for a rolling update Pods are restarted with a rolling strategy one-by-one.
# This prevents losing in-memory information stored redundantly in two Pods.
kind: StatefulSet
metadata:
name: keycloak
labels:
app: keycloak
spec:
serviceName: keycloak-discovery
# Run with one replica to save resources, or with two replicas to allow for rolling updates for configuration changes
replicas: 1
selector:
matchLabels:
app: keycloak
template:
metadata:
labels:
app: keycloak
spec:
containers:
- name: keycloak
image: quay.io/keycloak/keycloak:26.3.2
args: ["start"]
env:
- name: KC_BOOTSTRAP_ADMIN_USERNAME
value: "admin"
- name: KC_BOOTSTRAP_ADMIN_PASSWORD
value: "admin"
# In a production environment, add a TLS certificate to Keycloak to either end-to-end encrypt the traffic between
# the client or Keycloak, or to encrypt the traffic between your proxy and Keycloak.
# Respect the proxy headers forwarded by the reverse proxy
# In a production environment, verify which proxy type you are using, and restrict access to Keycloak
# from other sources than your proxy if you continue to use proxy headers.
- name: KC_PROXY_HEADERS
value: "xforwarded"
- name: KC_HTTP_ENABLED
value: "true"
# In this explorative setup, no strict hostname is set.
# For production environments, set a hostname for a secure setup.
- name: KC_HOSTNAME_STRICT
value: "false"
- name: KC_HEALTH_ENABLED
value: "true"
- name: 'KC_CACHE'
value: 'ispn'
# Use the Kubernetes configuration for distributed caches which is based on DNS
- name: 'KC_CACHE_STACK'
value: 'kubernetes'
# Passing the Pod's IP primary address to the JGroups clustering as this is required in IPv6 only setups
- name: POD_IP
valueFrom:
fieldRef:
fieldPath: status.podIP
# Instruct JGroups which DNS hostname to use to discover other Keycloak nodes
# Needs to be unique for each Keycloak cluster
- name: JAVA_OPTS_APPEND
value: '-Djgroups.dns.query="keycloak-discovery" -Djgroups.bind.address=$(POD_IP)'
- name: 'KC_DB_URL_DATABASE'
value: 'keycloak'
- name: 'KC_DB_URL_HOST'
value: 'postgres'
- name: 'KC_DB'
value: 'postgres'
# In a production environment, use a secret to store username and password to the database
- name: 'KC_DB_PASSWORD'
value: 'keycloak'
- name: 'KC_DB_USERNAME'
value: 'keycloak'
ports:
- name: http
containerPort: 8080
startupProbe:
httpGet:
path: /health/started
port: 9000
periodSeconds: 1
failureThreshold: 600
readinessProbe:
httpGet:
path: /health/ready
port: 9000
periodSeconds: 10
failureThreshold: 3
livenessProbe:
httpGet:
path: /health/live
port: 9000
periodSeconds: 10
failureThreshold: 3
resources:
limits:
cpu: 2000m
memory: 2000Mi
requests:
cpu: 500m
memory: 1700Mi
---
# This is deployment of PostgreSQL with an ephemeral storage for testing: Once the Pod stops, the data is lost.
# For a production setup, replace it with a database setup that persists your data.
apiVersion: apps/v1
kind: Deployment
metadata:
name: postgres
labels:
app: postgres
spec:
replicas: 1
selector:
matchLabels:
app: postgres
template:
metadata:
labels:
app: postgres
spec:
containers:
- name: postgres
image: mirror.gcr.io/postgres:17
env:
- name: POSTGRES_USER
value: "keycloak"
- name: POSTGRES_PASSWORD
value: "keycloak"
- name: POSTGRES_DB
value: "keycloak"
- name: POSTGRES_LOG_STATEMENT
value: "all"
ports:
- name: postgres
containerPort: 5432
volumeMounts:
# Using volume mount for PostgreSQL's data folder as it is otherwise not writable
- name: postgres-data
mountPath: /var/lib/postgresql
volumes:
- name: postgres-data
emptyDir: {}
---
apiVersion: v1
kind: Service
metadata:
labels:
app: postgres
name: postgres
spec:
selector:
app: postgres
ports:
- protocol: TCP
port: 5432
targetPort: 5432
type: ClusterIP
3)部署keycloak
kubectl apply -f ingress.yaml --namespace keycloak
4)查看部署详情
root@k8s-master01:~/keycloak# kubectl get pod -n keycloak
NAME READY STATUS RESTARTS AGE
keycloak-0 1/1 Running 0 5h55m
postgres-d4d4f5d96-5qhmh 1/1 Running 0 5h55m
3.2 配置域名和证书
编写ingress的清单文件如下
root@k8s-master01:~/keycloak# cat ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: keycloak-ingress
namespace: keycloak
annotations:
nginx.ingress.kubernetes.io/backend-protocol: "HTTP"
nginx.ingress.kubernetes.io/ssl-redirect: "true" # 强制将 HTTP 请求重定向到 HTTPS。
cert-manager.io/cluster-issuer: my-ca-issuer # 配置cert-manager自动申请证书
spec:
ingressClassName: nginx
tls:
- hosts:
- keycloak.xug.com
secretName: keycloak-tls-secret
rules:
- host: keycloak.xug.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: keycloak
port:
number: 80
PS: 关于cert-manager配置可以参考博主的另一篇文章:cert-manager 是什么?一篇文章搞懂 Kubernetes
证书自动化管理
创建ingress
root@k8s-master01:~/keycloak# kubectl apply -f ingress.yaml
等待证书生成后,就可以通过 keycloak.xug.com 访问 keycloak 了。
root@k8s-master01:~/keycloak# kubectl get certificate -n keycloak
NAME READY SECRET AGE
keycloak-tls-secret True keycloak-tls-secret 4h
root@k8s-master01:~/keycloak# kubectl get secret -n keycloak
NAME TYPE DATA AGE
keycloak-tls-secret kubernetes.io/tls 3 4h
访问 keycloak.xug.com 账号密码是 admin/admin
四、配置 Keycloak 作为 Harbor 的身份认证和授权服务
2.1 创建Realm
创建一个新的 Realm,命名为sso
2.2 添加用户
在 sso Realm 中创建测试用户。
给用户设置密码
2.3 创建client
这里写上Harbor的回调地址
五、Harbor对接keycloak
登录Harbor -> 导航到系统管理 -> 配置管理 -> 认证管理
填写上keycloak的认证信息 oidc endpoint、client id、client secret以及 oidc scope
退出登录,再重新登录Harbor,可以发现Harbor登录界面就多出了通过OIDC提供商登录
输入我们在keycloak sso realms中创建的用户和密码
如果我们是第一次通过OIDC登录Harbor,需要创建一个Harbor用户和keycloak用户进行绑定
Harbor 与 Keycloak 的用户绑定,本质是通过 OIDC 协议传递的 sub
唯一标识,在 Harbor 内部建立 “外部用户 ID(Keycloak sub
)- 内部用户 ID(Harbor user_id
)” 的映射关系。这一机制解决了两套独立用户体系的协同问题,既利用了 Keycloak 强大的身份认证能力,又保留了 Harbor 对用户权限的精细化管理,是企业级身份集成的核心技术支撑。
六、总结
本文介绍了如何在 Kubernetes 中部署 Keycloak,并将其作为统一登录系统与 Harbor 集成。
通过简单的配置,我们实现了使用 Keycloak 账号登录 Harbor 的功能,提升了账号管理和访问安全。Keycloak 支持多种协议,易于扩展,非常适合在云原生环境中作为统一的身份认证中心。
部署完成后,用户只需记住一套账号密码,即可安全访问多个系统,真正实现了单点登录(SSO)的便利。