DEV Community

Cover image for Argo CD GitLab Authentication: A Complete GitOps-Friendly Setup
Paweł Swiridow for u11d

Posted on Originally published at u11d.com

Argo CD GitLab Authentication: A Complete GitOps-Friendly Setup

Argo CD can use GitLab as an authentication provider, so users log in with their GitLab accounts instead of local Argo CD users. That gives you centralized identity, cleaner access control, and one less password database waiting to become a security incident with a logo.

The setup uses OIDC through Dex, stores the GitLab OAuth credentials as encrypted Kubernetes secrets with SOPS and age, and lets Argo CD decrypt them with KSOPS during reconciliation.

How authentication works

Argo CD does not talk to GitLab directly for user login. It uses Dex as an identity broker.

The flow looks like this:

User -> Argo CD -> Dex -> GitLab -> Dex -> Argo CD -> RBAC
Enter fullscreen mode Exit fullscreen mode

The short version:

  • The user opens Argo CD.
  • Argo CD redirects the user to Dex.
  • Dex redirects the user to GitLab.
  • GitLab authenticates the user and returns OIDC claims.
  • Argo CD reads those claims and applies RBAC rules.

Authentication tells Argo CD who the user is. RBAC tells Argo CD what damage they are allowed to do. Ideally, not much.

Create a GitLab OAuth application

First, create an OAuth application in GitLab.

In GitLab, go to your user, group, or admin application settings and create a new application with these values:

Name: Argo CD
Redirect URI: https://argocd.example.com/api/dex/callback
Scopes: openid, read_user
Enter fullscreen mode Exit fullscreen mode

GitLab gives you two important values:

Application ID
Secret
Enter fullscreen mode Exit fullscreen mode

In the Argo CD Dex config, these become:

dex.gitlab.clientID: "<gitlab-application-id>"
dex.gitlab.clientSecret: "<gitlab-application-secret>"
dex.gitlab.redirectURI: "https://argocd.example.com/api/dex/callback"
Enter fullscreen mode Exit fullscreen mode

Do not commit the secret in plain YAML. That is not GitOps. That is just leaving your keys under the doormat and calling it automation.

Encrypt the Argo CD secret with SOPS and age

Use SOPS with age to encrypt the Kubernetes secret before committing it.

Generate an age key if you do not have one yet:

age-keygen -o age.key
Enter fullscreen mode Exit fullscreen mode

Store the public key in .sops.yaml:

creation_rules:
  - encrypted_regex: '^(data|stringData)$'
    age: age1examplepublickeyreplacewithyourrealpublickey
Enter fullscreen mode Exit fullscreen mode

Create argocd-secret.yml:

apiVersion: v1
kind: Secret
metadata:
  name: argocd-secret
  namespace: argocd
type: Opaque
stringData:
  dex.gitlab.clientID: "<gitlab-application-id>"
  dex.gitlab.clientSecret: "<gitlab-application-secret>"
  dex.gitlab.redirectURI: "https://argocd.example.com/api/dex/callback"
Enter fullscreen mode Exit fullscreen mode

Encrypt it:

sops --encrypt argocd-secret.yml > argocd-secret.sops.yml
Enter fullscreen mode Exit fullscreen mode

Then remove the unencrypted file:

rm argocd-secret.yml
Enter fullscreen mode Exit fullscreen mode

Yes, really remove it. Git has a long memory and an even longer blame history.

Add KSOPS to the Argo CD repo server

Argo CD does not decrypt SOPS files by default. To make this work with Kustomize, install the KSOPS plugin in the Argo CD repo server.

The plugin lives here:

https://github.com/viaduct-ai/kustomize-sops
Enter fullscreen mode Exit fullscreen mode

If you deploy Argo CD with Helm, add this to your values:

repoServer:
  volumeMounts:
    - name: custom-tools
      mountPath: /usr/local/bin/kustomize
      subPath: kustomize
    - name: custom-tools
      mountPath: /usr/local/bin/ksops
      subPath: ksops
  volumes:
    - name: custom-tools
      emptyDir: {}
  initContainers:
    - name: install-ksops
      image: viaductoss/ksops:v4.5.1
      command:
        [
          "/usr/local/bin/ksops",
          "install",
          "--with-kustomize",
          "/custom-tools",
        ]
      volumeMounts:
        - mountPath: /custom-tools
          name: custom-tools
Enter fullscreen mode Exit fullscreen mode

This installs ksops and a Kustomize binary with plugin support into the repo server container.

Provide the age private key to Argo CD

The repo server needs the age private key to decrypt the SOPS file.

Create a Kubernetes secret from your age key:

kubectl -n argocd create secret generic sops-age-key \
  --from-literal=key="$(cat age.key)"
Enter fullscreen mode Exit fullscreen mode

Expose it to the repo server as SOPS_AGE_KEY:

repoServer:
  env:
    - name: SOPS_AGE_KEY
      valueFrom:
        secretKeyRef:
          name: sops-age-key
          key: key
  volumeMounts:
    - name: custom-tools
      mountPath: /usr/local/bin/kustomize
      subPath: kustomize
    - name: custom-tools
      mountPath: /usr/local/bin/ksops
      subPath: ksops
  volumes:
    - name: custom-tools
      emptyDir: {}
Enter fullscreen mode Exit fullscreen mode

Protect this key properly. Anyone with the private key can decrypt the secrets. Kubernetes secrets are base64 encoded, not magically protected by tiny YAML elves.

Create the KSOPS generator

Create secret-generator.yml:

apiVersion: viaduct.ai/v1
kind: ksops
metadata:
  name: argocd-secret-generator
  annotations:
    config.kubernetes.io/function: |
      exec:
        path: ksops
files:
  - argocd-secret.sops.yml
Enter fullscreen mode Exit fullscreen mode

This tells Kustomize to call ksops and decrypt argocd-secret.sops.yml during the build.

Reference the generator in Kustomize

In kustomization.yml, add the generator:

apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization

generators:
  - secret-generator.yml
Enter fullscreen mode Exit fullscreen mode

When Argo CD renders this application, Kustomize runs the KSOPS generator and produces the decrypted argocd-secret resource.

Configure Dex in Argo CD

Now configure Argo CD to use GitLab through Dex.

In Helm values, configure argocd-cm like this:

configs:
  cm:
    kustomize.buildOptions: "--enable-alpha-plugins --enable-exec"
    dex.config: |
      connectors:
        - type: gitlab
          id: gitlab
          name: GitLab
          config:
            baseURL: https://gitlab.example.com
            clientID: $dex.gitlab.clientID
            clientSecret: $dex.gitlab.clientSecret
Enter fullscreen mode Exit fullscreen mode

The $dex.gitlab.* values are loaded from argocd-secret. That is why the secret names must match exactly.

The Kustomize build options matter:

kustomize.buildOptions: "--enable-alpha-plugins --enable-exec"
Enter fullscreen mode Exit fullscreen mode

This allows Kustomize to execute the KSOPS plugin.

Do not enable exec plugins for repositories you do not trust. Otherwise, you have invented remote command execution with extra YAML. Security teams love that. Briefly.

Configure Argo CD RBAC