Zero-Trust SSO Gateway

Complete Lab Guide — Authentik + Traefik + Docker

⏱️ 4-6 Hours 📊 Intermediate 🏠 Home Lab

📋 Introduction

Welcome to the Zero-Trust SSO Gateway lab. In this comprehensive hands-on exercise, you will build an enterprise-grade identity infrastructure using open-source technologies. By the end of this lab, you will have a fully functional Single Sign-On (SSO) system that protects your self-hosted applications with modern Zero Trust security principles.

🏢
Enterprise Skill Transfer

The skills you develop here directly translate to enterprise IAM platforms like Okta, Microsoft Entra ID (Azure AD), Ping Identity, and ForgeRock. Understanding how to deploy and configure an Identity Provider (IdP) is a core competency for IAM Engineers earning $80K-$150K annually.

🎯 Lab Objectives

Upon successful completion of this lab, you will be able to:

🔧 Technologies Used

Technology Purpose Enterprise Equivalent
Authentik Identity Provider (IdP) — authentication, authorization, user management Okta, Azure AD, Ping Identity
Traefik Reverse Proxy — traffic routing, TLS termination, forward auth F5, NGINX Plus, AWS ALB
Docker & Docker Compose Container orchestration — service deployment and management Kubernetes, ECS, OpenShift
PostgreSQL Database — stores identity data, policies, audit logs RDS, Azure SQL, Oracle
Redis Cache — session management, rate limiting, task queues ElastiCache, Azure Cache
Let's Encrypt Certificate Authority — free, automated TLS certificates DigiCert, Venafi, AWS ACM

📑 Table of Contents

📦 Prerequisites

Before starting this lab, ensure you have the following resources and access rights prepared. Taking time to verify prerequisites will prevent interruptions during the hands-on phases.

🖥️ Two-Machine Architecture

⚠️
Critical: You Need TWO Machines

This lab requires two separate systems. Do not install Docker on your local workstation unless it IS your server.

  • HOST Machine — Your daily computer (Windows/Mac/Linux) used to send commands
  • SERVER Machine — A Linux server (Ubuntu/Debian) where all services run
Requirement 💻 HOST Machine 🖥️ SERVER Machine
Operating System Windows 10/11, macOS, or Linux Ubuntu 22.04/24.04 LTS or Debian 12
RAM 4GB+ (for browser and terminal) 4GB minimum, 8GB recommended
Storage 20GB+ free disk space
Network Internet access Static IP or DHCP reservation, ports 80/443 accessible
Software to Install SSH client, web browser Docker, Docker Compose (we'll install these)

🌐 Domain & DNS Requirements

📝 Information to Gather Before Starting

Fill in these values before proceeding. You'll reference them throughout the lab.

Item Your Value Example
Your Domain ________________ homelab.example.com
Server Public IP ________________ 203.0.113.50
Server Local IP ________________ 192.168.1.100
SSH Username ________________ admin
Email for SSL Certs ________________ admin@example.com

1

Phase 1: Local Machine Setup

Configure your HOST workstation with the tools needed to connect to and manage your server.

⏱️ 15-20 minutes 💻 Performed on: HOST Machine

In this phase, you will install and configure an SSH client on your local workstation. SSH (Secure Shell) provides encrypted communication between your HOST machine and the SERVER where all services will run.

💡
Why SSH?

SSH is the industry-standard protocol for secure remote server administration. In enterprise environments, you'll use the same concepts with jump hosts, bastion servers, and privileged access management (PAM) solutions like CyberArk.

Task 1.1: Install SSH Client

💻 HOST Machine — Your Workstation

Option A: Windows 10/11

Windows 10 (version 1809+) and Windows 11 include OpenSSH client by default. Let's verify it's installed:

1
Open PowerShell as Administrator

Right-click the Start button → Select "Windows Terminal (Admin)" or "PowerShell (Admin)"

2
Check if OpenSSH Client is Installed
PowerShell HOST
# Check if OpenSSH Client capability is installed
Get-WindowsCapability -Online | Where-Object Name -like 'OpenSSH.Client*'
3
Install OpenSSH Client (if not present)

If the previous command shows "NotPresent", run this command to install:

PowerShell HOST
# Install OpenSSH Client (requires admin privileges)
Add-WindowsCapability -Online -Name OpenSSH.Client~~~~0.0.1.0
Verification

Confirm SSH is working by checking the version:

PowerShell HOST
ssh -V
Expected Output:
OpenSSH_for_Windows_8.1p1, LibreSSL 3.0.2

Option B: macOS

macOS includes OpenSSH by default. No installation required.

1
Open Terminal

Press Cmd + Space, type "Terminal", and press Enter.

Verification

Verify SSH is available:

Bash HOST
ssh -V
Expected Output:
OpenSSH_9.0p1, LibreSSL 3.3.6

Option C: Linux (Ubuntu/Debian)

Most Linux distributions include OpenSSH client. If not, install it:

Bash HOST
# Update package lists
sudo apt update

# Install OpenSSH client
sudo apt install openssh-client -y

# Verify installation
ssh -V

Task 1.2: Test SSH Connection to Server

💻 HOST Machine — Connecting to Server
1
Connect to Your Server via SSH

Replace username with your SSH user and server-ip with your server's IP address:

Bash / PowerShell HOST
# Connect to your server (replace with your values)
ssh username@server-ip

# Example:
# ssh admin@192.168.1.100
# ssh admin@203.0.113.50
2
Accept the Host Key (First Connection Only)

When connecting for the first time, you'll see a message like:

The authenticity of host '192.168.1.100 (192.168.1.100)' can't be established. ED25519 key fingerprint is SHA256:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx. Are you sure you want to continue connecting (yes/no/[fingerprint])?

Type yes and press Enter. Then enter your password when prompted.

Verification

You should now see your server's command prompt. Confirm by checking the hostname:

Bash SERVER
# Display current hostname
hostname

# Display current user and hostname
whoami && hostname
Expected Output:
admin your-server-name
🎉
Phase 1 Complete!

You have successfully configured your HOST machine and established a secure SSH connection to your SERVER. All remaining commands in this lab will be executed on the SERVER via this SSH session.


2

Phase 2: Server Preparation

Update the operating system, configure the firewall, and install Docker + Docker Compose.

⏱️ 30-45 minutes 🖥️ Performed on: SERVER Machine
⚠️
Important: All Commands on SERVER

From this point forward, ALL commands are executed on the SERVER via your SSH session. The command prompt should show your server's hostname, not your local machine.

Task 2.1: Update the Operating System

🖥️ SERVER Machine — Via SSH Connection

Start by updating all system packages to ensure you have the latest security patches and software versions.

1
Update Package Lists and Upgrade Packages
Bash SERVER
# Update the package index (list of available packages)
sudo apt update

# Upgrade all installed packages to their latest versions
# -y flag automatically confirms prompts
sudo apt upgrade -y

# Install common utilities we'll need
sudo apt install -y curl wget git nano htop net-tools
💡
Understanding the Commands
  • apt update — Refreshes the list of available packages from repositories
  • apt upgrade — Installs newer versions of packages currently installed
  • -y flag — Automatically answers "yes" to confirmation prompts
  • sudo — Runs the command with superuser (root) privileges

Task 2.2: Configure the Firewall (UFW)

🖥️ SERVER Machine — Via SSH Connection

UFW (Uncomplicated Firewall) is a user-friendly interface for managing iptables firewall rules. We'll configure it to allow only necessary traffic.

1
Install and Enable UFW
Bash SERVER
# Install UFW (may already be installed on Ubuntu)
sudo apt install ufw -y

# IMPORTANT: Allow SSH first to prevent lockout!
sudo ufw allow ssh

# Allow HTTP (port 80) - required for Let's Encrypt certificate verification
sudo ufw allow 80/tcp

# Allow HTTPS (port 443) - all secure web traffic
sudo ufw allow 443/tcp

# Enable the firewall
sudo ufw enable

When prompted "Command may disrupt existing ssh connections. Proceed with operation (y|n)?", type y and press Enter.

🚨
Critical Security Warning

ALWAYS allow SSH before enabling the firewall! If you skip this step, you will be locked out of your server and unable to reconnect remotely.

Verification

Check the firewall status and rules:

Bash SERVER
sudo ufw status verbose
Expected Output:
Status: active Logging: on (low) Default: deny (incoming), allow (outgoing), disabled (routed) New profiles: skip To Action From -- ------ ---- 22/tcp ALLOW IN Anywhere 80/tcp ALLOW IN Anywhere 443/tcp ALLOW IN Anywhere 22/tcp (v6) ALLOW IN Anywhere (v6) 80/tcp (v6) ALLOW IN Anywhere (v6) 443/tcp (v6) ALLOW IN Anywhere (v6)

Task 2.3: Install Docker

🖥️ SERVER Machine — Via SSH Connection

Docker is a platform for developing, shipping, and running applications in containers. Containers package an application with all its dependencies, ensuring consistent behavior across environments.

1
Remove Old Docker Versions (if any)
Bash SERVER
# Remove any old Docker packages that might conflict
# It's OK if these packages aren't found
sudo apt remove docker docker-engine docker.io containerd runc 2>/dev/null || true
2
Install Docker Prerequisites
Bash SERVER
# Install packages needed to use HTTPS repositories
sudo apt install -y \
    apt-transport-https \
    ca-certificates \
    curl \
    gnupg \
    lsb-release
3
Add Docker's Official GPG Key and Repository
Bash SERVER
# Create directory for Docker's GPG key
sudo install -m 0755 -d /etc/apt/keyrings

# Download and add Docker's official GPG key
# This verifies that packages are authentically from Docker
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | \
    sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg

# Set proper permissions on the key file
sudo chmod a+r /etc/apt/keyrings/docker.gpg

# Add Docker repository to apt sources
echo \
  "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \
  $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \
  sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
4
Install Docker Engine
Bash SERVER
# Update apt to recognize the new Docker repository
sudo apt update

# Install Docker Engine, CLI, containerd, and plugins
sudo apt install -y \
    docker-ce \
    docker-ce-cli \
    containerd.io \
    docker-buildx-plugin \
    docker-compose-plugin
5
Add Your User to the Docker Group

By default, Docker commands require sudo. Adding your user to the docker group allows running Docker without sudo.

Bash SERVER
# Add current user to the docker group
sudo usermod -aG docker $USER

# Apply the group change without logging out
# This creates a new shell with updated group membership
newgrp docker
Verification

Verify Docker is installed correctly and your user can run Docker commands:

Bash SERVER
# Check Docker version
docker --version

# Check Docker Compose version
docker compose version

# Run a test container (downloads small image, runs, and exits)
docker run hello-world
Expected Output (docker --version):
Docker version 24.0.7, build afdd53b
Expected Output (hello-world):
Hello from Docker! This message shows that your installation appears to be working correctly. ...
🎉
Phase 2 Complete!

Your server is now updated, firewalled, and has Docker installed. You're ready to deploy the core services.


3

Phase 3: Core Services Deployment

Configure DNS, create Docker Compose stack, and deploy Traefik + Authentik.

⏱️ 45-60 minutes 🖥️ Performed on: SERVER Machine + DNS Provider

Task 3.1: Configure DNS Records

💻 HOST Machine — DNS Provider Dashboard

Before deploying services, you need to point your domain to your server. Log in to your DNS provider (Cloudflare, Route53, Namecheap, etc.) and create these records:

Type Name Value TTL Purpose
A authentik Your Server Public IP Auto / 300 Authentik web interface
A traefik Your Server Public IP Auto / 300 Traefik dashboard
A * (wildcard) — optional Your Server Public IP Auto / 300 Future apps (*.yourdomain.com)
💡
Cloudflare Users

If using Cloudflare, set the proxy status to "DNS only" (gray cloud) initially. You can enable the proxy later after SSL is working. Proxied traffic (orange cloud) can interfere with Let's Encrypt certificate issuance.

Verification

Wait 2-5 minutes for DNS propagation, then verify the records:

Bash SERVER or HOST
# Check DNS resolution (replace with your domain)
nslookup authentik.yourdomain.com

# Alternative using dig
dig authentik.yourdomain.com +short
Expected Output:
203.0.113.50 (your server's public IP)

Task 3.2: Create Project Directory Structure

🖥️ SERVER Machine — Via SSH Connection
1
Create the Main Project Directory
Bash SERVER
# Create main directory for all identity services
mkdir -p ~/identity-stack

# Create subdirectories for configuration and data
mkdir -p ~/identity-stack/traefik
mkdir -p ~/identity-stack/authentik/media
mkdir -p ~/identity-stack/authentik/templates
mkdir -p ~/identity-stack/postgres-data
mkdir -p ~/identity-stack/redis-data

# Navigate to the project directory
cd ~/identity-stack

# Verify the structure
tree . 2>/dev/null || ls -la

Task 3.3: Generate Secrets

🖥️ SERVER Machine — Via SSH Connection

Authentik requires a secret key for encryption and a secure database password. We'll generate these using OpenSSL.

1
Generate Random Secrets
Bash SERVER
# Ensure we're in the project directory
cd ~/identity-stack

# Generate a random secret key for Authentik (50 characters)
# This is used to encrypt session data and tokens
echo "AUTHENTIK_SECRET_KEY=$(openssl rand -base64 36)" >> .env

# Generate a random password for PostgreSQL database
echo "PG_PASS=$(openssl rand -base64 24)" >> .env

# View the generated secrets (keep these safe!)
cat .env
🔐
Security: Protect Your Secrets

The .env file contains sensitive credentials. In enterprise environments, these secrets would be stored in a secrets manager like HashiCorp Vault, AWS Secrets Manager, or Azure Key Vault.

  • Never commit .env files to version control (Git)
  • Set restrictive file permissions: chmod 600 .env
  • Back up secrets securely (encrypted password manager)

Task 3.4: Create Traefik Configuration

🖥️ SERVER Machine — Via SSH Connection
1
Create Traefik Static Configuration

This file defines Traefik's core settings, entrypoints, and certificate resolver.

Bash SERVER
# Create Traefik configuration file
# IMPORTANT: Replace YOUR_EMAIL@example.com with your real email
cat > ~/identity-stack/traefik/traefik.yml << 'EOF'
# ============================================
# TRAEFIK STATIC CONFIGURATION
# ============================================

# Global settings
global:
  checkNewVersion: true
  sendAnonymousUsage: false

# API and Dashboard configuration
api:
  dashboard: true          # Enable Traefik dashboard
  insecure: false          # Dashboard requires authentication

# Logging configuration
log:
  level: INFO              # DEBUG, INFO, WARN, ERROR
  filePath: "/var/log/traefik/traefik.log"

accessLog:
  filePath: "/var/log/traefik/access.log"
  bufferingSize: 100

# Entrypoints define how traffic enters Traefik
entryPoints:
  # HTTP entrypoint - redirects all traffic to HTTPS
  web:
    address: ":80"
    http:
      redirections:
        entryPoint:
          to: websecure
          scheme: https
          permanent: true
  
  # HTTPS entrypoint - main secure traffic handler
  websecure:
    address: ":443"
    http:
      tls:
        certResolver: letsencrypt

# Certificate resolvers for automatic TLS
certificatesResolvers:
  letsencrypt:
    acme:
      # IMPORTANT: Replace with YOUR email address
      email: YOUR_EMAIL@example.com
      storage: /letsencrypt/acme.json
      httpChallenge:
        entryPoint: web

# Provider configuration
providers:
  # Docker provider - auto-discovers containers
  docker:
    endpoint: "unix:///var/run/docker.sock"
    exposedByDefault: false  # Only expose containers with traefik.enable=true
    network: identity-network
  
  # File provider - for additional dynamic config
  file:
    directory: /etc/traefik/dynamic
    watch: true
EOF
2
Update Email Address in Configuration

Replace the placeholder email with your real email address. Let's Encrypt uses this for certificate expiration warnings.

Bash SERVER
# Replace placeholder with your actual email
# Example: sed -i 's/YOUR_EMAIL@example.com/admin@mycompany.com/g' ...
sed -i 's/YOUR_EMAIL@example.com/YOUR_REAL_EMAIL@domain.com/g' \
    ~/identity-stack/traefik/traefik.yml

# Verify the change
grep "email:" ~/identity-stack/traefik/traefik.yml
3
Create Empty ACME Storage File

Let's Encrypt certificates will be stored in this file. It must exist with proper permissions before Traefik starts.

Bash SERVER
# Create the letsencrypt directory
mkdir -p ~/identity-stack/letsencrypt

# Create empty acme.json file
touch ~/identity-stack/letsencrypt/acme.json

# Set restrictive permissions (required by Traefik)
# 600 = owner can read/write, no one else can access
chmod 600 ~/identity-stack/letsencrypt/acme.json

Task 3.5: Create Docker Compose File

🖥️ SERVER Machine — Via SSH Connection

This is the main deployment file that defines all services and their configurations.

1
Create the Complete Docker Compose Stack
Bash SERVER
# Create docker-compose.yml
# IMPORTANT: Replace 'yourdomain.com' with your actual domain
cat > ~/identity-stack/docker-compose.yml << 'EOF'
# ============================================
# IDENTITY STACK - DOCKER COMPOSE
# Authentik + Traefik + PostgreSQL + Redis
# ============================================

version: "3.8"

services:
  # ----------------------------------------
  # TRAEFIK - Reverse Proxy & Edge Router
  # ----------------------------------------
  traefik:
    image: traefik:v3.0
    container_name: traefik
    restart: unless-stopped
    security_opt:
      - no-new-privileges:true
    ports:
      - "80:80"      # HTTP (redirects to HTTPS)
      - "443:443"    # HTTPS
    volumes:
      # Docker socket - allows Traefik to detect containers
      - /var/run/docker.sock:/var/run/docker.sock:ro
      # Traefik configuration
      - ./traefik/traefik.yml:/etc/traefik/traefik.yml:ro
      # Let's Encrypt certificates
      - ./letsencrypt:/letsencrypt
      # Log files
      - ./traefik/logs:/var/log/traefik
    networks:
      - identity-network
    labels:
      # Enable Traefik for this container
      - "traefik.enable=true"
      # Dashboard router
      - "traefik.http.routers.traefik.rule=Host(\`traefik.yourdomain.com\`)"
      - "traefik.http.routers.traefik.entrypoints=websecure"
      - "traefik.http.routers.traefik.tls.certresolver=letsencrypt"
      - "traefik.http.routers.traefik.service=api@internal"
      # Protect dashboard with Authentik
      - "traefik.http.routers.traefik.middlewares=authentik@docker"

  # ----------------------------------------
  # POSTGRESQL - Database for Authentik
  # ----------------------------------------
  postgresql:
    image: postgres:16-alpine
    container_name: authentik-postgres
    restart: unless-stopped
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -d $${POSTGRES_DB} -U $${POSTGRES_USER}"]
      start_period: 20s
      interval: 30s
      retries: 5
      timeout: 5s
    volumes:
      - ./postgres-data:/var/lib/postgresql/data
    environment:
      POSTGRES_PASSWORD: ${PG_PASS}
      POSTGRES_USER: authentik
      POSTGRES_DB: authentik
    networks:
      - identity-network

  # ----------------------------------------
  # REDIS - Cache & Session Store
  # ----------------------------------------
  redis:
    image: redis:alpine
    container_name: authentik-redis
    restart: unless-stopped
    command: --save 60 1 --loglevel warning
    healthcheck:
      test: ["CMD-SHELL", "redis-cli ping | grep PONG"]
      start_period: 20s
      interval: 30s
      retries: 5
      timeout: 3s
    volumes:
      - ./redis-data:/data
    networks:
      - identity-network

  # ----------------------------------------
  # AUTHENTIK SERVER - Identity Provider
  # ----------------------------------------
  authentik-server:
    image: ghcr.io/goauthentik/server:2024.2.2
    container_name: authentik-server
    restart: unless-stopped
    command: server
    environment:
      AUTHENTIK_SECRET_KEY: ${AUTHENTIK_SECRET_KEY}
      AUTHENTIK_REDIS__HOST: redis
      AUTHENTIK_POSTGRESQL__HOST: postgresql
      AUTHENTIK_POSTGRESQL__USER: authentik
      AUTHENTIK_POSTGRESQL__NAME: authentik
      AUTHENTIK_POSTGRESQL__PASSWORD: ${PG_PASS}
    volumes:
      - ./authentik/media:/media
      - ./authentik/templates:/templates
    depends_on:
      postgresql:
        condition: service_healthy
      redis:
        condition: service_healthy
    networks:
      - identity-network
    labels:
      - "traefik.enable=true"
      # Main Authentik router
      - "traefik.http.routers.authentik.rule=Host(\`authentik.yourdomain.com\`)"
      - "traefik.http.routers.authentik.entrypoints=websecure"
      - "traefik.http.routers.authentik.tls.certresolver=letsencrypt"
      - "traefik.http.services.authentik.loadbalancer.server.port=9000"
      # Forward Auth middleware for protecting other apps
      - "traefik.http.middlewares.authentik.forwardauth.address=http://authentik-server:9000/outpost.goauthentik.io/auth/traefik"
      - "traefik.http.middlewares.authentik.forwardauth.trustForwardHeader=true"
      - "traefik.http.middlewares.authentik.forwardauth.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 WORKER - Background Tasks
  # ----------------------------------------
  authentik-worker:
    image: ghcr.io/goauthentik/server:2024.2.2
    container_name: authentik-worker
    restart: unless-stopped
    command: worker
    environment:
      AUTHENTIK_SECRET_KEY: ${AUTHENTIK_SECRET_KEY}
      AUTHENTIK_REDIS__HOST: redis
      AUTHENTIK_POSTGRESQL__HOST: postgresql
      AUTHENTIK_POSTGRESQL__USER: authentik
      AUTHENTIK_POSTGRESQL__NAME: authentik
      AUTHENTIK_POSTGRESQL__PASSWORD: ${PG_PASS}
    volumes:
      - ./authentik/media:/media
      - ./authentik/templates:/templates
    depends_on:
      postgresql:
        condition: service_healthy
      redis:
        condition: service_healthy
    networks:
      - identity-network

# ----------------------------------------
# NETWORKS
# ----------------------------------------
networks:
  identity-network:
    name: identity-network
    driver: bridge
EOF
2
Update Domain in Docker Compose

Replace all instances of yourdomain.com with your actual domain:

Bash SERVER
# Replace yourdomain.com with your actual domain
# Example: sed -i 's/yourdomain.com/homelab.example.com/g' ...
sed -i 's/yourdomain.com/YOUR_ACTUAL_DOMAIN.com/g' \
    ~/identity-stack/docker-compose.yml

# Verify the change
grep "Host" ~/identity-stack/docker-compose.yml

Task 3.6: Deploy the Stack

🖥️ SERVER Machine — Via SSH Connection
1
Start All Services
Bash SERVER
# Navigate to project directory
cd ~/identity-stack

# Pull the latest images
docker compose pull

# Start all services in detached mode
# -d = run in background
docker compose up -d

# Watch the logs to monitor startup (Ctrl+C to exit)
docker compose logs -f
Verification

Verify all containers are running and healthy:

Bash SERVER
# Check container status
docker compose ps

# Check for any errors in logs
docker compose logs --tail=20
Expected Output (docker compose ps):
NAME STATUS PORTS authentik-postgres Up (healthy) 5432/tcp authentik-redis Up (healthy) 6379/tcp authentik-server Up 0.0.0.0:9000->9000/tcp authentik-worker Up traefik Up 0.0.0.0:80->80/tcp, 0.0.0.0:443->443/tcp
⏱️
Wait for Certificates

Allow 2-5 minutes for Let's Encrypt to issue SSL certificates. You can monitor this in the Traefik logs. If you see certificate errors, check the Troubleshooting section.

🎉
Phase 3 Complete!

All core services are now running. In the next phase, you'll configure Authentik through its web interface.


4

Phase 4: Authentik Configuration

Create admin account, configure flows, and set up MFA.

⏱️ 30-45 minutes 💻 Performed on: HOST Machine (Web Browser)

Task 4.1: Initial Setup

💻 HOST Machine — Web Browser
1
Access Authentik Web Interface

Open your web browser and navigate to:

URL BROWSER
https://authentik.yourdomain.com/if/flow/initial-setup/

You should see the Authentik initial setup page. If you see a certificate error, wait a few more minutes for Let's Encrypt to issue the certificate.

2
Create Admin Account

Fill in the initial setup form:

Field Value Notes
Email Your email address Used for password recovery
Password Strong password (16+ characters) Mix of upper, lower, numbers, symbols

Click "Create account" to complete initial setup.

3
Access the Admin Interface

After creating your account, you'll be redirected to the user library. Click the "Admin interface" button in the top-right corner to access the administration panel.

Verification

You should now be in the Authentik Admin interface. Verify by checking:


Task 4.2: Configure MFA (Multi-Factor Authentication)

💻 HOST Machine — Authentik Admin Interface

Enabling MFA adds a critical security layer. We'll use TOTP (Time-based One-Time Password), compatible with apps like Google Authenticator, Microsoft Authenticator, or Authy.

1
Navigate to MFA Settings

In the Authentik Admin interface:

  1. Click "Directory" in the left sidebar
  2. Click "Users"
  3. Click on your admin user (akadmin)
  4. Click the "MFA Devices" tab
2
Enroll TOTP Device
  1. Click "Enroll" dropdown
  2. Select "TOTP Device"
  3. Scan the QR code with your authenticator app
  4. Enter the 6-digit code from your app to confirm
🏢
Enterprise MFA Options

Authentik supports additional MFA methods you'd find in enterprise environments:

  • WebAuthn / FIDO2 — Hardware security keys (YubiKey, Titan)
  • SMS / Email OTP — One-time passwords via SMS or email
  • Duo Push — Integration with Duo Security
  • Static Tokens — Backup codes for recovery
🎉
Phase 4 Complete!

Authentik is now configured with an admin account and MFA enabled. Your identity provider is ready to protect applications!


5

Phase 5: Testing & Validation

Verify the complete setup works end-to-end.

⏱️ 15-20 minutes 💻 Performed on: HOST Machine + SERVER

Task 5.1: Test Authentication Flow

💻 HOST Machine — Web Browser
1
Open an Incognito/Private Browser Window

This ensures you're testing a fresh session without cached credentials.

2
Navigate to Authentik
URL BROWSER
https://authentik.yourdomain.com
3
Complete Login Flow
  1. Enter your username (akadmin) and password
  2. If MFA is enabled, enter your TOTP code
  3. Verify you reach the user dashboard
Success Criteria

Task 5.2: Verify Server Health

🖥️ SERVER Machine — Via SSH Connection
Bash SERVER
cd ~/identity-stack

# Check all containers are healthy
docker compose ps

# Check resource usage
docker stats --no-stream

# Check for any error logs
docker compose logs --tail=50 | grep -i error

# Check certificate status
cat ~/identity-stack/letsencrypt/acme.json | grep -i "domain"
🏆
Congratulations!

You have successfully deployed a Zero-Trust SSO Gateway! You now have enterprise-grade identity infrastructure running in your home lab.


🔧 Troubleshooting Guide

Below are common issues you may encounter and their solutions.

🔴 Certificate Error / "Not Secure" Warning
Cause: Let's Encrypt hasn't issued certificates yet, or DNS is misconfigured.
Solutions:
  1. Wait 5-10 minutes for certificate issuance
  2. Verify DNS: nslookup authentik.yourdomain.com
  3. Check Traefik logs: docker logs traefik 2>&1 | grep -i acme
  4. Ensure ports 80/443 are accessible from the internet
  5. If using Cloudflare, disable proxy (orange cloud → gray cloud)
🔴 "502 Bad Gateway" Error
Cause: Backend service (Authentik) isn't running or isn't reachable.
Solutions:
  1. Check services: docker compose ps
  2. Check Authentik logs: docker logs authentik-server --tail=50
  3. Restart services: docker compose restart
  4. Verify network: docker network ls | grep identity
🔴 Database Connection Error
Cause: PostgreSQL container unhealthy or password mismatch.
Solutions:
  1. Check database health: docker logs authentik-postgres
  2. Verify .env file has PG_PASS set: cat .env
  3. Reset database (destroys data): docker compose down -v && docker compose up -d
🔴 SSH Connection Refused
Cause: SSH service not running, firewall blocking, or wrong IP.
Solutions:
  1. Verify server is powered on (physical access may be required)
  2. Confirm IP address is correct
  3. Check if another device can ping the server
  4. If locked out due to firewall, use console access to run: sudo ufw allow ssh

🧹 Cleanup Instructions

When you're done with the lab or need to free up resources, follow these steps to cleanly tear down the environment.

🖥️ SERVER Machine — Via SSH Connection

Option A: Stop Services (Preserve Data)

This stops all containers but preserves your data for later use.

Bash SERVER
cd ~/identity-stack

# Stop all containers (data is preserved)
docker compose stop

# Verify containers are stopped
docker compose ps

Option B: Remove Containers (Preserve Data Volumes)

Removes containers but keeps database and configuration data.

Bash SERVER
cd ~/identity-stack

# Stop and remove containers, but keep volumes
docker compose down

# Verify
docker compose ps

Option C: Complete Removal (Delete Everything)

⚠️
Warning: Permanent Data Loss

This will permanently delete all user data, configurations, and certificates. This action cannot be undone.

Bash SERVER
cd ~/identity-stack

# Stop and remove everything including volumes
docker compose down -v --remove-orphans

# Remove the project directory
cd ~
rm -rf ~/identity-stack

# Remove unused Docker images (optional)
docker image prune -a

# Remove unused Docker networks (optional)
docker network prune

🚀 What's Next?

Congratulations on completing this lab! Here are suggested next steps to continue your IAM learning journey:

🏢
Career Impact

Document this project in your portfolio! Include architecture diagrams, screenshots, and a description of the Zero Trust principles you implemented. This demonstrates hands-on experience with IAM technologies that employers actively seek.