goauthentik / authentik

The authentication glue you need.
https://goauthentik.io
Other
13.71k stars 917 forks source link

Allow specifying outpost AUTHENTIK_TOKEN using blueprints #9711

Open pixil98 opened 6 months ago

pixil98 commented 6 months ago

Is your feature request related to a problem? Please describe. I'm trying to deploy an LDAP outpost using blueprints. One of the steps is to copy the auto generated token from the outpost in Authentic into the deployment of the outpost container. This prevents me from automating deployment of clusters using LDAP.

Describe the solution you'd like I would like to be able to specify the AUTHENTIK_TOKEN to use as part of the blueprint.

Describe alternatives you've considered An admin user can get the token via the web apis. In my case, I could probably use this to poll it via a kubernetes job and write it to a secret that is used for the outpost's envvars. It feels pretty messy.

Salvoxia commented 4 months ago

I want to achieve the same and opted for a version of the described alternative. I'm deploying Authentik in Kubernetes with the officiel Helm Chart, but wrote a separate deployment to deploy the LDAP outpost. In Authentik everything for LDAP including the Outpost gets set up using Blueprints, I just need to get the Outpost Token.
My outpost deployment contains an InitContainer with a shell script that uses the AUTHENTIK_BOOTSTRAP_TOKEN to query the API for the Outpost Token and passes it along the Outpost container in a file. Not great but imho not completely terrible either.

If anyone is interested, this is the shell script:

#!/usr/bin/env sh
# This script expects the following environment variables:
# AUTHENTIK_API_TOKEN - An API token to authenticate to the Authentik API with
# AUTHTENTIK_HOST - The base URL of the Authentik API
# OUTPOST_NAME - Exact name of the Outpost to fetch the token for
# AUTHENTIK_INSECURE - Whether to skip verification of Authentik API SSL certificate

inscureArg=""
if [ "${AUTHENTIK_INSECURE}" = "true" ]; then
  insecureArg="--insecure"
fi

# Fetch outpost instances from API
outpostInstances=$(curl ${insecureArg} -s --get --data-urlencode "name__iexact=${OUTPOST_NAME}" -L "${AUTHENTIK_HOST}/api/v3/outposts/instances/" -H 'Accept: application/json' -H "Authorization: Bearer ${AUTHENTIK_API_TOKEN}")

# Check if we found the outpost
# Since the search was for the exact name, only 0 or 1 results should ever be returned
numberOfResults=$(echo "${outpostInstances}" | jq -r '.results | length')

if [ ${numberOfResults} -eq 0 ]; then
  echo "Outpost with name ${OUTPOST_NAME} not found, aborting..."
  exit 1
fi
# Extract the token identifier
tokenIdentifier=$(echo  "${outpostInstances}" | jq -r --arg outpost_name "${OUTPOST_NAME}" '.results[] | select(.name == $outpost_name) | .token_identifier')
# Sanity check, should not happen
if [ -z "${tokenIdentifier}" ]; then
  echo "Token identifier for outpost ${OUTPOST_NAME} not found, aborting..."
  exit 1
fi

# Fetch the token
viewKeyResult=$(curl ${insecureArg} -s -L "${AUTHENTIK_HOST}/api/v3/core/tokens/${tokenIdentifier}/view_key/" -H 'Accept: application/json' -H "Authorization: Bearer ${AUTHENTIK_API_TOKEN}")
outpostToken=$(echo "${viewKeyResult}" | jq -r '.key')
# Sanity check
if [ -z "${outpostToken}" ]; then
  echo "Token for outpost ${OUTPOST_NAME} not found, aborting..."
  exit 1
fi

touch /token/token
echo $outpostToken > /token/token

And this is the deployment:

apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app.kubernetes.io/name: authentik
    app.kubernetes.io/component: authentik-outpost-ldap
  name: authentik-outpost-ldap
spec:
  replicas: 2
  strategy:
    rollingUpdate:
      maxUnavailable: 1
  selector:
    matchLabels:
      app.kubernetes.io/name: authentik
      app.kubernetes.io/component: authentik-outpost-ldap
  template:
    metadata:
      labels:
        app.kubernetes.io/name: authentik
        app.kubernetes.io/component: authentik-outpost-ldap
    spec:
      volumes:
        - name: token
          emptyDir: {}
        - name: outpost-token-script
          configMap:
            defaultMode: 0700
            name: outpost-token-script
      initContainers:
        - name: outpost-token
          image: alpine
          command: ["sh", "-c", "apk update && apk upgrade && apk add --no-cache curl jq && /token-script/getOutpostToken.sh"]
          volumeMounts:
            - name: token
              mountPath: /token
            - name: outpost-token-script
              mountPath: /token-script
              readOnly: true
          envFrom:
            - configMapRef:
                name: authentik-outpost-ldap
            - secretRef:
                name: authentik-outpost-ldap
      containers:
        - name: ldap
          image: ghcr.io/goauthentik/ldap:2024.6.0
          envFrom:
            - configMapRef:
                name: authentik-outpost-ldap
          env:
            - name: AUTHENTIK_TOKEN
              value: file:///token/token
          volumeMounts:
            - name: token
              mountPath: /token
          ports:
            - containerPort: 3389
              name: ldap
              protocol: TCP
            - containerPort: 6636
              name: ldaps
              protocol: TCP
BeryJu commented 2 months ago

Due to the way the token is created for outposts it's not easy to add the ability to override that when it's coming from a blueprint. This will probably be something more easily possible with the K8s operator #5675

Salvoxia commented 2 months ago

Thank you for your reply! I actually stumbled across #3150 (same use case) and #5675, but since the latter was already pretty old at that point I was hoping a simpler solution might be quicker. Looking forward to the operator though!