Authentification dans Kubernetes avec GitHub OAuth et Dex

Je vous présente un tutoriel pour générer des accès à un cluster Kubernetes à l'aide de Dex, dex-k8s-authenticator et GitHub.

Authentification dans Kubernetes avec GitHub OAuth et Dex
Un mème local du chat en russe Kubernetes dans Telegram

Introduction

Nous utilisons Kubernetes pour créer des environnements dynamiques pour notre équipe de développement et de QA. Ainsi, nous souhaitons leur fournir un accès au cluster tant pour le tableau de bord que pour kubectl. Contrairement à OpenShift, Kubernetes vanilla n'a pas d'authentification native, c'est pourquoi nous utilisons des outils tiers.

Dans cette configuration, nous utilisons :

  • dex-k8s-authenticator  — une application web pour générer la configuration kubectl
  • Dex — un fournisseur OpenID Connect
  • GitHub — simplement parce que nous utilisons GitHub dans notre entreprise

Nous avons essayé d'utiliser Google OIDC, mais malheureusement nous n'avons pas réussi à les configurer avec des groupes, donc l'intégration avec GitHub nous convient parfaitement. Sans le mapping des groupes, il est impossible de créer des politiques RBAC basées sur des groupes.

Alors, comment fonctionne notre processus d'autorisation dans Kubernetes de manière visuelle :

Authentification dans Kubernetes avec GitHub OAuth et Dex
Le processus d'autorisation

Un peu plus en détail et en points :

  1. L'utilisateur se connecte à dex-k8s-authenticator (login.k8s.example.com)
  2. dex-k8s-authenticator redirige la requête vers Dex (dex.k8s.example.com)
  3. Dex redirige vers la page d'autorisation sur GitHub
  4. GitHub génère les informations nécessaires à l'autorisation et les renvoie à Dex
  5. Dex transmet les informations reçues à dex-k8s-authenticator
  6. L'utilisateur reçoit un token OIDC de GitHub
  7. dex-k8s-authenticator ajoute le token au kubeconfig
  8. kubectl transmet le token au KubeAPIServer
  9. KubeAPIServer retourne les autorisations à kubectl sur la base du token transmis
  10. L'utilisateur reçoit les autorisations de kubectl

Actions préparatoires

Bien sûr, nous avons déjà un cluster Kubernetes (k8s.example.com), ainsi que HELM préinstallé. Nous avons aussi une organisation dans GitHub (super-org).
Si vous n'avez pas HELM, son installation est très simple.

Tout d'abord, nous devons configurer GitHub.

Allons sur la page des paramètres de l'organisation, (https://github.com/organizations/super-org/settings/applications) et créons une nouvelle application (Application OAuth autorisée) :
Authentification dans Kubernetes avec GitHub OAuth et Dex
Création d'une nouvelle application sur GitHub

Remplissons les champs avec les URL nécessaires, par exemple :

  • URL de la page d'accueil : https://dex.k8s.example.com
  • URL de retour d'autorisation : https://dex.k8s.example.com/callback

Faites attention aux liens, il est important de ne pas perdre les slashs.

En réponse au formulaire rempli, GitHub générera ID Client et Client secret, conservez-les dans un endroit sûr, ils nous seront utiles (nous par exemple utilisons Vault pour stocker les secrets) :

Client ID : 1ab2c3d4e5f6g7h8
Client secret : 98z76y54x32w1

Préparez les enregistrements DNS pour les sous-domaines login.k8s.example.com et dex.k8s.example.com, ainsi que les certificats SSL pour les ingress.

Créons des certificats 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

Le ClusterIssuer nommé le-clusterissuer devrait déjà exister, sinon nous allons le créer avec 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

Configuration du KubeAPIServer

Pour que le kubeAPIServer fonctionne, il est nécessaire de configurer OIDC et de mettre à jour le cluster :

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

Nous utilisons kops pour le déploiement de clusters, mais cela fonctionne de la même manière pour d'autres gestionnaires de clusters.

Configuration de Dex et dex-k8s-authenticator

Pour faire fonctionner Dex, il est nécessaire d'avoir un certificat et une clé du maître Kubernetes, nous allons les extraire :

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

Clonons le dépôt dex-k8s-authenticator :

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

Avec des fichiers de valeurs, nous pouvons ajuster de manière flexible les variables pour nos chart HELM.

Décrivons la configuration pour 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

Et pour 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

Installons Dex et 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

Vérifions le fonctionnement des services (Dex doit retourner le code 400, et dex-k8s-authenticator — le code 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

Configuration RBAC

Créons un ClusterRole pour le groupe, dans notre cas avec des accès en lecture seule :

cat << EOF | kubectl create -f -
apiVersion: 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

Créons la configuration pour ClusterRoleBinding :

cat <<EOF | kubectl create -f -
apiVersion: 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

Nous sommes maintenant prêts pour les tests.

Tests

Allons sur la page de connexion (https://login.k8s.example.com) et connectons-nous avec un compte GitHub :

Authentification dans Kubernetes avec GitHub OAuth et Dex
Page d'autorisation

Authentification dans Kubernetes avec GitHub OAuth et Dex
Page d'autorisation redirigée vers GitHub

Authentification dans Kubernetes avec GitHub OAuth et Dex
 Suivez les instructions générées pour obtenir les accès

Après avoir copié-collé depuis la page web, nous pouvons utiliser kubectl pour gérer les ressources de notre cluster :

kubectl get po
NAME                READY   STATUS    RESTARTS   AGE
mypod               1/1     Running   0          3d

kubectl delete po mypod
Error from server (Forbidden): pods "mypod" is forbidden: User "amet@example.com" cannot delete pods in the namespace "default"

Et cela fonctionne, tous les utilisateurs GitHub dans notre organisation peuvent voir les ressources et accéder aux pods, mais ils n'ont pas les droits de les modifier.

Source : habr.com

Acheter un hébergement fiable pour les sites avec protection DDoS, serveurs VPS VDS 🔥 Acheter un hébergement fiable pour les sites avec protection DDoS, serveurs VPS VDS | ProHoster