We fasten ActiveDirectory authorization to Kubernetes with Keycloak

This article was written in order to expand an existing one , but talks about the features of the bundle with Microsoft ActiveDirectory, as well as complements it.

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

  • Keycloak is an open source project. Which provides a single entry point for applications. It works with many protocols, including the LDAP and OpenID that interest us.
  • Keycloak gatekeeper - reverse proxy application that allows you to integrate authorization through Keycloak.
  • Gangway - the application that generates a config for kubectl with which you can log in and connect to the Kubernetes API via OpenID

How rights work in Kubernetes.


We can manage user / group rights using RBAC, a lot 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 you need a user delivery mechanism in Kubernetes. To do this, we will add an OpenID provider to Kuberntes, which will say that such a user really exists, and Kubernetes itself will give rights to it.

Training

  • 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 / write the certificates, the client must be added to Kubernetes, for this 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

Install Keycloak


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

Install the repository and update it:

helm repo add codecentric https://codecentric.imtqy.com/helm-charts
helm repo update

Create the keycloak.yml file with the following contents:

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

In the left corner, click Add realm
KeyValue
Namekubernetes
Display nameKubernetes

Disable verification of user email confirmation:
Client scopes -> Email -> Mappers -> Email verified (Delete) We

configure the federation for importing users from ActiveDirectory, I will leave screenshots below, I think it will be more clear.

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

Federation setup



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

Next, we need to map our groups

User federation -> ldap_localhost -> Mappers -> Create

Mapper Creation


Client setup


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

Clients -> Create

Client setup


Create a scoupe for groups:

Client Scopes -> Create

Making scoupe


And configure mapper for them:

Client Scopes -> groups -> Mappers -> Create

Mapper


Add the mapping of our groups to Default Client Scopes:

Clients -> kubernetes -> Client Scopes -> Default Client Scopes
Select groups in Available Client Scopes , click Add selected

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

Clients -> kubernetes -> Credentials -> Secret
This is the end of the setup, but I got an error when after a successful authorization I got an error 403. Bug report .

Fix:

Client Scopes -> roles -> Mappers -> Create

Mapper


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();


Configure Kubernetes


We need to indicate 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:

kubeadm config
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
...


Configure 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, so it will also transmit information about you in the headers to the final application. Thus, if your application supports OpenID, then the user immediately logs in. Let's look at the example of Kubernetes Dashboard

Install 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 permissions:


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.imtqy.com/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 , you will be redirected to Keycloak and, if authorization is successful, we will already be logged into Dashboard.

Gangway installation


For convenience, you can add gangway which will generate a config file for kubectl, with which we will already 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-----


It looks something like this. Allows you to immediately download the config file and create it using a set of commands:


All Articles