We fasten ActiveDirectory authorization to Kubernetes using Keycloak

This article has been written to expand on the already the existing, but talks about the features of the bundle with Microsoft ActiveDirectory, and also complements it.

In this article I will tell you how to install and configure:

  • keycloak is an open source project. Which provides a single point of entry for applications. Works with a variety of protocols, including LDAP and OpenID which we are interested in.
  • keycloak gatekeeper - reverse proxy application that allows you to integrate authorization through Keycloak.
  • Gangways - an application that generates a config for kubectl with which you can log in and connect to the Kubernetes API through OpenID.

How permissions work in Kubernetes.

We can manage user / group rights using RBAC, a bunch of articles have already been created about this, I will not dwell on this in detail. The problem is that you can use RBAC to restrict user rights, but Kubernetes doesn't know anything about users. It turns out that we need a user delivery mechanism in Kubernetes. To do this, we will add a provider to Kuberntes OpenID, which will say that such a user really exists, and Kubernetes itself will give him the rights.

Prepare

  • You will need a Kubernetes cluster or minikube
  • Active Directory
  • Domains:
    keycloak.example.org
    kubernetes-dashboard.example.org
    gangway.example.org
  • Certificate for domains or self-signed certificate

I will not dwell on how to create a self-signed certificate, you need to create 2 certificates, this is the root (Certificate Authority) and wildcard client for the *.example.org domain

After you receive / issue certificates, the client must be added to Kubernetes, for this we create a secret for it:

kubectl create secret tls tls-keycloak --cert=example.org.crt --key=example.org.pem

Next, we will use it for our Ingress controller.

Keycloak Installation

I decided that the easiest way is to use ready-made solutions for this, namely helm charts.

Install the repository and update it:

helm repo add codecentric https://codecentric.github.io/helm-charts
helm repo update

Create a keycloak.yml file with the following content:

keycloak.yml

keycloak:
  # Имя администратора
  username: "test_admin"
  # ΠŸΠ°Ρ€ΠΎΠ»ΡŒ администратор  
  password: "admin"
  # Π­Ρ‚ΠΈ Ρ„Π»Π°Π³ΠΈ Π½ΡƒΠΆΠ½Ρ‹ Ρ‡Ρ‚ΠΎ Π±Ρ‹ ΠΏΠΎΠ·Π²ΠΎΠ»ΠΈΡ‚ΡŒ Π·Π°Π³Ρ€ΡƒΠΆΠ°Ρ‚ΡŒ Π² Keycloak скрипты прямо Ρ‡Π΅Ρ€Π΅Π· web ΠΌΠΎΡ€Π΄Ρƒ. Π­Ρ‚ΠΎ Π½Π°ΠΌ 
  ΠΏΠΎΠ½Π°Π΄ΠΎΠ±ΠΈΡ‚ΡŒΡΡ Ρ‡Ρ‚ΠΎ Π±Ρ‹ ΠΏΠΎΡ‡ΠΈΠ½ΠΈΡ‚ΡŒ ΠΎΠ΄ΠΈΠ½ Π±Π°Π³, ΠΎ ΠΊΠΎΡ‚ΠΎΡ€ΠΎΠΌ Π½ΠΈΠΆΠ΅.
  extraArgs: "-Dkeycloak.profile.feature.script=enabled -Dkeycloak.profile.feature.upload_scripts=enabled" 
  # Π’ΠΊΠ»ΡŽΡ‡Π°Π΅ΠΌ ingress, ΡƒΠΊΠ°Π·Ρ‹Π²Π°Π΅ΠΌ имя хоста ΠΈ сСртификат ΠΊΠΎΡ‚ΠΎΡ€Ρ‹ΠΉ ΠΌΡ‹ ΠΏΡ€Π΅Π΄Π²Π°Ρ€ΠΈΡ‚Π΅Π»ΡŒΠ½ΠΎ сохранили Π² secrets
  ingress:
    enabled: true 
    path: /
    annotations:
      kubernetes.io/ingress.class: nginx
      ingress.kubernetes.io/affinity: cookie
    hosts:
      - keycloak.example.org
    tls:
    - hosts:
        - keycloak.example.org
      secretName: tls-keycloak
  # Keycloak для своСй Ρ€Π°Π±ΠΎΡ‚Ρ‹ Ρ‚Ρ€Π΅Π±ΡƒΠ΅Ρ‚ Π±Π°Π·Ρƒ Π΄Π°Π½Π½Ρ‹Ρ…, Π² тСстовых цСлях я Ρ€Π°Π·Π²ΠΎΡ€Π°Ρ‡ΠΈΠ²Π°ΡŽ Postgresql прямо Π² Kuberntes, Π² ΠΏΡ€ΠΎΠ΄Π°ΠΊΡˆΠ΅Π½Π΅ Ρ‚Π°ΠΊ Π»ΡƒΡ‡ΡˆΠ΅ Π½Π΅ Π΄Π΅Π»Π°Ρ‚ΡŒ!
  persistence:
    deployPostgres: true
    dbVendor: postgres

postgresql:
  postgresUser: keycloak
  postgresPassword: ""
  postgresDatabase: keycloak
  persistence:
    enabled: true

Federation setup

Next, go to the web interface keycloak.example.org

Click in the left corner add realm

Key
Value

Name
kubernetes

Display Name
Kubernetes

Disable user email verification:
Client scopes β€”> Email β€”> Mappers β€”> Email verified (Delete)

We set up federation to import users from ActiveDirectory, I will leave screenshots below, I think it will be clearer.

User federation β€”> Add provider… β€”> ldap

Federation setupWe fasten ActiveDirectory authorization to Kubernetes using Keycloak
We fasten ActiveDirectory authorization to Kubernetes using Keycloak

If all is well, then after pressing the button Synchronize all users you will see a message about the successful import of users.

Next we need to map our groups

User federation --> ldap_localhost --> Mappers --> Create

Creating a mapperWe fasten ActiveDirectory authorization to Kubernetes using Keycloak

Client setup

It is necessary to create a client, in terms of Keycloak, this is an application that will be authorized from him. I will highlight the important points in the screenshot in red.

Clients β€”> Create

Client setupWe fasten ActiveDirectory authorization to Kubernetes using Keycloak

Let's create scoupe for groups:

Client Scopes β€”> Create

Create scopeWe fasten ActiveDirectory authorization to Kubernetes using Keycloak

And set up a mapper for them:

Client Scopes β€”> groups β€”> Mappers β€”> Create

MapperWe fasten ActiveDirectory authorization to Kubernetes using Keycloak

Add the mapping of our groups to the Default Client Scopes:

Clients β€”> kubernetes β€”> Client Scopes β€”> Default Client Scopes
Choosing groups Π² Available Client Scopespush Add selected

We get the secret (and write it to the thread) which we will use for authorization in Keycloak:

Clients β€”> kubernetes β€”> Credentials β€”> Secret
This completes the setup, but I had an error when, after successful authorization, I received an error 403. Bug report.

Fix:

Client Scopes β€”> roles β€”> Mappers β€”> Create

MapperWe fasten ActiveDirectory authorization to Kubernetes using Keycloak

Script code

// add current client-id to token audience
token.addAudience(token.getIssuedFor());

// return token issuer as dummy result assigned to iss again
token.getIssuer();

Configuring Kubernetes

We need to specify where our root certificate from the site lies, and where the OIDC provider is located.
To do this, edit the file /etc/kubernetes/manifests/kube-apiserver.yaml

kube-apiserver.yaml


...
spec:
  containers:
  - command:
    - kube-apiserver
...
    - --oidc-ca-file=/var/lib/minikube/certs/My_Root.crt
    - --oidc-client-id=kubernetes
    - --oidc-groups-claim=groups
    - --oidc-issuer-url=https://keycloak.example.org/auth/realms/kubernetes
    - --oidc-username-claim=email
...

Update kubeadm config in the cluster:

kubeadmconfig

kubectl edit -n kube-system configmaps kubeadm-config


...
data:
  ClusterConfiguration: |
    apiServer:
      extraArgs:
        oidc-ca-file: /var/lib/minikube/certs/My_Root.crt
        oidc-client-id: kubernetes
        oidc-groups-claim: groups
        oidc-issuer-url: https://keycloak.example.org/auth/realms/kubernetes
        oidc-username-claim: email
...

Setting auth-proxy

You can use keycloak gatekeeper to protect your web application. In addition to the fact that this reverse proxy will authorize the user before showing the page, it will also pass information about you to the end application in the headers. Thus, if your application supports OpenID, then the user is immediately authorized. Consider the example of Kubernetes Dashboard

Installing Kubernetes Dashboard


helm install stable/kubernetes-dashboard --name dashboard -f values_dashboard.yaml

values_dashboard.yaml

enableInsecureLogin: true
service:
  externalPort: 80
rbac:
  clusterAdminRole: true
  create: true
serviceAccount:
  create: true
  name: 'dashboard-test'

Setting access rights:

Let's create a ClusterRoleBinding that will give cluster admin rights (standard ClusterRole cluster-admin) for users in the DataOPS group.


kubectl apply -f rbac.yaml

rbac.yaml


apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: dataops_group
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: cluster-admin
subjects:
- apiGroup: rbac.authorization.k8s.io
  kind: Group
  name: DataOPS

Install keycloak gatekeeper:


helm repo add gabibbo97 https://gabibbo97.github.io/charts/
helm repo update
helm install gabibbo97/keycloak-gatekeeper --version 2.1.0 --name keycloak-gatekeeper -f values_proxy.yaml

values_proxy.yaml



# Π’ΠΊΠ»ΡŽΡ‡Π°Π΅ΠΌ ingress
ingress:
  enabled: true
  annotations:
    kubernetes.io/ingress.class: nginx
  path: /
  hosts:
    - kubernetes-dashboard.example.org
  tls:
   - secretName: tls-keycloak
     hosts:
       - kubernetes-dashboard.example.org

# Π“ΠΎΠ²ΠΎΡ€ΠΈΠΌ Π³Π΄Π΅ ΠΌΡ‹ Π±ΡƒΠ΄Π΅ΠΌ Π°Π²Ρ‚ΠΎΡ€ΠΈΠ·ΠΎΠ²Ρ‹Π²Π°Ρ‚ΡŒΡΡ Ρƒ OIDC ΠΏΡ€ΠΎΠ²Π°ΠΉΠ΄Π΅Ρ€Π°
discoveryURL: "https://keycloak.example.org/auth/realms/kubernetes"
# Имя ΠΊΠ»ΠΈΠ΅Π½Ρ‚Π° ΠΊΠΎΡ‚ΠΎΡ€ΠΎΠ³ΠΎ ΠΌΡ‹ создали Π² Keycloak
ClientID: "kubernetes"
# Secret ΠΊΠΎΡ‚ΠΎΡ€Ρ‹ΠΉ я просил Π·Π°ΠΏΠΈΡΠ°Ρ‚ΡŒ
ClientSecret: "c6ec03b8-d0b8-4cb6-97a0-03becba1d727"
# ΠšΡƒΠ΄Π° ΠΏΠ΅Ρ€Π΅Π½Π°ΠΏΡ€Π°Π²ΠΈΡ‚ΡŒ Π² случаС ΡƒΡΠΏΠ΅ΡˆΠ½ΠΎΠΉ Π°Π²Ρ‚ΠΎΡ€ΠΈΠ·Π°Ρ†ΠΈΠΈ. Π€ΠΎΡ€ΠΌΠ°Ρ‚ <SCHEMA>://<SERVICE_NAME>.><NAMESAPCE>.<CLUSTER_NAME>
upstreamURL: "http://dashboard-kubernetes-dashboard.default.svc.cluster.local"
# ΠŸΡ€ΠΎΠΏΡƒΡΠΊΠ°Π΅ΠΌ ΠΏΡ€ΠΎΠ²Π΅Ρ€ΠΊΡƒ сСртификата, Ссли Ρƒ нас самоподписанный
skipOpenidProviderTlsVerify: true
# Настройка ΠΏΡ€Π°Π² доступа, пускаСм Π½Π° всС path Ссли ΠΌΡ‹ Π² Π³Ρ€ΡƒΠΏΠΏΠ΅ DataOPS
rules:
  - "uri=/*|groups=DataOPS"

After that, when you try to go to kubernetes-dashboard.example.org, we will be redirected to Keycloak and in case of successful authorization we will get to the Dashboard already logged in.

gangway installation

For convenience, you can add a gangway that will generate a config file for kubectl, with the help of which we will get into Kubernetes under our user.


helm install --name gangway stable/gangway -f values_gangway.yaml

values_gangway.yaml


gangway:
  # ΠŸΡ€ΠΎΠΈΠ·Π²ΠΎΠ»ΡŒΠ½ΠΎΠ΅ имя кластСра
  clusterName: "my-k8s"
  # Π“Π΄Π΅ Ρƒ нас OIDC ΠΏΡ€ΠΎΠ²Π°ΠΉΠ΄Π΅Ρ€
  authorizeURL: "https://keycloak.example.org/auth/realms/kubernetes/protocol/openid-connect/auth"
  tokenURL: "https://keycloak.example.org/auth/realms/kubernetes/protocol/openid-connect/token"
  audience: "https://keycloak.example.org/auth/realms/kubernetes/protocol/openid-connect/userinfo"
  # ВСоритичСски сюда ΠΌΠΎΠΆΠ½ΠΎ Π΄ΠΎΠ±Π°Π²ΠΈΡ‚ΡŒ groups ΠΊΠΎΡ‚ΠΎΡ€Ρ‹Π΅ ΠΌΡ‹ Π·Π°ΠΌΠ°ΠΏΠΈΠ»ΠΈ
  scopes: ["openid", "profile", "email", "offline_access"]
  redirectURL: "https://gangway.example.org/callback"
  # Имя ΠΊΠ»ΠΈΠ΅Π½Ρ‚Π°
  clientID: "kubernetes"
  # Π‘Π΅ΠΊΡ€Π΅Ρ‚
  clientSecret: "c6ec03b8-d0b8-4cb6-97a0-03becba1d727"
  # Если ΠΎΡΡ‚Π°Π²ΠΈΡ‚ΡŒ Π΄Π΅Ρ„ΠΎΠ»Ρ‚Π½ΠΎΠ΅ Π·Π½Π°Ρ‡Π½ΠΈΠ΅, Ρ‚ΠΎ Π·Π° имя ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»Ρ Π±ΡƒΠ΄Π΅Ρ‚ Π±Ρ€Π°Ρ‚ΡŒΡ <b>Frist name</b> <b>Second name</b>, Π° ΠΏΡ€ΠΈ "sub" Π΅Π³ΠΎ Π»ΠΎΠ³ΠΈΠ½
  usernameClaim: "sub"
  # Π”ΠΎΠΌΠ΅Π½Π½ΠΎΠ΅ имя ΠΈΠ»ΠΈ IP адрСсс API сСрвСра
  apiServerURL: "https://192.168.99.111:8443"

# Π’ΠΊΠ»ΡŽΡ‡Π°Π΅ΠΌ Ingress
ingress:
  enabled: true
  annotations:
    kubernetes.io/ingress.class: nginx
    nginx.ingress.kubernetes.io/proxy-buffer-size: "64k"
  path: /
  hosts:
  - gangway.example.org
  tls:
  - secretName: tls-keycloak
    hosts:
      - gangway.example.org

# Если ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅ΠΌ самоподписанный сСртификат, Ρ‚ΠΎ Π΅Π³ΠΎ(ΠΎΡ‚ΠΊΡ€Ρ‹Ρ‚Ρ‹ΠΉ ΠΊΠΎΡ€Π½Π΅Π²ΠΎΠΉ сСртификат) Π½Π°Π΄ΠΎ ΡƒΠΊΠ°Π·Π°Ρ‚ΡŒ.
trustedCACert: |-
 -----BEGIN CERTIFICATE-----
 MIIDVzCCAj+gAwIBAgIBATANBgkqhkiG9w0BAQsFADA1MQswCQYDVQQGEwJVUzEQMA4GA1UEChMHRGF0YU9QUzEUMBIGA1UEAxMLbXkgcm9vdCBrZXkwHhcNMjAwMjE0MDkxODAwWhcNMzAwMjE0MDkxODAwWjA1MQswCQYDVQQGEwJVUzEQMA4GA1UEChMHRGF0YU9QUzEUMBIGA1UEAxMLbXkgcm9vdCBrZXkwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDyP749PqqIRwNSqaK6qr0Zsi03G4PTCUlgaYTPZuMrwUVPK8xX2dWWs9MPRMOdXpgr8aSTZnVfmelIlVz4D7o2vK5rfmAe9GPcK0WbwKwXyhFU0flS9sU/g46ogHFrk03SZxQAeJhMLfEmAJm8LF5HghtGDs3t4uwGsB95o+lqPLiBvxRB8ZS3jSpYpvPgXAuZWKdZUQ3UUZf0X3hGLp7uIcIwJ7i4MduOGaQEO4cePeEJy9aDAO6qV78YmHbyh9kaW+1DL/Sgq8NmTgHGV6UOnAPKHTnMKXl6KkyUz8uLBGIdVhPxrlzG1EzXresJbJenSZ+FZqm3oLqZbw54Yp5hAgMBAAGjcjBwMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFHISTOU/6BQqqnOZj+1xJfxpjiG0MAsGA1UdDwQEAwIBBjARBglghkgBhvhCAQEEBAMCAAcwHgYJYIZIAYb4QgENBBEWD3hjYSBjZXJ0aWZpY2F0ZTANBgkqhkiG9w0BAQsFAAOCAQEAj7HC8ObibwOLT4ZYmISJZwub9lcE0AZ5cWkPW39j/syhdbbqjK/6jy2D3WUEbR+s1Vson5Ov7JhN5In2yfZ/ByDvBnoj7CP8Q/ZMjTJgwN7j0rgmEb3CTZvnDPAz8Ijw3FP0cjxfoZ1Z0V2F44Ry7gtLJWr06+MztXVyto3aIz1/XbMQnXYlzc3c3B5yUQIy44Ce5aLRVsAjmXNqVRmDJ2QPNLicvrhnUJsO0zFWI+zZ2hc4Ge1RotCrjfOc9hQY63jZJ17myCZ6QCD7yzMzAob4vrgmkD4q7tpGrhPY/gDcE+lUNhC7DO3l0oPy2wsnT2TEn87eyWmDiTFG9zWDew==
 -----END CERTIFICATE-----

Looks like this. Allows you to immediately download the config file and generate it using a set of commands:

We fasten ActiveDirectory authorization to Kubernetes using Keycloak

Source: habr.com

Add a comment