Kubernetes authentication with Keycloak and gangway

Kubernetes authentication with Keycloak and gangway

Kuberos is an OIDC authentication helper for kubectl loin.

VMware has depricated gangway at Jul 16, 2021.

Parts of the Kubernetes series

nano /etc/kubernetes/manifests/kube-apiserver.yaml
...
    command:
    - /hyperkube
    - apiserver
    - --advertise-address=10.10.40.30
...

    - --oidc-issuer-url=https://keycloak.devopstales.intra/auth/realms/mydomain
    - --oidc-client-id=k8s
    - --oidc-username-claim=email
    - --oidc-groups-claim=groups
...

systemctl restart docker kubelet
nano gangway.yaml
---
apiVersion: v1
kind: Namespace
metadata:
  name: gangway
---
apiVersion: v1
kind: Secret
metadata:
  name: gangway-key
  labels:
    app.kubernetes.io/name: gangway
type: Opaque
data:
  sessionkey: "ZTZNYlJUbDdHcHlSeXVFU0J6ZDZmbUs5Mks5a21NWEo="
---
apiVersion: v1
kind: ConfigMap
metadata:
  name: gangway
  namespace: gangway
data:
  gangway.yaml: |
    clusterName: "minikube"
    authorizeURL: "https://keycloak.devopstales.intra/auth/realms/mydomain/protocol/openid-connect/auth"
    tokenURL: "https://keycloak.devopstales.intra/auth/realms/mydomain/protocol/openid-connect/token"
    audience: "https://keycloak.devopstales.intra/auth/realms/mydomain/protocol/openid-connect/userinfo"
    # Used to specify the scope of the requested Oauth authorization.
    # scopes: ["openid", "profile", "email", "offline_access"]
    # scopes: ["groups"]
    # scopes: ["openid", "profile", "email", "offline_access", "groups"]
    # Where to redirect back to. This should be a URL where gangway is reachable.
    # Typically this also needs to be registered as part of the oauth application
    # with the oAuth provider.
    # Env var: GANGWAY_REDIRECT_URL
    redirectURL: "https://gangway.devopstales.intra/callback"
    clientID: "k8s"
    clientSecret: "43219919-0904-4338-bc0f-c986e1891a7a"
    # The JWT claim to use as the username. This is used in UI.
    # Default is "nickname". This is combined with the clusterName
    # for the "user" portion of the kubeconfig.
    # Env var: GANGWAY_USERNAME_CLAIM
    # usernameClaim: "sub"
    usernameClaim: "preferred_username"
    emailClaim: "email"
    # The API server endpoint used to configure kubectl
    # Env var: GANGWAY_APISERVER_URL
    # apiServerURL: "https://kube.codeformuenster.org:6443"
    apiServerURL: "https://192.168.0.106:8443"
    # The path to find the CA bundle for the API server. Used to configure kubectl.
    # This is typically mounted into the default location for workloads running on
    # a Kubernetes cluster and doesn't need to be set.
    # Env var: GANGWAY_CLUSTER_CA_PATH
    # cluster_ca_path: "/var/run/secrets/kubernetes.io/serviceaccount/ca.crt"
    # The path to a root CA to trust for self signed certificates at the Oauth2 URLs
    # Env var: GANGWAY_TRUSTED_CA_PATH
    # for self signd certificate:
    trustedCAPath: /gangway/rootca.crt
    # The path gangway uses to create urls (defaults to "")
    # Env var: GANGWAY_HTTP_PATH
    # httpPath: "https://${GANGWAY_HTTP_PATH}"
    # for self signd certificate:
  rootca.crt: |
    -----BEGIN CERTIFICATE-----
    MIIEATCCAumgAwIBAgIUXHOQsGW+UHpCphHViHSvyBny8BwwDQYJKoZIhvcNAQEL
    BQAwgY4xCzAJBgNVBAYTAkhVMQ0wCwYDVQQIDARQZXN0MREwDwYDVQQHDAhCdWRh
    cGVzdDETMBEGA1UECgwKTXkgQ29tcGFueTELMAkGA1UECwwCT1UxFzAVBgNVBAMM
    Dm15ZG9tYWluLmludHJhMSIwIAYJKoZIhvcNAQkBFhNyb290QG15ZG9tYWluLmlu
    dHJhMCAXDTE5MTIyNzE3MTk0OVoYDzIxMTkxMjAzMTcxOTQ5WjCBjjELMAkGA1UE
    BhMCSFUxDTALBgNVBAgMBFBlc3QxETAPBgNVBAcMCEJ1ZGFwZXN0MRMwEQYDVQQK
    DApNeSBDb21wYW55MQswCQYDVQQLDAJPVTEXMBUGA1UEAwwObXlkb21haW4uaW50
    cmExIjAgBgkqhkiG9w0BCQEWE3Jvb3RAbXlkb21haW4uaW50cmEwggEiMA0GCSqG
    SIb3DQEBAQUAA4IBDwAwggEKAoIBAQDevUZouW101hAu68qojvfmnC3fUIA9L5nj
    J+OTbgwDhxYduHVcmwFcrZJzBn/udm72sAUsjmoc34ZoEQVr1mCjrnDdb3NtOWBI
    XYmN7/RySzu5DSFLFv8Sj+27VvGLpYTXgDEt+IQpV4EgosX6DzjYK7BtmqaWCY3t
    aClGnzxEotlxMakTCt9eALD+l/ffV4NbiS6sPNaOFHbG8CKRnfzDzqh78qYaSH8d
    wWxGLGAvciNm1wv1G3NIkjMIZlkMqAv6uTzEtfOPQrHigG8sbb4hHAg8a9RtH2Sk
    nXjZRb3Wfo+XJ2eUCZyC6pwvZfEuZBuRAAo12Ycp/Ve2FC3kvpzLAgMBAAGjUzBR
    MB0GA1UdDgQWBBTRPLiCReojvBQXna2zkBBTsKdsnzAfBgNVHSMEGDAWgBTRPLiC
    ReojvBQXna2zkBBTsKdsnzAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUA
    A4IBAQBXp676KZ7VTPc/RPI+QIyb0ugPE+w6fkpw1PIzG+y0h53AsOu15tXxKX5L
    SwotjXoDuTnqQqLZ5wTFSiolscay+MpEDnoIdo+Pw7u3q3bpn6GmDjae1BaIL/En
    wvxvvJQsOJrXfEUQeC6M75i/MrYPSwhWNDAbqJTY2qEuRXcj/AALGrnlF5DEEd+O
    RYw79sj+xU88/kCOVWI35LwiH+/0QWFyKcPQvY8nER69nt5evFGqUQPE6qlOJKg/
    YD8dK+OF26Ta/qz0iKNAfh3WDgYU4lHAawKtwAbpBVBLlzLl+bD11BQvn6zDWVWA
    rfrwKUIO+dwSL3ZKS0kA0OlN3dyy
    -----END CERTIFICATE-----
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: gangway
  namespace: gangway
  labels:
    app: gangway
spec:
  replicas: 1
  selector:
    matchLabels:
      app: gangway
  strategy:
  template:
    metadata:
      labels:
        app: gangway
    spec:
      containers:
      - name: gangway
        image: gcr.io/heptio-images/gangway:v3.2.0
        command: ["gangway", "-config", "/gangway/gangway.yaml"]
        env:
        - name: GANGWAY_SESSION_SECURITY_KEY
          valueFrom:
            secretKeyRef:
              key: sessionkey
              name: gangway-key
        - name: GANGWAY_PORT
          value: "8080"
        ports:
        - name: http
          containerPort: 8080
          protocol: TCP
        resources:
          requests:
            cpu: "100m"
            memory: "128Mi"
          limits:
            cpu: "200m"
            memory: "512Mi"
        volumeMounts:
        - name: gangway
          mountPath: /gangway/
        livenessProbe:
          httpGet:
            path: /
            port: 8080
          initialDelaySeconds: 20
          timeoutSeconds: 1
          periodSeconds: 60
          failureThreshold: 3
        readinessProbe:
          httpGet:
            path: /
            port: 8080
          timeoutSeconds: 1
          periodSeconds: 10
          failureThreshold: 3
      volumes:
      - name: gangway
        configMap:
          name: gangway
---
kind: Service
apiVersion: v1
metadata:
  name: gangway
  namespace: gangway
  labels:
    app: gangway
spec:
  type: ClusterIP
  ports:
    - name: "http"
      protocol: TCP
      port: 80
      targetPort: "http"
  selector:
    app: gangway
---
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: gangway
  namespace: gangway
  annotations:
    ingress.kubernetes.io/rewrite-target: /
    cert-manager.io/cluster-issuer: ca-issuer
    ingress.kubernetes.io/force-ssl-redirect: "true"
    kubernetes.io/ingress.class: nginx
    nginx.ingress.kubernetes.io/proxy-buffer-size: "64k"
spec:
  rules:
  - host: gangway.devopstales.intra
    http:
      paths:
      - backend:
          serviceName: gangway
          servicePort: http
  tls:
  - secretName: gangway-tls
    hosts:
    - gangway.devopstales.intra

gangway

Below is an example of a ClusterRoleBinding that binds the role cluster-admin to the Keycloak group devops-team. (In my case it came from ldap) Create your own role bindings to fit your needs and apply them to the cluster.

nano devops-team_ClusterRoleBinding.yaml
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: cluster-admin-it-afdeling
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: cluster-admin
subjects:
  - apiGroup: rbac.authorization.k8s.io
    kind: Group
    name: devops-team
nano user_ClusterRoleBinding.yaml
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: devopstales-admin
subjects:
  - kind: User
    apiGroup: rbac.authorization.k8s.io
    name: devopstales
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: cluster-admin