Мы в нашей организации, как и многие, переходим на отечественные продукты, коснулось это и среды контейнеризации. За годы эксплуатации мы нежно полюбили OKD (Openshift) и очень расстраивались в ванильном kubernetes, подмечая отсутствие ставших уже привычными вещей. Однако OKD состоит из свободно распространяемых компонентов, а значит что-то да можно переиспользовать, например, web-консоль. Ее то мы и решили перенести в насколько это возможной полноте функционала. Существующие гайды обычно покрывают лишь установку самой консоли, нам же хотелось использовать и SSO и дополнительные элементы консоли - каталог ссылок и объявления в заголовке.
Итак, нам потребуется:
Сертификаты для обслуживания web-console
OIDC провайдер. В нашем случае Keycloak
Кластер kubernetes
Образ openshift-console из quay.io
Репозитарий с CRD для web-консоли
cli от openshift (опционально, но команды cli придется адаптировать самостоятельно)
1. Настраиваем клиент в OIDC провайдере
Создаем клиент keycloak. Все изображения с примерами настройки спрячу под спойлер.
client ID:
kubernetes
root url:
<url консоли>
valid redirect urls:
/*
Client authentication:
on
Сохраняем клиент
-
Внутри клиента, во вкладке client scopes добавляем audience
переходим в scope kubernetes-dedicated (либо <cliendID>-dedicated если ID другой)
Жмем
Configure a new mapper
Выбираем тип:
Audience
прописываем имя и выбираем included client audience:
kubernetes
(cliendID)Add to ID token:
on
Остальные параметры оставляем по умолчанию, жмем save
-
В глобальной вкладке client scopes (слева) добавляем Groups:
name: groups
Type: Default
-
Переходим во вкладку Mappers, жмем Configure a new mapper
выбираем Group Membership
name: groups
Token Claim Name: groups
Full group path: off
Сохраняем изменения
Скрытый текст
Важно: Это рабочий конфиг, однако после необходимо захарденить клиент согласно политикам безопасности в вашей организации.
Сохраняем конфиг, сохраняем себе Client ID
и Client Secret
(вкладка Credentials). Также нам потребуется Issuer, взять его можно из раздела realm settings -> OpenID Endpoint Configuration.
Пример настройки:
2. Настройка kubernetes
Для того, чтобы консоль могла выполнять действия от имени пользователя, необходимо чтобы кластер kuebrentes мог аутентифицировать запросы с использование вашего jwt-токена. Наш кластер разворачивался через kubeadm, для других инсталляций локации конфигов могут отличаться
На ВМ с kubernetes apiserver необходимо добавить сертификат, который используется в keycloak по пути:
/etc/kubernetes/pki/oidc-ca.crt
Добавляем конфиг oidc в kubernetes apiserver:
/etc/kubernetes/manifests/kube-apiserver.yaml
...
spec:
containers:
- command:
- kube-apiserver
...
- --oidc-issuer-url=https://auth.keycloak.myinfra.zone/auth/realms/myrealm
- --oidc-client-id=kubernetes
- --oidc-username-claim=preferred_username
- --oidc-groups-claim=groups
- --oidc-ca-file=/etc/kubernetes/pki/oidc-ca.crt
- --oidc-username-prefix=-
...
oidc-issuer-url - урл из OpenID Endpoint Configuration
oidc-client-id - client id
oidc-username-claim - атрибут из jwt по которому определяется логин пользователя
oidc-groups-claim - claim, в котором содержится список групп
oidc-ca-file - путь до файла с сертификатом keycloak
oidc-username-prefix - префикс, который добавляется к логину пользователя внутри kubernetes (например, для rolebinding)
После перезапуска подов должны подтянуться новые настройки, проверить доступ можно сгенерировав access токен и обратившись с ним в kube-api
Скрытый текст
curl -k -L -X POST 'https://auth.keycloak.myinfra.zone/auth/realms/myrealm/protocol/openid-connect/token' \
-H 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'client_id=kubernetes' \
--data-urlencode 'grant_type=password' \
--data-urlencode 'client_secret=<KUBERNETES_CLIENT_ID_TOKEN>' \
--data-urlencode 'scope=openid' \
--data-urlencode 'username=<KEYCLOAK_USER>' \
--data-urlencode 'password=<KEYCLOAK_USER_PASSWORD>'
oc login --token=ACCESS_TOKEN_HERE --server=https://apiserver_url:6443
3. Настройка openshift-console в кластере
Нам остается только грамотно настроить манифесты:
Скрытый текст
Вспомогательные манифесты, namespaces, serviceaccouns, clusterrolebindings:
---
kind: Namespace
apiVersion: v1
metadata:
name: openshift-console
---
kind: Namespace
apiVersion: v1
metadata:
name: openshift-console-user-settings
---
kind: ServiceAccount
apiVersion: v1
metadata:
name: console
namespace: openshift-console
---
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: okd-console-role
subjects:
- kind: ServiceAccount
name: console
namespace: openshift-console
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: cluster-admin
---
Сертификаты для https консоли и ca для доверия к keycloak:
kind: Secret
apiVersion: v1
metadata:
name: console-serving-cert
namespace: openshift-console
data:
ca.crt: >-
ca_cert_base_64
tls.crt: >-
public_cert_base_64
tls.key: >-
private_cert_base_64
type: kubernetes.io/tls
Deployment:
Скрытый текст
---
kind: Deployment
apiVersion: apps/v1
metadata:
name: console
namespace: openshift-console
labels:
app: console
component: ui
spec:
replicas: 1
selector:
matchLabels:
app: console
component: ui
template:
metadata:
name: console
creationTimestamp: null
labels:
app: console
component: ui
spec:
nodeSelector:
node-role.kubernetes.io/control-plane: ''
restartPolicy: Always
serviceAccountName: console
schedulerName: default-scheduler
affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: component
operator: In
values:
- ui
topologyKey: kubernetes.io/hostname
terminationGracePeriodSeconds: 30
securityContext: {}
containers:
- name: console
image: quay.io/openshift/origin-console:4.12.0
command:
- /opt/bridge/bin/bridge
- '--public-dir=/opt/bridge/static'
- '--control-plane-topology-mode=HighlyAvailable'
- '--k8s-public-endpoint=https://kubernetes_apserver:6443'
- '--listen=http://[::]:8080'
- '--k8s-auth=oidc'
- '--k8s-mode=in-cluster'
- '--tls-cert-file=/var/serving-cert/tls.crt'
- '--tls-key-file=/var/serving-cert/tls.key'
- '--base-address=https://console.apps.myinfra.zone'
- '--user-auth=oidc'
- '--user-auth-oidc-ca-file=/var/serving-cert/ca.crt'
- '--user-auth-oidc-client-id=kubernetes' # the same as for kubernetes apiserver client
- '--user-auth-oidc-client-secret=oidc_client_from_keycloak'
- '--user-auth-logout-redirect=https://console.apps.myinfra.zone'
- >-
-user-auth-oidc-issuer-url=https://auth.keycloak.myinfra.zone/auth/realms/myrealm
resources: {}
volumeMounts:
- name: console-serving-cert
readOnly: true
mountPath: /var/serving-cert
terminationMessagePath: /dev/termination-log
terminationMessagePolicy: File
readinessProbe:
httpGet:
path: /health
port: 8080
scheme: HTTP
timeoutSeconds: 1
periodSeconds: 10
successThreshold: 1
failureThreshold: 3
livenessProbe:
httpGet:
path: /health
port: 8080
scheme: HTTP
initialDelaySeconds: 150
timeoutSeconds: 1
periodSeconds: 10
successThreshold: 1
failureThreshold: 3
ports:
- name: https
containerPort: 8443
protocol: TCP
- name: http
containerPort: 8080
protocol: TCP
imagePullPolicy: IfNotPresent
serviceAccount: console
volumes:
- name: console-serving-cert
secret:
secretName: console-serving-cert
defaultMode: 420
dnsPolicy: ClusterFirst
strategy:
type: RollingUpdate
rollingUpdate:
maxUnavailable: 1
maxSurge: 3
revisionHistoryLimit: 10
progressDeadlineSeconds: 600
Сервис и ingress:
---
kind: Service
apiVersion: v1
metadata:
name: console
namespace: openshift-console
spec:
ports:
- name: https
protocol: TCP
port: 443
targetPort: 8443
- name: http
protocol: TCP
port: 80
targetPort: 8080
internalTrafficPolicy: Cluster
type: ClusterIP
selector:
app: console
component: ui
---
kind: Ingress
apiVersion: networking.k8s.io/v1
metadata:
name: okd-console
namespace: openshift-console
annotations:
nginx.ingress.kubernetes.io/affinity: cookie
nginx.ingress.kubernetes.io/force-ssl-redirect: 'true'
nginx.ingress.kubernetes.io/ssl-passthrough: 'false'
spec:
ingressClassName: nginx
tls:
- secretName: console-serving-cert
rules:
- host: console.apps.myinfra.zone
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: console
port:
number: 80
В зависимости от ваших требований можно включить ssl-passthrough, не забыв при этом поменять listen port в приложении и поправить раздел rules у ingress, однако я столкнулся с проблемой, что при такой конфигурации не работает > 1 пода - трафик round-robin отправляется на поды и возможны потери сессии.
На этом этапе у вас должна запуститься веб-консоль, при входе должно произойти перенаправление в SSO. После входа запросы будут выполняться от аутентифицированного пользователя.
4. Включаем console links, ConsoleNotification, ConsoleCLIDownload
Все что нам надо, это взять из репозитария манифесты CRD и применить их в кластере:
Скрытый текст
oc apply -f https://raw.githubusercontent.com/openshift/api/refs/heads/release-4.12/console/v1/0000_10_consolelink.crd.yaml
oc apply -f https://raw.githubusercontent.com/openshift/api/refs/heads/release-4.12/console/v1/0000_10_consoleclidownload.crd.yaml
oc apply -f https://raw.githubusercontent.com/openshift/api/refs/heads/release-4.12/console/v1/0000_10_consolenotification.crd.yaml
При желании можно применить и другие CRD console<type> из репозитория. Консоль подхватит и их.
После чего можно создавать соответствующие объекты (API Explorer -> group: console.openshift.io) и наблюдать эффект:
Вот и все, вы запустили web-консоль OKD с авторизацией, оповещениями и настраиваемыми ссылками!