Autenticación en Kubernetes con GitHub OAuth y Dex

Les presento un tutorial para generar accesos a un clúster de Kubernetes utilizando Dex, dex-k8s-authenticator y GitHub.

Autenticación en Kubernetes con GitHub OAuth y Dex
Un meme local del chat de Kubernetes en ruso Telegram

Introducción

Usamos Kubernetes para crear entornos dinámicos para el equipo de desarrolladores y QA. De esta manera, queremos proporcionarles acceso al clúster tanto para el panel como para kubectl. A diferencia de OpenShift, Kubernetes 'vanilla' no tiene autenticación nativa, por lo que utilizamos herramientas de terceros para esto.

En esta configuración usamos:

  • dex-k8s-authenticator  — una aplicación web para generar la configuración de kubectl
  • Dex — proveedor OpenID Connect
  • GitHub — simplemente porque usamos GitHub en nuestra empresa

Intentamos usar Google OIDC, pero desafortunadamente no pudimos configurarlos con grupos, por lo que la integración con GitHub nos satisfizo. Sin el mapeo de grupos no es posible crear políticas RBAC basadas en grupos.

Entonces, ¿cómo funciona nuestro proceso de autorización en Kubernetes de manera visual?

Autenticación en Kubernetes con GitHub OAuth y Dex
Proceso de autorización

Un poco más de detalles, punto por punto:

  1. El usuario inicia sesión en dex-k8s-authenticator (login.k8s.example.com)
  2. dex-k8s-authenticator redirige la solicitud a Dex (dex.k8s.example.com)
  3. Dex redirige a la página de autorización en GitHub
  4. GitHub genera la información necesaria sobre la autorización y la devuelve a Dex
  5. Dex pasa la información obtenida a dex-k8s-authenticator
  6. El usuario recibe un token OIDC de GitHub
  7. dex-k8s-authenticator añade el token al kubeconfig
  8. kubectl envía el token a KubeAPIServer
  9. KubeAPIServer, basándose en el token enviado, devuelve accesos a kubectl
  10. El usuario recibe accesos de kubectl

Acciones preparatorias

Por supuesto, ya tenemos un clúster de Kubernetes (k8s.example.com), así como HELM preinstalado. También tenemos una organización en GitHub (super-org).
Si no tiene HELM, se instala muy fácilmente.

Primero necesitamos configurar GitHub.

Vamos a la página de configuración de la organización, (https://github.com/organizations/super-org/settings/applications) y creamos una nueva aplicación (Aplicación OAuth Autorizada):
Autenticación en Kubernetes con GitHub OAuth y Dex
Creación de una nueva aplicación en GitHub

Completemos los campos con las URL necesarias, por ejemplo:

  • Homepage URL: https://dex.k8s.example.com
  • Authorization callback URL: https://dex.k8s.example.com/callback

Tenga cuidado con los enlaces, es importante no perder las barras.

En respuesta al formulario completo, GitHub generará Client ID y Client secret, guárdelos en un lugar seguro, los necesitaremos (por ejemplo, usamos Vault para el almacenamiento de secretos):

Client ID: 1ab2c3d4e5f6g7h8
Client secret: 98z76y54x32w1

Prepara los registros DNS para los subdominios login.k8s.example.com y dex.k8s.example.com, así como los certificados SSL para los ingresos.

Crearemos los certificados SSL:

cat <<EOF | kubectl create -f -
apiVersion: certmanager.k8s.io/v1alpha1
kind: Certificate
metadata:
  name: cert-auth-dex
  namespace: kube-system
spec:
  secretName: cert-auth-dex
  dnsNames:
    - dex.k8s.example.com
  acme:
    config:
    - http01:
        ingressClass: nginx
      domains:
      - dex.k8s.example.com
  issuerRef:
    name: le-clusterissuer
    kind: ClusterIssuer
---
apiVersion: certmanager.k8s.io/v1alpha1
kind: Certificate
metadata:
  name: cert-auth-login
  namespace: kube-system
spec:
  secretName: cert-auth-login
  dnsNames:
    - login.k8s.example.com
  acme:
    config:
    - http01:
        ingressClass: nginx
      domains:
      - login.k8s.example.com
  issuerRef:
    name: le-clusterissuer
    kind: ClusterIssuer
EOF
kubectl describe certificates cert-auth-dex -n kube-system
kubectl describe certificates cert-auth-login -n kube-system

El ClusterIssuer con el nombre le-clusterissuer debe existir ya, si no existe, lo crearemos usando HELM:

helm install --namespace kube-system -n cert-manager stable/cert-manager
cat << EOF | kubectl create -f -
apiVersion: certmanager.k8s.io/v1alpha1
kind: ClusterIssuer
metadata:
  name: le-clusterissuer
  namespace: kube-system
spec:
  acme:
    server: https://acme-v02.api.letsencrypt.org/directory
    email: k8s-admin@example.com
    privateKeySecretRef:
      name: le-clusterissuer
    http01: {}
EOF

Configuración de KubeAPIServer

Para que kubeAPIServer funcione, es necesario configurar OIDC y actualizar el clúster:

kops edit cluster
...
  kubeAPIServer:
    anonymousAuth: false
    authorizationMode: RBAC
    oidcClientID: dex-k8s-authenticator
    oidcGroupsClaim: groups
    oidcIssuerURL: https://dex.k8s.example.com/
    oidcUsernameClaim: email
kops update cluster --yes
kops rolling-update cluster --yes

Utilizamos kops para el despliegue de clústeres, pero esto funciona de manera similar para otros administradores de clústeres.

Configuración de Dex y dex-k8s-authenticator

Para que Dex funcione, es necesario tener un certificado y una clave del maestro de Kubernetes, lo extraeremos de allí:

sudo cat /srv/kubernetes/ca.{crt,key}
-----BEGIN CERTIFICATE-----
AAAAAAAAAAABBBBBBBBBBCCCCCC
-----END CERTIFICATE-----
-----BEGIN RSA PRIVATE KEY-----
DDDDDDDDDDDEEEEEEEEEEFFFFFF
-----END RSA PRIVATE KEY-----

Clonaremos el repositorio dex-k8s-authenticator:

git clone git@github.com:mintel/dex-k8s-authenticator.git
cd dex-k8s-authenticator/

Con los archivos values podemos ajustar de manera flexible las variables para nuestros gráficos HELM.

Describiremos la configuración para Dex:

cat < values-dex.yml
global:
  deployEnv: prod
tls:
  certificate: |-
    -----BEGIN CERTIFICATE-----
    AAAAAAAAAAABBBBBBBBBBCCCCCC
    -----END CERTIFICATE-----
  key: |-
    -----BEGIN RSA PRIVATE KEY-----
    DDDDDDDDDDDEEEEEEEEEEFFFFFF
    -----END RSA PRIVATE KEY-----
ingress:
  enabled: true
  annotations:
    kubernetes.io/ingress.class: nginx
    kubernetes.io/tls-acme: "true"
  path: /
  hosts:
    - dex.k8s.example.com
  tls:
    - secretName: cert-auth-dex
      hosts:
        - dex.k8s.example.com
serviceAccount:
  create: true
  name: dex-auth-sa
config: |
  issuer: https://dex.k8s.example.com/
  storage: # https://github.com/dexidp/dex/issues/798
    type: sqlite3
    config:
      file: /var/dex.db
  web:
    http: 0.0.0.0:5556
  frontend:
    theme: "coreos"
    issuer: "Example Co"
    issuerUrl: "https://example.com"
    logoUrl: https://example.com/images/logo-250x25.png
  expiry:
    signingKeys: "6h"
    idTokens: "24h"
  logger:
    level: debug
    format: json
  oauth2:
    responseTypes: ["code", "token", "id_token"]
    skipApprovalScreen: true
  connectors:
  - type: github
    id: github
    name: GitHub
    config:
      clientID: $GITHUB_CLIENT_ID
      clientSecret: $GITHUB_CLIENT_SECRET
      redirectURI: https://dex.k8s.example.com/callback
      orgs:
      - name: super-org
        teams:
        - team-red
  staticClients:
  - id: dex-k8s-authenticator
    name: dex-k8s-authenticator
    secret: generatedLongRandomPhrase
    redirectURIs:
      - https://login.k8s.example.com/callback/
envSecrets:
  GITHUB_CLIENT_ID: "1ab2c3d4e5f6g7h8"
  GITHUB_CLIENT_SECRET: "98z76y54x32w1"
EOF

Y para dex-k8s-authenticator:

cat < values-auth.yml
global:
  deployEnv: prod
dexK8sAuthenticator:
  clusters:
  - name: k8s.example.com
    short_description: "k8s cluster"
    description: "Kubernetes cluster"
    issuer: https://dex.k8s.example.com/
    k8s_master_uri: https://api.k8s.example.com
    client_id: dex-k8s-authenticator
    client_secret: generatedLongRandomPhrase
    redirect_uri: https://login.k8s.example.com/callback/
    k8s_ca_pem: |
      -----BEGIN CERTIFICATE-----
      AAAAAAAAAAABBBBBBBBBBCCCCCC
      -----END CERTIFICATE-----
ingress:
  enabled: true
  annotations:
    kubernetes.io/ingress.class: nginx
    kubernetes.io/tls-acme: "true"
  path: /
  hosts:
    - login.k8s.example.com
  tls:
    - secretName: cert-auth-login
      hosts:
        - login.k8s.example.com
EOF

Instalemos Dex y dex-k8s-authenticator:

helm install -n dex --namespace kube-system --values values-dex.yml charts/dex
helm install -n dex-auth --namespace kube-system --values values-auth.yml charts/dex-k8s-authenticator

Verifiquemos el funcionamiento de los servicios (Dex debería devolver el código 400, mientras que dex-k8s-authenticator debería devolver el código 200):

curl -sI https://dex.k8s.example.com/callback | head -1
HTTP/2 400
curl -sI https://login.k8s.example.com/ | head -1
HTTP/2 200

Configuración de RBAC

Creamos un ClusterRole para el grupo, en nuestro caso con accesos de solo lectura:

cat << EOF | kubectl create -f -
apiversión: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: cluster-read-all
rules:
  -
    apiGroups:
      - ""
      - apps
      - autoscaling
      - batch
      - extensions
      - policy
      - rbac.authorization.k8s.io
      - storage.k8s.io
    resources:
      - componentstatuses
      - configmaps
      - cronjobs
      - daemonsets
      - deployments
      - events
      - endpoints
      - horizontalpodautoscalers
      - ingress
      - ingresses
      - jobs
      - limitranges
      - namespaces
      - nodes
      - pods
      - pods/log
      - pods/exec
      - persistentvolumes
      - persistentvolumeclaims
      - resourcequotas
      - replicasets
      - replicationcontrollers
      - serviceaccounts
      - services
      - statefulsets
      - storageclasses
      - clusterroles
      - roles
    verbs:
      - get
      - watch
      - list
  - nonResourceURLs: ["*"]
    verbs:
      - get
      - watch
      - list
  - apiGroups: [""]
    resources: ["pods/exec"]
    verbs: ["create"]
EOF

Vamos a crear una configuración para ClusterRoleBinding:

cat <<EOF | kubectl create -f -
apiversión: rbac.authorization.k8s.io/v1beta1
kind: ClusterRoleBinding
metadata:
  name: dex-cluster-auth
  namespace: kube-system
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: cluster-read-all
subjects:
  kind: Group
  name: "super-org:team-red"
EOF

Ahora estamos listos para probar.

Pruebas

Vamos a la página de inicio de sesión (https://login.k8s.example.com) y nos autenticamos con la cuenta de GitHub:

Autenticación en Kubernetes con GitHub OAuth y Dex
Página de autenticación

Autenticación en Kubernetes con GitHub OAuth y Dex
Página de autenticación redirigida a GitHub

Autenticación en Kubernetes con GitHub OAuth y Dex
 Sigamos las instrucciones generadas para obtener los accesos

Después de copiar y pegar desde la página web, podemos usar kubectl para gestionar los recursos de nuestro clúster:

kubectl get po
NOMBRE             LISTO   ESTADO    REINICIOS   EDAD
mypod              1/1     Corriendo 0           3d

kubectl delete po mypod
Error del servidor (Prohibido): pods "mypod" está prohibido: El usuario "amet@example.com" no puede eliminar pods en el namespace "default"

Y eso funciona, todos los usuarios de GitHub en nuestra organización pueden ver los recursos y entrar en los pods, sin embargo, no tienen derechos para modificarlos.

Fuente: habr.com

Compra un hosting fiable para sitios web con protección contra DDoS, servidores VPS VDS 🔥 Compra un hosting fiable para sitios web con protección contra DDoS, servidores VPS VDS | ProHoster