This shows you the differences between two versions of the page.
| Both sides previous revision Previous revision Next revision | Previous revision | ||
|
управление_доступом_в_kubernetes [2025/12/13 16:19] val |
управление_доступом_в_kubernetes [2026/01/13 09:39] (current) val [Шаг 7. Использование OpenID Connect] |
||
|---|---|---|---|
| Line 15: | Line 15: | ||
| * [[https://medium.com/@subhampradhan966/implementing-and-verifying-kubernetes-service-accounts-a-step-by-step-guide-c43b727260b2|Implementing and Verifying Kubernetes Service Accounts: A Step-by-Step Guide]] | * [[https://medium.com/@subhampradhan966/implementing-and-verifying-kubernetes-service-accounts-a-step-by-step-guide-c43b727260b2|Implementing and Verifying Kubernetes Service Accounts: A Step-by-Step Guide]] | ||
| - | + | * [[https://medium.com/@amirhosseineidy/kubernetes-authentication-with-keycloak-oidc-63571eaeed61|Kubernetes authentication with keycloak oidc]] | |
| + | * [[https://vlasov.pro/ru/p/kubernetes-oidc/|Kubernetes авторизация через OIDC]] | ||
| + | * [[https://timeweb.cloud/docs/k8s/connect-oidc-provider-to-cluster|Подключение OIDC-провайдера к кластеру]] | ||
| + | * [[https://www.talkingquickly.co.uk/setting-up-oidc-login-kubernetes-kubectl-with-keycloak|OIDC Login to Kubernetes and Kubectl with Keycloak]] | ||
| + | * [[https://github.com/int128/kubelogin|kubelogin - This is a kubectl plugin for Kubernetes OpenID Connect]] | ||
| ===== Реклама ===== | ===== Реклама ===== | ||
| Line 101: | Line 104: | ||
| kube1:~/users# kubectl delete serviceaccounts admin-user | kube1:~/users# kubectl delete serviceaccounts admin-user | ||
| + | |||
| + | user1@client1:~$ rm -rf .kube/ | ||
| </code> | </code> | ||
| ===== Шаг 6. Использование Service Accounts в приложениях ===== | ===== Шаг 6. Использование Service Accounts в приложениях ===== | ||
| Line 145: | Line 150: | ||
| </code> | </code> | ||
| - | ===== Вопросы? ===== | + | ===== Шаг 7. Использование OpenID Connect ===== |
| - | ===== Домашнее задание ===== | + | * Сервис Keycloak [[Сервис Keycloak#Аутентификация пользователей WEB приложения]] |
| - | + | * !!! в конфигурации kube-apiserver параметра client-secret нет и не требуется !!! | |
| - | - Куда и через сколько исчезает "kubectl get csr" после "approve" ? | + | |
| - | + | ||
| - | ===== Перезапуск вебинара ===== | + | |
| <code> | <code> | ||
| - | user1@client1:~$ rm -v user1* | ||
| - | |||
| - | user1@client1:~$ rm -rfv .kube/ | ||
| - | |||
| - | kube1:~# rm -rfv users/ | ||
| - | </code> | ||
| - | |||
| - | ===== Дополнительные материалы ===== | ||
| - | |||
| - | ==== Демонстрация OIDC ==== | ||
| - | |||
| - | <code> | ||
| - | user1@client1:~$ cp -rv .kube.iodc/ .kube/ | ||
| - | </code> | ||
| - | |||
| - | * Открываем браузер | ||
| - | * Подколючемся к giltab через keycloak | ||
| - | <code> | ||
| - | user1@client1:~$ kubelogin | ||
| - | |||
| - | user1@client1:~$ kubectl auth whoami | ||
| - | </code> | ||
| - | |||
| - | * [[#Предоставление полного доступа к Kubernetes Cluster]] | ||
| - | |||
| - | |||
| - | ==== Черновик OIDC ==== | ||
| - | <code> | ||
| - | https://medium.com/@amirhosseineidy/kubernetes-authentication-with-keycloak-oidc-63571eaeed61 | ||
| - | |||
| - | https://vlasov.pro/ru/p/kubernetes-oidc/ | ||
| - | |||
| - | https://github.com/int128/kubelogin | ||
| - | |||
| - | https://timeweb.cloud/docs/k8s/connect-oidc-provider-to-cluster | ||
| - | |||
| - | ? https://www.talkingquickly.co.uk/setting-up-oidc-login-kubernetes-kubectl-with-keycloak | ||
| - | |||
| - | Email verified | ||
| - | |||
| kube1:~/users# vim /etc/kubernetes/manifests/kube-apiserver.yaml | kube1:~/users# vim /etc/kubernetes/manifests/kube-apiserver.yaml | ||
| + | </code><code> | ||
| ... | ... | ||
| spec: | spec: | ||
| Line 199: | Line 162: | ||
| - command: | - command: | ||
| - kube-apiserver | - kube-apiserver | ||
| - | - --oidc-issuer-url=https://keycloak.corp13.un/realms/corp13 | + | - --oidc-issuer-url=https://keycloak.corpX.un/realms/corpX |
| #- --oidc-client-id=account | #- --oidc-client-id=account | ||
| - --oidc-client-id=any-client | - --oidc-client-id=any-client | ||
| Line 206: | Line 169: | ||
| - --oidc-groups-claim=groups | - --oidc-groups-claim=groups | ||
| ... | ... | ||
| + | </code><code> | ||
| + | kube1:~# ps ax | grep kube-apiserver | ||
| - | kube1:~/users# kubectl -n kube-system logs Pod/kube-apiserver-kube1 | + | kube1:~# journalctl -f | grep kube-apiserver |
| + | |||
| + | kube1/2:~# kubectl -n kube-system logs Pod/kube-apiserver-kube1 | ||
| + | ... | ||
| + | Error: unknown flag: --oidc-client-secret | ||
| ... | ... | ||
| E1203 05:22:46.412571 1 authentication.go:73] "Unable to authenticate the request" err="[invalid bearer token, oidc: verify token: oidc: expected audience \"any-client\" got [\"account\"]]" | E1203 05:22:46.412571 1 authentication.go:73] "Unable to authenticate the request" err="[invalid bearer token, oidc: verify token: oidc: expected audience \"any-client\" got [\"account\"]]" | ||
| ... | ... | ||
| + | E1218 10:36:21.105422 1 authentication.go:75] "Unable to authenticate the request" err="[invalid bearer token, oidc: email not verified]" | ||
| + | ... | ||
| + | </code> | ||
| + | |||
| + | * [[Сервис Keycloak#Проверка получения токена]] Keycloak | ||
| + | * [[Система Kubernetes#Создание файла конфигурации kubectl]] c полученным токеном | ||
| + | |||
| + | <code> | ||
| + | client1:~# wget https://github.com/int128/kubelogin/releases/download/v1.35.0/kubelogin_linux_amd64.zip | ||
| + | |||
| + | client1:~# unzip kubelogin_linux_amd64.zip | ||
| + | |||
| + | client1:~# mv kubelogin /usr/local/bin/ | ||
| user1@client1:~$ cat .kube/config | user1@client1:~$ cat .kube/config | ||
| - | apiVersion: v1 | + | </code><code> |
| - | clusters: | + | ... |
| - | - cluster: | + | |
| - | certificate-authority-data: ... | + | |
| - | server: https://192.168.13.221:6443 | + | |
| - | name: cluster.local | + | |
| - | contexts: | + | |
| - | - context: | + | |
| - | cluster: cluster.local | + | |
| - | user: user1 | + | |
| - | name: default-context | + | |
| - | current-context: default-context | + | |
| - | kind: Config | + | |
| - | preferences: {} | + | |
| users: | users: | ||
| - name: user1 | - name: user1 | ||
| Line 239: | Line 209: | ||
| #refresh-token: | #refresh-token: | ||
| name: oidc | name: oidc | ||
| + | </code><code> | ||
| + | user1@client1:~$ kubelogin | ||
| + | user1@client1:~$ kubectl auth whoami | ||
| </code> | </code> | ||
| + | |||
| + | * [[Система Kubernetes#Предоставление полного доступа к Kubernetes Cluster]] | ||
| + | |||
| + | ===== Вопросы? ===== | ||
| + | |||
| + | ===== Домашнее задание ===== | ||
| + | |||
| + | - Куда и через сколько исчезает "kubectl get csr" после "approve" ? | ||
| + | |||
| + | ===== Перезапуск вебинара ===== | ||
| + | <code> | ||
| + | user1@client1:~$ rm -v user1* | ||
| + | |||
| + | user1@client1:~$ rm -rfv .kube/ | ||
| + | |||
| + | kube1:~# rm -rfv users/ | ||
| + | </code> | ||
| + | |||
| + | ===== Дополнительные материалы ===== | ||
| + | |||
| ==== Черновик Auditing ==== | ==== Черновик Auditing ==== | ||
| <code> | <code> | ||
| Line 296: | Line 289: | ||
| ==== Черновик DEX ==== | ==== Черновик DEX ==== | ||
| + | |||
| + | === Описание === | ||
| <code> | <code> | ||
| Создаём группы и пользователей под Kubernetes | Создаём группы и пользователей под Kubernetes | ||
| Line 631: | Line 626: | ||
| Применяем файл test-pod.txt | Применяем файл test-pod.txt | ||
| - | |||
| И тестируем права | И тестируем права | ||
| Line 638: | Line 632: | ||
| Если не пускает польз когда у него права только на один ns то надо дать права на просмотр. | Если не пускает польз когда у него права только на один ns то надо дать права на просмотр. | ||
| + | </code> | ||
| + | |||
| + | === Файлы === | ||
| + | <code> | ||
| + | === role.txt === | ||
| + | apiVersion: rbac.authorization.k8s.io/v1 | ||
| + | kind: ClusterRoleBinding | ||
| + | metadata: | ||
| + | name: k8s-admins | ||
| + | subjects: | ||
| + | - kind: Group | ||
| + | name: k8s-cluster-admins # ИМЯ ГРУППЫ из FreeIPA | ||
| + | apiGroup: rbac.authorization.k8s.io | ||
| + | roleRef: | ||
| + | kind: ClusterRole | ||
| + | name: cluster-admin | ||
| + | apiGroup: rbac.authorization.k8s.io | ||
| + | --- | ||
| + | # 1) Админы НС core-test-admin | ||
| + | apiVersion: rbac.authorization.k8s.io/v1 | ||
| + | kind: RoleBinding | ||
| + | metadata: | ||
| + | name: core-test-admin-admins | ||
| + | namespace: core-test-admin | ||
| + | subjects: | ||
| + | - kind: Group | ||
| + | name: k8s-ns-core-test-admin # ГРУППА из токена Dex / FreeIPA | ||
| + | apiGroup: rbac.authorization.k8s.io | ||
| + | roleRef: | ||
| + | kind: ClusterRole | ||
| + | name: admin # встроенная роль с полным доступом в ns | ||
| + | apiGroup: rbac.authorization.k8s.io | ||
| + | --- | ||
| + | # 2) Вьюеры НС core-test-view | ||
| + | apiVersion: rbac.authorization.k8s.io/v1 | ||
| + | kind: RoleBinding | ||
| + | metadata: | ||
| + | name: core-test-view-viewers | ||
| + | namespace: core-test-view | ||
| + | subjects: | ||
| + | - kind: Group | ||
| + | name: k8s-ns-core-test-view # ГРУППА для read-only в этом ns | ||
| + | apiGroup: rbac.authorization.k8s.io | ||
| + | roleRef: | ||
| + | kind: ClusterRole | ||
| + | name: view # встроенная read-only роль для ns | ||
| + | apiGroup: rbac.authorization.k8s.io | ||
| + | --- | ||
| + | # 3) Read-only по всему кластеру для группы k8s-ns | ||
| + | apiVersion: rbac.authorization.k8s.io/v1 | ||
| + | kind: ClusterRoleBinding | ||
| + | metadata: | ||
| + | name: k8s-ns-read-all | ||
| + | subjects: | ||
| + | - kind: Group | ||
| + | name: k8s-ns # ГРУППА "общие зрители k8s" | ||
| + | apiGroup: rbac.authorization.k8s.io | ||
| + | roleRef: | ||
| + | kind: ClusterRole | ||
| + | name: view # cluster-wide view | ||
| + | apiGroup: rbac.authorization.k8s.io | ||
| + | |||
| + | |||
| + | kubectl apply -f k8s-admins-rbac.yaml | ||
| + | kubectl get clusterrolebinding k8s-admins -o yaml | ||
| + | |||
| + | === issuer.txt === | ||
| + | apiVersion: cert-manager.io/v1 | ||
| + | kind: ClusterIssuer | ||
| + | metadata: | ||
| + | name: freeipa-acme | ||
| + | spec: | ||
| + | acme: | ||
| + | email: admin@teach.local | ||
| + | server: https://ipa-server-1.teach.local/acme/directory | ||
| + | privateKeySecretRef: | ||
| + | name: freeipa-acme-account-key | ||
| + | |||
| + | # ВАЖНО: одна длинная строка из ipa-ca.b64 | ||
| + | caBundle: ПОДСТАВЬ_ЗДЕСЬ_СТРОКУ_ИЗ_ipa-ca.b64 | ||
| + | |||
| + | solvers: | ||
| + | - http01: | ||
| + | ingress: | ||
| + | ingressClassName: nginx | ||
| + | |||
| + | === dex-auth-values.txt === | ||
| + | global: | ||
| + | deployEnv: dev | ||
| + | |||
| + | image: | ||
| + | repository: mintel/dex-k8s-authenticator | ||
| + | tag: 1.4.0 | ||
| + | pullPolicy: Always | ||
| + | |||
| + | dexK8sAuthenticator: | ||
| + | port: 5555 | ||
| + | debug: true | ||
| + | web_path_prefix: / | ||
| + | clusters: | ||
| + | - name: teach-cluster | ||
| + | short_description: "Учебный кластер" | ||
| + | description: "Kubespray кластер teach.local" | ||
| + | |||
| + | # ДОЛЖНО совпадать с issuer у Dex и --oidc-issuer-url у kube-apiserver | ||
| + | issuer: https://dex.teach.local/dex | ||
| + | |||
| + | # Адрес API-сервера кластера | ||
| + | k8s_master_uri: https://192.168.100.230:6443 | ||
| + | |||
| + | # Те же client_id / secret, что в staticClients Dex | ||
| + | client_id: dex-k8s-authenticator | ||
| + | client_secret: 9f3b2e3a7a8d14d0a5ff4b1b8e92c3de0b6b7f21a4e59bd8c1f3d2e7c8a9b0c1 | ||
| + | |||
| + | # ДОЛЖНО 1-в-1 совпадать с redirectURIs в Dex для этого клиента | ||
| + | | ||
| + | redirect_uri: https://auth.teach.local/callback | ||
| + | |||
| + | k8s_ca_pem: | | ||
| + | -----BEGIN CERTIFICATE----- | ||
| + | ТУТ ЦЕЛИКОМ СОДЕРЖИМОЕ /etc/kubernetes/ssl/ca.crt | ||
| + | БЕЗ base64, как есть | ||
| + | -----END CERTIFICATE----- | ||
| + | |||
| + | service: | ||
| + | type: ClusterIP | ||
| + | port: 5555 | ||
| + | |||
| + | ingress: | ||
| + | enabled: true | ||
| + | ingressClassName: nginx | ||
| + | annotations: {} | ||
| + | labels: {} | ||
| + | |||
| + | # В ЭТОМ чарте path один, для всех хостов: | ||
| + | path: / | ||
| + | |||
| + | # hosts — это СПИСОК СТРОК, а не объектов: | ||
| + | hosts: | ||
| + | - auth.teach.local | ||
| + | |||
| + | tls: | ||
| + | - secretName: dex-auth-tls | ||
| + | hosts: | ||
| + | - auth.teach.local | ||
| + | |||
| + | # Чтобы убрать x509: unknown authority для Dex (FreeIPA CA) | ||
| + | caCerts: | ||
| + | enabled: true | ||
| + | secrets: | ||
| + | - name: ipa-ca | ||
| + | filename: ipa-ca.crt | ||
| + | value: |- | ||
| + | ТУТ_ОДНА_СТРОКА_BASE64_ОТ /etc/ipa/ca.crt | ||
| + | |||
| + | === config.txt === | ||
| + | replicaCount: 1 | ||
| + | |||
| + | image: | ||
| + | repository: ghcr.io/dexidp/dex | ||
| + | tag: v2.44.0 | ||
| + | pullPolicy: IfNotPresent | ||
| + | |||
| + | service: | ||
| + | type: ClusterIP | ||
| + | ports: | ||
| + | http: | ||
| + | port: 5556 | ||
| + | |||
| + | https: | ||
| + | enabled: false | ||
| + | |||
| + | grpc: | ||
| + | enabled: false | ||
| + | |||
| + | rbac: | ||
| + | create: true | ||
| + | |||
| + | podDisruptionBudget: | ||
| + | enabled: false | ||
| + | |||
| + | networkPolicy: | ||
| + | enabled: false | ||
| + | |||
| + | autoscaling: | ||
| + | enabled: false | ||
| + | |||
| + | ingress: | ||
| + | enabled: true | ||
| + | className: nginx | ||
| + | hosts: | ||
| + | - host: dex.teach.local | ||
| + | paths: | ||
| + | - path: /dex/ | ||
| + | pathType: ImplementationSpecific | ||
| + | tls: | ||
| + | - secretName: dex-tls # СЕКРЕТ, который делает cert-manager/FreeIPA | ||
| + | hosts: | ||
| + | - dex.teach.local | ||
| + | |||
| + | configSecret: | ||
| + | create: true | ||
| + | |||
| + | serviceMonitor: | ||
| + | enabled: false | ||
| + | |||
| + | serviceAccount: | ||
| + | create: true # или false, если не нужен отдельный SA | ||
| + | name: "" # пусто = helm сам сгенерит имя по release | ||
| + | annotations: {} | ||
| + | |||
| + | config: | ||
| + | issuer: https://dex.teach.local/dex | ||
| + | |||
| + | storage: | ||
| + | type: kubernetes | ||
| + | config: | ||
| + | inCluster: true | ||
| + | type: memory | ||
| + | |||
| + | web: | ||
| + | http: 0.0.0.0:5556 | ||
| + | |||
| + | logger: | ||
| + | level: debug | ||
| + | format: text | ||
| + | |||
| + | oauth2: | ||
| + | skipApprovalScreen: true | ||
| + | |||
| + | staticClients: | ||
| + | - id: example-app | ||
| + | name: Example App | ||
| + | secret: example-app-secret | ||
| + | redirectURIs: | ||
| + | - http://127.0.0.1:5555/callback | ||
| + | - id: dex-k8s-authenticator | ||
| + | name: Dex K8s Authenticator | ||
| + | secret: SUPER-SECRET-STRING | ||
| + | redirectURIs: | ||
| + | - https://auth.teach.local/callback | ||
| + | |||
| + | |||
| + | connectors: | ||
| + | - type: ldap | ||
| + | id: freeipa | ||
| + | name: FreeIPA | ||
| + | config: | ||
| + | host: 192.168.100.252:389 | ||
| + | insecureNoSSL: true | ||
| + | startTLS: false | ||
| + | |||
| + | bindDN: "uid=dex-bind,cn=users,cn=accounts,dc=teach,dc=local" | ||
| + | bindPW: "123456" | ||
| + | |||
| + | userSearch: | ||
| + | baseDN: "cn=users,cn=accounts,dc=teach,dc=local" | ||
| + | filter: "(objectClass=person)" | ||
| + | emailAttr: "mail" | ||
| + | idAttr: "uid" | ||
| + | nameAttr: "cn" | ||
| + | username: "uid" | ||
| + | |||
| + | groupSearch: | ||
| + | baseDN: "cn=groups,cn=accounts,dc=teach,dc=local" | ||
| + | filter: "(objectClass=groupofnames)" | ||
| + | userAttr: "dn" | ||
| + | groupAttr: "member" | ||
| + | nameAttr: "cn" | ||
| + | |||
| + | |||
| + | === certificate-auth.txt === | ||
| + | apiVersion: cert-manager.io/v1 | ||
| + | kind: Certificate | ||
| + | metadata: | ||
| + | name: dex-auth-cert | ||
| + | namespace: dex-auth | ||
| + | spec: | ||
| + | secretName: dex-auth-tls | ||
| + | dnsNames: | ||
| + | - auth.teach.local | ||
| + | issuerRef: | ||
| + | name: freeipa-acme | ||
| + | kind: ClusterIssuer | ||
| + | |||
| + | === certificate.txt === | ||
| + | apiVersion: cert-manager.io/v1 | ||
| + | kind: Certificate | ||
| + | metadata: | ||
| + | name: dex-cert | ||
| + | namespace: dex | ||
| + | spec: | ||
| + | secretName: dex-tls | ||
| + | dnsNames: | ||
| + | - dex.teach.local | ||
| + | issuerRef: | ||
| + | name: freeipa-acme | ||
| + | kind: ClusterIssuer | ||
| + | |||
| + | === test-pod.txt === | ||
| + | apiVersion: v1 | ||
| + | kind: Pod | ||
| + | metadata: | ||
| + | name: admin-nginx | ||
| + | namespace: core-test-admin | ||
| + | labels: | ||
| + | app: admin-nginx | ||
| + | spec: | ||
| + | containers: | ||
| + | - name: nginx | ||
| + | image: nginx:1.27 | ||
| + | ports: | ||
| + | - containerPort: 80 | ||
| + | --- | ||
| + | apiVersion: v1 | ||
| + | kind: Pod | ||
| + | metadata: | ||
| + | name: view-nginx | ||
| + | namespace: core-test-view | ||
| + | labels: | ||
| + | app: view-nginx | ||
| + | spec: | ||
| + | containers: | ||
| + | - name: nginx | ||
| + | image: nginx:1.27 | ||
| + | ports: | ||
| + | - containerPort: 80 | ||
| </code> | </code> | ||