# LDAP

<https://habr.com/ru/articles/441112/>

![](https://296194292-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FLoAqAoOfr7XVUQw7Gff8%2Fuploads%2Fgit-blob-dde92941d31a9dbd2bd7a92ac959fcf4b84f31a7%2Fkj60vi6ugv7bunlqdts2nlr2vja.png?alt=media)

Небольшая инструкция о том, как используя Keycloak можно связать Kubernetes с вашим LDAP-сервером и настроить импорт пользователей и групп. Это позволит настраивать RBAC для ваших пользователей и использовать auth-proxy чтобы защитить Kubernetes Dashboard и другие приложения, которые не умеют производить авторизацию самостоятельно.

## Установка Keycloak

Предположим что у вас уже есть LDAP-сервер. Это может быть Active Directory, FreeIPA, OpenLDAP или что-либо еще. Если LDAP-сервера у вас нет, то в принципе вы можете создавать пользователей прямо в интерфейсе Keycloak, либо использовать публичные oidc-провайдеры (Google, Github, Gitlab), результат получится почти такой же.

Первым делом установим сам Keycloak, установка может выполняться отдельно, так и сразу в Kubernetes-кластер, как правило если у вас имеется несколько Kubernetes-кластеров, было бы проще установить его отдельно. С другой стороны вы всегда можете использовать [официальный helm-чарт](https://github.com/helm/charts/tree/master/stable/keycloak) и установить его прямо в ваш кластер.

Для хранения данных Keycloak вам понадобится база данных. По умолчанию используется `h2` (все данные хранятся локально), но возможно также использовать `postgres`, `mysql` или `mariadb`.

Если вы все же надумали установить Keycloak отдельно, более подробные инструкции вы найдете в [официальной документации](https://www.keycloak.org/docs/latest/getting_started/index.html).

## Настройка федерации

Первым делом создадим новый realm. Realm — это пространство нашего приложения. Каждое приложение может иметь свой realm с разными пользователями и настройками авторизации. Master realm используется самим Keycloak и использовать его для чего-нибудь еще неправильно.

Нажимаем **Add realm**

| Option            | Value                                                                   |
| ----------------- | ----------------------------------------------------------------------- |
| Name              | kubernetes                                                              |
| Display Name      | Kubernetes                                                              |
| HTML Display Name | \<img src="<https://kubernetes.io/images/nav\\_logo.svg>" width="400" > |

Kubernetes по умолчанию проверяет подтвержден у пользователя email или нет. Так как мы используем собственный LDAP-сервер, то тут эта проверка почти всегда будет возвращать `false`. Давайте отключим представление этого параметра в Kubernetes:

**Client scopes** --> **Email** --> **Mappers** --> **Email verified** (Delete)

Теперь настроим федерацию, для этого перейдем в:

**User federation** --> **Add provider...** --> **ldap**

Приведу пример настройки для FreeIPA:

| Option                         | Value                                                   |
| ------------------------------ | ------------------------------------------------------- |
| Console Display Name           | freeipa.example.org                                     |
| Vendor                         | Red Hat Directory Server                                |
| UUID LDAP attribute            | ipauniqueid                                             |
| Connection URL                 | ldaps\://freeipa.example.org                            |
| Users DN                       | cn=users,cn=accounts,dc=example,dc=org                  |
| Bind DN                        | uid=keycloak-svc,cn=users,cn=accounts,dc=example,dc=org |
| Bind Credential                |                                                         |
| Allow Kerberos authentication: | on                                                      |
| Kerberos Realm:                | EXAMPLE.ORG                                             |
| Server Principal:              | HTTP/freeipa.<example.org@EXAMPLE.ORG>                  |
| KeyTab:                        | /etc/krb5.keytab                                        |

Пользователя `keycloak-svc` нужно создать заранее на нашем LDAP-сервере.

В случае с Active Directory, достаточно просто выбрать **Vendor: Active Directory** и необходимые настройки подставятся в форму автоматически.

Нажимаем **Save**

Теперь перейдем:

**User federation** --> **freeipa.example.org** --> **Mappers** --> **First Name**

| Option         | Value     |
| -------------- | --------- |
| Ldap attribure | givenName |

Теперь включим маппинг групп:

**User federation** --> **freeipa.example.org** --> **Mappers** --> **Create**

| Option                        | Value                                        |
| ----------------------------- | -------------------------------------------- |
| Name                          | groups                                       |
| Mapper type                   | group-ldap-mapper                            |
| LDAP Groups DN                | cn=groups,cn=accounts,dc=example,dc=org      |
| User Groups Retrieve Strategy | GET\_GROUPS\_FROM\_USER\_MEMBEROF\_ATTRIBUTE |

На этом настройка федерации закончена, перейдем к настройке клиента.

## Настройка клиента

Создадим нового клиента (приложение которое будет получать пользователей из Keycloak). Переходим:

**Clients** --> **Create**

| Option              | Value                                |
| ------------------- | ------------------------------------ |
| Client ID           | kubernetes                           |
| Access Type         | confidential                         |
| Root URL            | <http://kubernetes.example.org/>     |
| Valid Redirect URIs | <http://kubernetes.example.org/\\>\* |
| Admin URL           | <http://kubernetes.example.org/>     |

Так же создадим scope для групп:

**Client Scopes** --> **Create**

| Option          | Value       |
| --------------- | ----------- |
| Template        | No template |
| Name            | groups      |
| Full group path | false       |

И настроим mapper для них:

**Client Scopes** --> **groups** --> **Mappers** --> **Create**

| Option           | Value            |
| ---------------- | ---------------- |
| Name             | groups           |
| Mapper Type      | Group membership |
| Token Claim Name | groups           |

Теперь нам нужно включить маппинг груп в нашем client scope:

**Clients** --> **kubernetes** --> **Client Scopes** --> **Default Client Scopes**

Выбираем **groups** в **Available Client Scopes**, нажимаем **Add selected**

Теперь настроим аутентификацию нашего приложения, переходим:

**Clients** --> **kubernetes**

| Option                | Value |
| --------------------- | ----- |
| Authorization Enabled | ON    |

Нажимем **save** и на этом настройка клиента завершена, теперь на вкладке

**Clients** --> **kubernetes** --> **Credentials**

вы сможете получить **Secret** который мы будем использовать в дальнейшем.

## Настройка Kubernetes

Настройка Kubernetes для OIDC-авторизации достаточно тривиальна и не является чем-то очень сложным. Все что вам нужно это положить CA-сертификат вашего OIDC-сервера в `/etc/kubernetes/pki/oidc-ca.pem` и добавить необходимые опции для kube-apiserver.

Для этого обновите `/etc/kubernetes/manifests/kube-apiserver.yaml` на всех ваших мастерах:

```
...
spec:
  containers:
  - command:
    - kube-apiserver
...
    - --oidc-ca-file=/etc/kubernetes/pki/oidc-ca.pem
    - --oidc-client-id=kubernetes
    - --oidc-groups-claim=groups
    - --oidc-issuer-url=https://keycloak.example.org/auth/realms/kubernetes
    - --oidc-username-claim=email
...
```

А так-же обновите kubeadm конфиг в кластере, что бы не потерять эти настройки при обновлении:

```
kubectl edit -n kube-system configmaps kubeadm-config
```

```
...
data:
  ClusterConfiguration: |
    apiServer:
      extraArgs:
        oidc-ca-file: /etc/kubernetes/pki/oidc-ca.pem
        oidc-client-id: kubernetes
        oidc-groups-claim: groups
        oidc-issuer-url: https://keycloak.example.org/auth/realms/kubernetes
        oidc-username-claim: email
...
```

На этом настройка Kubernetes завершена. Вы можете повторить данные действия во всех ваших Kubernetes-кластерах.

## Начальная авторизация

После данных действий вы уже будете иметь Kubernetes-кластер с настроенной OIDC-авторизацией. Единственный момент, что ваши пользователи пока что не имеют настроенного клиента как и собственного kubeconfig. Что бы эту проблему решить нужно настроить автоматическую выдачу kubeconfig пользователям после успешной авторизации.

Для этого можно использовать специальные web-приложения, которые позволяют провести аутентификацию пользователя а затем скачать готовый kubeconfig. Одно из самых удобных — это [Kuberos](https://github.com/negz/kuberos), он позволяет в одном конфиге описать все Kubernetes-кластеры и легко переключаться между ними.

Для настройки Kuberos достаточно описать template для kubeconfig и запустить со следующими параметрами:

```
kuberos https://keycloak.example.org/auth/realms/kubernetes kubernetes /cfg/secret /cfg/template
```

Для более детальной информации смотрите [Usage](https://github.com/negz/kuberos#usage) на Github.

Так же возможно использовать [kubelogin](https://github.com/int128/kubelogin) если вы хотите производить авторизацию непосредственно на компьютере пользователя. В этом случае пользователю откроется браузер с формой авторизации на localhost.

Полученный kubeconfig можно проверить на сайте [jwt.io](https://jwt.io/#debugger-io). Просто скопируейте значение `users[].user.auth-provider.config.id-token` из вашего kubeconfig в форму на сайте и сразу получите расшифровку.

## Настройка RBAC

При настройке RBAC можно ссылаться как на имя пользователя (поле `name` в jwt-токене), так и на группу пользователей (поле `groups` в jwt-токене). Вот пример настройки прав для группы `kubernetes-default-namespace-admins`:

**kubernetes-default-namespace-admins.yaml**

```jsx
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: default-admins
  namespace: default
rules:
- apiGroups:
  - '*'
  resources:
  - '*'
  verbs:
  - '*'
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: kubernetes-default-namespace-admins
  namespace: default
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: Role
  name: default-admins
subjects:
- apiGroup: rbac.authorization.k8s.io
  kind: Group
  name: kubernetes-default-namespace-admins
```

Больше примеров для RBAC можно найти в [официальной документации Kubernetes](https://kubernetes.io/docs/reference/access-authn-authz/rbac/)

## Настройка auth-proxy

Есть замечательный проект [keycloak-gatekeeper](https://github.com/keycloak/keycloak-gatekeeper), который позволяет защитить любое приложение, предоставляя пользователю возможность аутентифицироваться на OIDC-сервере. Я покажу как можно настроить его на примере Kubernetes Dashboard:

**dashboard-proxy.yaml**

```jsx
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: kubernetes-dashboard-proxy
spec:
  replicas: 1
  template:
    metadata:
      labels:
        app: kubernetes-dashboard-proxy
    spec:
      containers:
      - args:
        - --listen=0.0.0.0:80
        - --discovery-url=https://keycloak.example.org/auth/realms/kubernetes
        - --client-id=kubernetes
        - --client-secret=<your-client-secret-here>
        - --redirection-url=https://kubernetes-dashboard.example.org
        - --enable-refresh-tokens=true
        - --encryption-key=ooTh6Chei1eefooyovai5ohwienuquoh
        - --upstream-url=https://kubernetes-dashboard.kube-system
        - --resources=uri=/*
        image: keycloak/keycloak-gatekeeper
        name: kubernetes-dashboard-proxy
        ports:
        - containerPort: 80
          livenessProbe:
            httpGet:
              path: /oauth/health
              port: 80
            initialDelaySeconds: 3
            timeoutSeconds: 2
          readinessProbe:
            httpGet:
              path: /oauth/health
              port: 80
            initialDelaySeconds: 3
            timeoutSeconds: 2
---
apiVersion: v1
kind: Service
metadata:
  name: kubernetes-dashboard-proxy
spec:
  ports:
  - port: 80
    protocol: TCP
    targetPort: 80
  selector:
    app: kubernetes-dashboard-proxy
  type: ClusterIP
```
