6 min read

Mastering Kubernetes:Securing Your K3s Cluster with Authentik Because "Open to Everyone" Is Not a Security Policy

Mastering Kubernetes:Securing Your K3s Cluster with Authentik Because "Open to Everyone" Is Not a Security Policy
Photo by Growtika / Unsplash

If you've been following along with this series, congratulations! Your cluster is likely up, running, and accessible from anywhere with an internet connection. While that's undeniably cool and convenient, it's also a little too convenient. Your cluster is now as accessible as a street-side lemonade stand—except the customers in this case might be script kiddies or opportunistic bots. To quote Monica and Rachel’s infamous apartment door from Friends: "Come on in! No locks here!"

Clearly, this isn’t what we want. The idea of every endpoint being open to the entire internet feels about as safe as storing your password on a sticky note attached to your monitor (don’t do that, by the way). That’s where authentication and authorization come in. But here’s the catch: some tools come with no authentication at all, while others offer authentication that feels like it was designed by someone who learned about security from a YouTube tutorial.

But fear not! Today, we’ll walk through how to secure your homelab with Authentik—our chosen superhero in the world of authentication and authorization. It’s a bit of effort upfront, but I promise it’s worth it. By the end of this post, you’ll have a bulletproof setup that can guard your cluster like Gandalf shouting, "You shall not pass!"


Picking the Right Tool for the Job

Before I start shoving YAML files in your face, let’s take a moment to talk about why I picked Authentik for this tutorial. Like most things in tech (and life), this decision involves trade-offs. Here’s the quick breakdown of three popular tools:

  1. Authelia:
    Authelia is a lightweight, self-hosted identity management solution that’s great for small environments. It offers robust two-factor authentication (2FA) and has a straightforward YAML-based configuration. However, it’s like a Swiss Army knife with only a few tools—it works, but you might outgrow it fast if you need something more complex.
  2. Keycloak:
    Think of Keycloak as the “enterprise-grade everything” option. It’s packed with features like identity federation, OpenID Connect, SAML, and social login integration. But with great power comes great complexity—and server load. Unless you’re running a full-fledged enterprise, Keycloak might feel like using a flamethrower to toast a marshmallow.
  3. Authentik:
    The happy middle ground. Authentik is versatile, modern, and strikes a perfect balance between simplicity and power. Its flow-based configuration makes setting up custom authentication workflows a breeze, and it supports modern protocols like OpenID Connect, SAML, and LDAP. While it’s a newer player in the game, it’s quickly gaining traction—and for good reason.

After some trial and error (and possibly a bit of hair-pulling), I settled on Authentik. Why? It offers better granularity for user flows than Authelia and is far less resource-hungry than Keycloak. It’s slightly harder to set up than Authelia, but don’t worry—I’ll walk you through it step-by-step so you don’t have to cry into your YAML files.


Installing Authentik: Helm and Secrets

To get started, you’ll need Helm. If you’ve made it this far without installing Helm, I have to ask: What are you doing with your life? Helm is the package manager for Kubernetes, and by now, it should already be part of your toolbox.

  1. Run the following command to generate a SUPER_SECURE_PASSWORD:
pwgen -s 50 1
  1. Next, generate a SUPER_SECURE_KEY:
openssl rand 60 | base64 -w 0

Plug these secrets into your values.yaml file, which customizes your Helm chart installation. Here’s what the file might look like (don’t forget to replace the placeholder secrets):

authentik:
    secret_key: "SUPER_SECURE_KEY"
    error_reporting:
        enabled: true
    postgresql:
        password: "SUPER_SECURE_PASSWORD"

server:
    ingress:
        # Specify kubernetes ingress controller class name
        ingressClassName: traefik
        enabled: true
        hosts:
            - auth.yourdomain.com

postgresql:
    enabled: true
    auth:
        password: "SUPER_SECURE_PASSWORD"
redis:
    enabled: true

values.yaml

With your values.yaml file ready, run the following commands to install Authentik:

helm repo add authentik https://charts.goauthentik.io
helm repo update
helm upgrade --install authentik authentik/authentik -f values.yaml

after a while or so, you can check your resources with

kubectl get all -n authentik

if all appears ready then we're good to proceed.


Troubleshooting: The Case of the 404 Page

After installing Authentik, you’ll likely head to auth.yourdomain.com/if/flow/initial-setup/ as per the official docs. And if you’re anything like me, you’ll land on a 404 Not Found page and scream internally. Don’t panic—this is normal.

To fix this, simply delete the authentik-server and authentik-worker pods. This will restart them, and voilà! You should now see the initial setup page. Configure your admin user (aka akadmin), and you’re good to go.


Setting Up Your Authentik Flow

Once Authentik is running, it’s time to create your authentication flow. Authentik operates with Providers and Applications:

  • Providers handle authentication logic (e.g., forward authentication).
  • Applications consume those Providers.

For this tutorial, we’ll set up a Forward-Auth provider, which acts as a middleware layer in front of your endpoints. If a user isn’t authenticated, they’ll be redirected to the Authentik login page. Once logged in, the middleware adds the necessary cookies and redirects them back to their destination.

First we need to create a Provider through the Admin Interface's , below is my provider setup, I have chosen Forward-Auth(domain level) which I'll explain shortly. Feel free to configure it to your own liking

Below is my application configuration for a domain wide, catch-all forward auth, which I know is a mouthful, but all it does is a generic endpoint which is responsible for domain-wide authentication with forward auth, which means we can use it as a middleware, to sit in front of any endpoint for us, and if the user is not authenticated, it'll redirect to the auth page, and once authenticated, it'll inject required auth cookies to the browser (so you're not prompted every single time) and redirects to the intended endpoint. If those cookies are present already, you'll land into your desired endpoint right away.

You should also select Provider>Authentication Settings>Intercept Header Authentication for this to work properly.

And finally here’s what the middleware YAML file looks like:

apiVersion: traefik.containo.us/v1alpha1
kind: Middleware
metadata:
  name: authentik
  namespace: authentik
spec:
  forwardAuth:
    address: http://ak-outpost-authentik-embedded-outpost.authentik.svc.cluster.local:9000/outpost.goauthentik.io/auth/traefik
    trustForwardHeader: true
    authResponseHeaders:
      - X-authentik-username
      - X-authentik-groups
      - X-authentik-email
      - X-authentik-name
      - X-authentik-uid
      - X-authentik-jwt
      - X-authentik-meta-jwks
      - X-authentik-meta-outpost
      - X-authentik-meta-provider
      - X-authentik-meta-app
      - X-authentik-meta-version

authentik-middleware.yaml

Update the address field with your specific embedded outpost service name (use kubectl get svc -n authentik to find it). Then apply the middleware:

kubectl apply -f authentik-middleware.yaml

Testing the Setup

To test your shiny new middleware, deploy Traefik’s Whoami service:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: whoami-tls-ingress
  annotations:
    spec.ingressClassName: traefik
    cert-manager.io/cluster-issuer: letsencrypt-prod
    traefik.ingress.kubernetes.io/router.middlewares: authentik-authentik@kubernetescrd
    acme.cert-manager.io/http01-edit-in-place: 'true'
spec:
  rules:
    - host: whoami.yourdomain.com
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: whoami
                port:
                  number: 5678
  tls:
    - secretName: whoami-tls
      hosts:
        - whoami.yourdomain.com
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: whoami
spec:
  selector:
    matchLabels:
      app: whoami
  replicas: 1
  template:
    metadata:
      labels:
        app: whoami
    spec:
      containers:
        - name: whoami
          image: traefik/whoami:v1.9.0
          ports:
            - containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
  name: whoami
spec:
  type: ClusterIP
  ports:
    - port: 5678
      targetPort: 80
  selector:
    app: whoami

whoami.yaml

Apply the configuration and visit whoami.yourdomain.com. You should see a beautiful login screen, courtesy of Authentik.


Wrapping Up

With Authentik in place, you’ve just transformed your homelab into Fort Knox. Whether it’s securing applications, enabling 2FA, or delegating authentication for tools like ArgoCD, Authentik has you covered. Stay tuned for the next post, where we’ll dive even deeper into advanced flows and integrations!

Until then, happy clustering—and remember, YAML is your friend (most of the time).