Using Dex for authentication

Dex is an identity service that uses OpenID Connect to drive authentication for other apps.

Dex acts as a portal to other identity providers through “connectors.” This lets Dex defer authentication to LDAP servers, SAML providers, or established identity providers like GitHub, Google, and Active Directory. Clients write their authentication logic once to talk to Dex, then Dex handles the protocols for a given backend.

This section shows you how to set up GitHub authentication using Service Mesh Manager. To set up other authentication backends such as Active Directory or LDAP, see the DEX Connectors documentation.

Enable GitHub authentication

As GitHub is an OAuth 2 provider, Service Mesh Manager requires a bridge between OAuth 2 (or any other authentication backend) and OIDC.

Prerequisites

Before starting to set up GitHub authentication to Service Mesh Manager, make sure that you have already:

You need the following information to follow this guide:

  • GITHUB_CLIENT_ID: The Client ID from the GitHub OAuth 2 registration.
  • GITHUB_CLIENT_SECRET: The Client Secret from the GitHub OAuth 2 registration.
  • GITHUB_ORG_NAME: The name of the GitHub organization to authenticate against. If you want to support multiple organizations, consult the Dex manual.
  • GITHUB_ADMIN_TEAM_NAME: The name of the GitHub team that contains the users who receive administrative privileges.
  • DEX_EXTERNAL_URL: The URL where Dex will be exposed. This must be separate from the dashboard URL.
  • SMM_DASHBOARD_URL: The URL where the Service Mesh Manager dashboard is exposed.
  • OIDC_CLIENT_SECRET: The secret to be used between Dex and the Service Mesh Manager authentication backend (can be any random string).

To follow the examples, export these values as environment variables from your terminal, as these will be needed in multiple steps:

export GITHUB_CLIENT_ID=<***>
export GITHUB_CLIENT_SECRET=<***>
export GITHUB_ORG_NAME=my-github-org
export GITHUB_ADMIN_TEAM_NAME=admin
export DEX_EXTERNAL_URL=dex.example.org
export SMM_DASHBOARD_URL=smm.example.org
export OIDC_CLIENT_SECRET=$(openssl rand -base64 32) # or any random string

Create namespace for Dex

Dex will be installed into its own namespace for isolation. Create the namespace for it:

kubectl create ns dex

Dex will be exposed externally using Istio, so enable Istio sidecar injection on the namespace:

kubectl label ns dex istio.io/rev=cp-v113x.istio-system

Create MeshGateway for Dex

GitHub needs to access Dex to invoke the OAuth 2 callback, so that Dex can understand what was the result of the authentication on the GitHub side.

Create an externally available MeshGateway:

cat > dex-meshgateway.yaml <<EOF
apiVersion: servicemesh.cisco.com/v1alpha1
kind: IstioMeshGateway
metadata:
    labels:
        app.kubernetes.io/instance: dex
        app.kubernetes.io/name: dex-ingress
    name: dex-ingress
    namespace: dex
spec:
    istioControlPlane:
        name: cp-v113x
        namespace: istio-system
    deployment:
      metadata:
        labels:
          app.kubernetes.io/instance: dex
          app.kubernetes.io/name: dex-ingress
          gateway-name: dex-ingress
          gateway-type: ingress
      replicas:
        max: 1
        min: 1
        count: 1
    service:
      metadata:
        annotations:
          external-dns.alpha.kubernetes.io/hostname: ${DEX_EXTERNAL_URL}.
      ports:
      - name: http2
        port: 80
        protocol: TCP
        targetPort: 8080
      - name: https
        port: 443
        protocol: TCP
        targetPort: 8443
      type: LoadBalancer
    type: ingress
---
apiVersion: networking.istio.io/v1beta1
kind: Gateway
metadata:
  labels:
    app.kubernetes.io/instance: dex
    app.kubernetes.io/name: dex-ingress
  name: dex-ingress
  namespace: dex
spec:
  selector:
    app.kubernetes.io/instance: dex
    app.kubernetes.io/name: dex-ingress
  servers:
  - hosts:
    - '*'
    port:
      name: http
      number: 80
      protocol: HTTP
  - hosts:
    - '*'
    port:
      name: https
      number: 443
      protocol: HTTPS
    tls:
      credentialName: dex-ingress-tls
      mode: SIMPLE
---
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
  labels:
    app.kubernetes.io/instance: dex
    app.kubernetes.io/name: dex-ingress
  name: dex-ingress
  namespace: dex
spec:
  gateways:
    - dex-ingress
  hosts:
    - '*'
  http:
  - match:
    - uri:
        prefix: /
    route:
    - destination:
        host: dex
        port:
          number: 80
EOF
kubectl apply -f dex-meshgateway.yaml

Get certificates for Dex

The secret referenced in the MeshGateway resource is not yet available. To secure the communication between the end-user’s browser and your Dex installation, enable the Let’s Encrypt support for gateways in Service Mesh Manager:

cat > certs.yaml <<EOF
---
apiVersion: cert-manager.io/v1
kind: Issuer
metadata:
  name: dex-issuer
  namespace: dex
spec:
  acme:
    email: noreply@cisco.com
    preferredChain: ""
    privateKeySecretRef:
      name: smm-letsencrypt-issuer
    server: https://acme-v02.api.letsencrypt.org/directory
    solvers:
    - http01:
        ingress:
          class: nginx
---
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
  name: dex-tls
  namespace: dex
  annotations:
    acme.smm.cisco.com/gateway-selector: |
      {
        "app.kubernetes.io/instance": "dex",
        "app.kubernetes.io/name": "dex-ingress"
      }      
spec:
  dnsNames:
  - ${DEX_EXTERNAL_URL}
  duration: 2160h0m0s
  issuerRef:
    group: cert-manager.io
    kind: Issuer
    name: dex-issuer
  privateKey:
    algorithm: RSA
    encoding: PKCS1
    size: 2048
  renewBefore: 360h0m0s
  secretName: dex-ingress-tls
  usages:
  - server auth
  - client auth
EOF
kubectl apply -f certs.yaml

After executing the previous commands, check that the Certificate has been successfully issued by running the kubectl get certificate command. The output should be similar to:

NAME      READY   SECRET            AGE
dex-tls   True    dex-ingress-tls   24h

If the READY column shows True, then the Certificate has been issued. If not, refer to the Cert Manager documentation for troubleshooting the issue.

Provision Dex

Now you can install Dex onto the namespace using helm. First create a file called dex-values.yaml for the Dex installation:

cat > dex-values.yaml <<EOF
---
config:
  issuer: https://${DEX_EXTERNAL_URL}
  storage:
    type: kubernetes
    config:
      inCluster: true

  connectors:
    - type: github
      id: github
      name: GitHub
      config:
        clientID: $GITHUB_CLIENT_ID
        clientSecret: "$GITHUB_CLIENT_SECRET"
        redirectURI: https://${DEX_EXTERNAL_URL}/callback
        orgs:
        - name: $GITHUB_ORG_NAME
        loadAllGroups: true

  oauth2:
    skipApprovalScreen: true

  staticClients:
    - id: smm-app
      redirectURIs:
        - "https://${SMM_DASHBOARD_URL}/auth/callback"
      name: 'Cisco Service Mesh Manager'
      secret: ${OIDC_CLIENT_SECRET}

service:
  enabled: true
  ports:
    http:
      port: 80

    https:
      port: 443
EOF

Run the following commands to install Dex using these values:

helm repo add dex https://charts.dexidp.io
helm install -n dex dex -f dex-values.yaml dex/dex

Verify that Dex has started successfully by running the kubectl get pods -n dex command. The output should be similar to:

NAME                           READY   STATUS    RESTARTS   AGE
dex-6d879bb86d-pxtvm           2/2     Running   1          20m
dex-ingress-6885b4f747-c5l96   1/1     Running   0          24m

Configure SMM to use OIDC provider

Enable Dex as an OIDC provider to Service Mesh Manager by patching the ControlPlane resource:

cat > smm-oidc-enable.yaml <<EOF
spec:
  smm:
    auth:
      oidc:
        enabled: true
        client:
          id: smm-app
          issuerURL: https://${DEX_EXTERNAL_URL}
          secret: ${OIDC_CLIENT_SECRET}
        groups:
          claim: groups
          prefix: 'oidc:'
        username:
          claim: email
          prefix: 'oidc:'
EOF
kubectl patch --type=merge --patch "$(cat smm-oidc-enable.yaml )" controlplane smm
  • If you are using Service Mesh Manager in Operator Mode, then the Istio deployment is updated automatically.
  • If you are using the imperative mode, run the smm operator reconcile command to apply the changes.

Create user mapping

After logging in, the users will be mapped to have the:

  • Username of oidc:<email-of-the-github-user>, and the
  • groups of oidc:$$GITHUB_ORG_NAME:<team-name> for each of the GitHub Teams the user is a member of.

By default, these users and groups cannot modify the resources in the target cluster, so you need to create the ClusterRoleBinding right for these Groups or Users. For example, to grant administrative access to the users in the $GITHUB_ADMIN_TEAM_NAME GitHub Team, run the following command:

cat > allow-admin-access.yaml <<EOF
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: oidc-admin-access
subjects:
- kind: Group
  name: 'oidc:$GITHUB_ORG_NAME:$GITHUB_ADMIN_TEAM_NAME'
  apiGroup: rbac.authorization.k8s.io
roleRef:
  kind: ClusterRole
  name: cluster-admin
  apiGroup: rbac.authorization.k8s.io
EOF
kubectl apply -f allow-admin-access.yaml

The groups a given user belongs to is shown in the right hand menu on the user interface:

Menu with group information Menu with group information

In this example, the username is oidc:test@example.org and the user belongs to only one group, called oidc:example-org:test.

Verify login

To test that the login works, navigate to the URL where the Service Mesh Manager dashboard is exposed ($SMM_DASHBOARD_URL), and select Sign in with OIDC.

Login using OIDC Login using OIDC