User Tools

Site Tools


управление_доступом_в_kubernetes

This is an old revision of the document!


Управление доступом в Kubernetes

Реклама

  • Kubernetes — это решение, превращающее Linux сервера в кластер, и, как Linux является многопользовательской, многозадачной операционной системой, так и Kubernetes позволяет множеству пользователей и приложений работать в одном кластере, безопасно разделяя его ресурсы
  • На нашем вебинаре Вы узнаете как настраивать права доступа в Kubernetes на примерах практических, полезных задач

Техническое задание

  • Создать учетную запись и файл конфигурации с правами для подключения к приложениям, администрированию ресурсов выделенного Namespace и всего кластера Kubernetes
  • Использовать учтённые записи приложений в Kubernetes для доступа к API

Запись вебинара

  • Тэги: Kubernetes, ServiceAccout, RBAC

Шаг 1. Что у нас есть, для начала

Шаг 2. Создание учетной записи

Вариант 2.1 Использование сертификатов

kube1:~# cat ~/.kube/config

kube1:~# echo LS0tLS1CR...LS0tLS0K | base64 -d
kube1:~# kubectl auth whoami

Вариант 2.2 Использование ServiceAccount

  • Для примера можно использовать ServiceAccout из темы Kubernetes Dashboard в Namespace default
  • Показать временные и long-lived Bearer Token для ServiceAccount

Шаг 2.3 Создание файла конфигурации kubectl

Шаг 3. Использование Role и RoleBinding

Шаг 4. Использование ClusterRole и ClusterRoleBinding

Шаг 5. Использование JSON Web Token (JWT) для доступа в Kubernetes

kube1:~/users# kubectl delete clusterrolebindings admin-user

kube1:~/users# kubectl delete serviceaccounts admin-user

user1@client1:~$ rm -rf .kube/

Шаг 6. Использование Service Accounts в приложениях

kube1:~/users# kubectl -n my-ns describe pod my-debian | grep -i account

kube1:~/users# kubectl -n my-ns exec -ti pods/my-debian -- bash

root@my-debian:/# apt update && apt install kubectl
kube1:~/users# kubectl auth can-i get pods --as=system:serviceaccount:my-ns:default

kube1:~/users# cat sa-default-cluster-admin.yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: sa-default-cluster-admin
subjects:
- kind: ServiceAccount
  name: default
  namespace: my-ns
roleRef:
  kind: ClusterRole
  name: cluster-admin
  apiGroup: rbac.authorization.k8s.io
kube1:~/users# kubectl apply -f sa-default-cluster-admin.yaml

Проверяем доступ к API из приложения

kube1:~/users# kubectl delete -f sa-default-cluster-admin.yaml

kube1:~/users# kubectl -n my-ns delete pod my-debian                                                      

Шаг 7. Использование OpenID Connect

kube1:~/users# vim /etc/kubernetes/manifests/kube-apiserver.yaml
...
spec:
  containers:
  - command:
    - kube-apiserver
    - --oidc-issuer-url=https://keycloak.corpX.un/realms/corpX
    #- --oidc-client-id=account
    - --oidc-client-id=any-client
    - --oidc-username-claim=email
    #- --oidc-username-claim=preferred_username
    - --oidc-groups-claim=groups
...
kube1:~# ps ax | grep kube-apiserver

kube1:~/users# kubectl -n kube-system logs Pod/kube-apiserver-kube1
...
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]"
...
  • Открываем браузер
  • Подколючемся к giltab через keycloak
user1@client1:~$ kubelogin

user1@client1:~$ kubectl auth whoami

Вопросы?

Домашнее задание

  1. Куда и через сколько исчезает “kubectl get csr” после “approve” ?

Перезапуск вебинара

user1@client1:~$ rm -v user1*

user1@client1:~$ rm -rfv .kube/

kube1:~# rm -rfv users/

Дополнительные материалы

Черновик Auditing

https://habr.com/ru/companies/slurm/articles/711868/|Журналы аудита Kubernetes: лучшие практики и настройка

kube1:~# cat /etc/kubernetes/audit-policy.yaml
apiVersion: audit.k8s.io/v1
kind: Policy
rules:

  - level: None
    verbs: ["get", "watch", "list"]

  - level: None
    resources:
    - group: "" # core
      resources: ["events"]

  - level: None
    users:
    - "system:kube-scheduler"
    - "system:kube-proxy"
    - "system:apiserver"
    - "system:kube-controller-manager"
    - "system:serviceaccount:gatekeeper-system:gatekeeper-admin"

  - level: None
    userGroups: ["system:nodes"]

  - level: RequestResponse

kube1:~# kubectl apply -f /etc/kubernetes/audit-policy.yaml --dry-run=client
error: resource mapping not found for name...

https://kubernetes.io/docs/tasks/debug/debug-cluster/audit/|Kubernetes Documentation - Auditing

kube1:~# vim /etc/kubernetes/manifests/kube-apiserver.yaml
...
    - --audit-log-maxage=1
...

kube1:~# date

kube1:~# journalctl -u kubelet --since "2025-12-01 10:19:00" | cat | less
...
..."SyncLoop REMOVE" source="file" pods=["kube-system/kube-apiserver-kube1"]...
...
..."Killing container with a grace period" pod="kube-system/kube-apiserver-kube1"...


kube1:~# tail -f /var/log/kubernetes/audit/audit.log | jq

user1@client1:~$ kubectl -n my-apwebd-ns delete pod my-webd-<TAB>

Черновик DEX

Описание

Создаём группы и пользователей под Kubernetes
У нас уже есть настроенная Freeipa
FreeIPA — это серверная система для централизованного управления пользователями, группами, доступами, сертификатами и DNS в инфраструктуре.
Заходим на freeipa, через web интерфейс или же можно воспользоваться консолью
https://ipa-server-1.teach.local/
ssh root@192.168.100.252
Мы воспользуемся консолью чтобы сэкономить время
Но предварительно на web проверим что нет групп для  кубернетис
Используем команду kinit чтобы получить билет Kerberos и введем команды для создания

ipa group-add k8s-cluster-admins \
  --desc="Kubernetes cluster admins"

ipa group-add k8s-ns-core-test-view \
  --desc="View access to core-test namespace"

ipa group-add k8s-ns-core-test-admin \
  --desc="Admin access to core-test namespace"
ipa group-add k8s-ns \
--desc="Access only view all ns"

Теперь обновим страницу с группами и увидим созданные нами группы
Далее Создаём тестового пользователя
ipa user-add ivan \
  --first=Ivan \
  --last=Petrov \
  --password

ipa user-add sergey \
  --first=Sergey \
  --last=Admin \
  --password


ipa user-add kirill \
  --first=Kirill \
  --last=Viewer \
  --password
ipa user-add ira \
  --first=Irina\
  --last=Viewer \
  --password


Далее попросят ввести пароль
Вводим пароль 123456
И подтверждаем
Конечно такой пароль только для демонстрации а не для продового использования
Добавляем пользователя в группу

ipa group-add-member k8s-ns-core-test-view --users=ivan
ipa group-add-member k8s-ns-core-test-admin --users=kirill
ipa group-add-member k8s-ns --users=ira
ipa group-add-member k8s-cluster-admins --users=sergey

Далее Аккаунт для Dex в FreeIPA (LDAP bind user)
Dex должен логиниться в LDAP FreeIPA с каким-то пользователем.

ipa user-add dex-bind \
  --first=Dex \
  --last=Bind \
  --password

Проверяем LDAP-доступ к FreeIPA
Чтобы  убедится что dex сможет залогинется


Зайдем на сервер c которого будем управлять k8s и выполнил поиск нашего пользователя
ssh root@192.168.100.249

ldapsearch -x   -H ldap://192.168.100.252:389   -D "uid=dex-bind,cn=users,cn=accounts,dc=teach,dc=local"   -W   -b "cn=users,cn=accounts,dc=teach
,dc=local"   "(uid=ivan)"

Подключимся пользователем dex-bind
И поищем созданного нами пользователя ivan
Видим что все ок данные получаем

Далее настроим установим и настроим dex
Для этого будем использовать helm
Helm — это менеджер пакетов для Kubernetes, грубо говоря:
«apt/yum для Kubernetes


Нам понадобится серт мэнеджер для выпуска сетификатов и ингресс контроллер


helm repo add jetstack https://charts.jetstack.io
helm repo update

helm install cert-manager jetstack/cert-manager \
  --namespace cert-manager \
  --create-namespace \
  --set crds.enabled=true


helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx
helm repo update

helm install ingress-nginx ingress-nginx/ingress-nginx \
  -n ingress-nginx --create-namespace \
  --set controller.ingressClassResource.name=nginx \
  --set controller.ingressClassByName=true \
  --set controller.hostNetwork=true \
  --set controller.dnsPolicy=ClusterFirstWithHostNet \
  --set controller.kind=DaemonSet \
  --set controller.service.type=NodePort


Перезагружаем сервера



Начиная с новых версий FreeIPA, в нём есть ACME-сервер
ACME-server — это “серверная сторона автоматики сертификатов”.
ACME (Automatic Certificate Management Environment) — это протокол, по которому:
клиент (cert-manager, acme.sh, lego, certbot и т.п.)
общается с CA (центром сертификации) через ACME-server
чтобы автоматически выпускать и обновлять TLS-сертификаты.
Примеры ACME-серверов:
Let’s Encrypt (самый известный; их сервер — Boulder)



ipa-acme-manage enable
ipa-acme-manage status


появится эндпоинт

https://ipa-server-1.teach.local/acme/directory

curl -vk https://ipa-server-1.teach.local/acme/directory

далее получаем сертифкат freeipa корневой
идем на сервер freeipa

sudo cat /etc/ipa/ca.crt > ipa-ca.crt
далее переводим в base64
base64 -w0 ipa-ca.crt > ipa-ca.b64

затем нам нужен ClusterIssuer

ClusterIssuer — это кластерный “поставщик сертификатов” в cert-manager, который виден во всех неймспейсах Kubernetes.
Говоря по-человечески:
cert-manager — это оператор, который умеет автоматически выпускать и обновлять сертификаты.
ClusterIssuer/Issuer — это “настройка, куда идти за сертификатом и как его получать”.

Файл issuser.txt
И caBundle ПОДСТАВЬ_ЗДЕСЬ_СТРОКУ_ИЗ_ipa-ca.b64


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


1. Выпускаем сертификат для Dex
Сделаем Certificate, который:
использует твой ClusterIssuer freeipa-acme;
кладёт сертификат в Secret dex-tls в namespace dex.


Для начала нам нужна dns запись
Посмотрим какие ip у серверов k8s
kubectl get nodes -o wide
В freeipa

ipa dnsrecord-add teach.local dex --a-rec=192.168.100.229
это адрес одной из наших нод

Создаем нейаспейс
kubectl create ns dex

затем применяем файл  с запросом сетификата
файл 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

Добавляем репозиторий  с dex


helm repo add dex https://charts.dexidp.io
helm repo update

Качаем локально хелм чарт
helm pull dex/dex

У нас скачивается архив с helm чартом
ls
далее распаковываем архив
tar -xvf dex-0.24.0.tgz
переходим в каталог
cd dex


и тут нас интересует файл values.yaml
vi values.yml
открываем файл и редактируем берем пример из файла config.txt
нужно сгенерить секрет
openssl rand -hex 32
и заменить SUPER-SECRET-STRING на полученный секрет
Создаем секрет

kubectl -n dex create secret generic dex-ldap-bind \
  --from-literal=DEX_LDAP_BIND_DN='uid=dex-bind,cn=users,cn=accounts,dc=teach,dc=local' \
  --from-literal=DEX_LDAP_BIND_PW='123456'


сохраняем и применяем
helm upgrade --install dex .   -n dex   -f values.yaml
Тест с любой машины, где dex.teach.local резолвится в IP ноды:
curl -vk https://dex.teach.local/dex/.well-known/openid-configuration
Кладём CA FreeIPA на мастер
Переходим на мастер ноду
ssh root@192.168.100.230
scp root@ipa-server-1.teach.local:/etc/ipa/ca.crt /root/ipa-ca.crt
sudo cp /root/ipa-ca.crt /etc/kubernetes/ssl/dex-ca.crt

Затем правим настройки kubeapi

vi /etc/kubernetes/manifests/kube-apiserver.yaml

Добавляем

- --oidc-issuer-url=https://dex.teach.local/dex
- --oidc-client-id=dex-k8s-authenticator
- --oidc-username-claim=email
- --oidc-groups-claim=groups
- --oidc-ca-file=/etc/kubernetes/ssl/dex-ca.crt

Kubeapi сам перезагрузится

Если все ок то в браузере перейдя по ссылке увидим ответ от dex
https://dex.teach.local/dex/.well-known/openid-configuration

отлично dex установлен но Dex сам по себе — только  (OIDC-провайдер).
Страничку, где после логина рисуется готовый kubeconfig, делает другой сервис — dex-k8s-authenticator.


kubectl create namespace dex-auth


Добавляем на freeipa dns запись

ipa dnsrecord-add teach.local auth --a-ip-address=192.168.100.229
ipa dnsrecord-show teach.local auth
выпускаем сертификат для web интерфейса
kubectl apply -f ../certificate-auth.yaml

Ставим dex-k8s-authenticator

helm repo add wiremind https://wiremind.github.io/wiremind-helm-charts
helm repo update
helm pull wiremind/dex-k8s-authenticator

tar -xvf  dex-k8s-authenticator-1.7.0.tgz

cd dex-k8s-authenticator

vi values.yaml

в вэлюсах указываем наши данные
вначале копируем содержимое файла certificate-auth.txt

secret: тут должен совпадать с секретов в values dex
далее добавить CA Freeipa в файл конфига
caCerts:
  enabled: true
  secrets:
    - name: ipa-ca
      filename: ipa-ca.crt
      value: |-


идем на сервер Freeipa
выполняем команду чтобы получить серт в base64
cat /etc/ipa/ca.crt | base64 -w0 > ipa-ca.b64


в k8s_ca_pem:
указываем CA кубернетис
идем на ноду мастера k8s

cat /etc/kubernetes/ssl/ca.crt

helm upgrade --install dex-auth .   -n dex-auth   -f values.yaml

Теперь нас необходимо связать группы в k8s и freeipa
Применяем манифесты для добавления кластер ролей

kubectl create ns core-test-admin
kubectl create ns core-test-view

проверяем заходим на https://auth.teach.local/

пробуем залогинится под польз кто админ

Заходим на другой сервер и проверяем команды которые нам дал dex

ssh root@192.168.100.227

Вводим команды из web интерфейса

И пробуем подключиться

Далее запускаем тестовые поды в наших ns

Применяем файл  test-pod.txt

И тестируем права

Пробуем заходить разными польз

Если не пускает польз когда у него права только на один ns то надо дать права на просмотр.

Файлы

=== 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
управление_доступом_в_kubernetes.1766117620.txt.gz · Last modified: 2025/12/19 07:13 by val