mirror of
https://github.com/ghndrx/authentik-terraform.git
synced 2026-02-10 06:44:58 +00:00
feat: Authentik Terraform configuration for homelab SSO
Infrastructure as Code for Authentik identity provider managing: OAuth2/OIDC Applications: - Grafana, Home Assistant, Immich - Uptime Kuma (proxy auth) - Sonarr, Radarr, Prowlarr (*arr stack proxy auth) - ArgoCD Identity Sources: - Google Workspace federation LDAP: - TrueNAS LDAP provider and outpost CI/CD: - GitHub Actions workflow for plan/apply - Secrets managed via GitHub Actions secrets Provider: beryju/authentik v2025.2
This commit is contained in:
105
.github/workflows/deploy.yml
vendored
Normal file
105
.github/workflows/deploy.yml
vendored
Normal file
@@ -0,0 +1,105 @@
|
|||||||
|
name: Deploy Authentik Configuration
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [main]
|
||||||
|
pull_request:
|
||||||
|
branches: [main]
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
env:
|
||||||
|
TF_VERSION: "1.7.0"
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
plan:
|
||||||
|
name: Terraform Plan
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Setup Terraform
|
||||||
|
uses: hashicorp/setup-terraform@v3
|
||||||
|
with:
|
||||||
|
terraform_version: ${{ env.TF_VERSION }}
|
||||||
|
|
||||||
|
- name: Create secrets.auto.tfvars
|
||||||
|
run: |
|
||||||
|
cat > secrets.auto.tfvars << EOF
|
||||||
|
authentik_url = "${{ secrets.AUTHENTIK_URL }}"
|
||||||
|
authentik_token = "${{ secrets.AUTHENTIK_TOKEN }}"
|
||||||
|
|
||||||
|
# Google OAuth (optional)
|
||||||
|
google_client_id = "${{ secrets.GOOGLE_CLIENT_ID }}"
|
||||||
|
google_client_secret = "${{ secrets.GOOGLE_CLIENT_SECRET }}"
|
||||||
|
|
||||||
|
# Application URLs
|
||||||
|
argocd_url = "${{ secrets.ARGOCD_URL }}"
|
||||||
|
grafana_url = "${{ secrets.GRAFANA_URL }}"
|
||||||
|
home_assistant_url = "${{ secrets.HOME_ASSISTANT_URL }}"
|
||||||
|
immich_url = "${{ secrets.IMMICH_URL }}"
|
||||||
|
uptime_kuma_url = "${{ secrets.UPTIME_KUMA_URL }}"
|
||||||
|
sonarr_url = "${{ secrets.SONARR_URL }}"
|
||||||
|
radarr_url = "${{ secrets.RADARR_URL }}"
|
||||||
|
prowlarr_url = "${{ secrets.PROWLARR_URL }}"
|
||||||
|
EOF
|
||||||
|
|
||||||
|
- name: Terraform Init
|
||||||
|
run: terraform init
|
||||||
|
|
||||||
|
- name: Terraform Validate
|
||||||
|
run: terraform validate
|
||||||
|
|
||||||
|
- name: Terraform Plan
|
||||||
|
run: terraform plan -out=tfplan
|
||||||
|
|
||||||
|
- name: Upload Plan
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
if: github.event_name == 'pull_request'
|
||||||
|
with:
|
||||||
|
name: tfplan
|
||||||
|
path: tfplan
|
||||||
|
|
||||||
|
apply:
|
||||||
|
name: Terraform Apply
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: plan
|
||||||
|
if: github.ref == 'refs/heads/main' && github.event_name == 'push'
|
||||||
|
environment: production
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Setup Terraform
|
||||||
|
uses: hashicorp/setup-terraform@v3
|
||||||
|
with:
|
||||||
|
terraform_version: ${{ env.TF_VERSION }}
|
||||||
|
|
||||||
|
- name: Create secrets.auto.tfvars
|
||||||
|
run: |
|
||||||
|
cat > secrets.auto.tfvars << EOF
|
||||||
|
authentik_url = "${{ secrets.AUTHENTIK_URL }}"
|
||||||
|
authentik_token = "${{ secrets.AUTHENTIK_TOKEN }}"
|
||||||
|
|
||||||
|
# Google OAuth (optional)
|
||||||
|
google_client_id = "${{ secrets.GOOGLE_CLIENT_ID }}"
|
||||||
|
google_client_secret = "${{ secrets.GOOGLE_CLIENT_SECRET }}"
|
||||||
|
|
||||||
|
# Application URLs
|
||||||
|
argocd_url = "${{ secrets.ARGOCD_URL }}"
|
||||||
|
grafana_url = "${{ secrets.GRAFANA_URL }}"
|
||||||
|
home_assistant_url = "${{ secrets.HOME_ASSISTANT_URL }}"
|
||||||
|
immich_url = "${{ secrets.IMMICH_URL }}"
|
||||||
|
uptime_kuma_url = "${{ secrets.UPTIME_KUMA_URL }}"
|
||||||
|
sonarr_url = "${{ secrets.SONARR_URL }}"
|
||||||
|
radarr_url = "${{ secrets.RADARR_URL }}"
|
||||||
|
prowlarr_url = "${{ secrets.PROWLARR_URL }}"
|
||||||
|
EOF
|
||||||
|
|
||||||
|
- name: Terraform Init
|
||||||
|
run: terraform init
|
||||||
|
|
||||||
|
- name: Terraform Apply
|
||||||
|
run: terraform apply -auto-approve
|
||||||
30
.gitignore
vendored
Normal file
30
.gitignore
vendored
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
# Terraform
|
||||||
|
.terraform/
|
||||||
|
.terraform.lock.hcl
|
||||||
|
*.tfstate
|
||||||
|
*.tfstate.*
|
||||||
|
*.tfplan
|
||||||
|
crash.log
|
||||||
|
override.tf
|
||||||
|
override.tf.json
|
||||||
|
*_override.tf
|
||||||
|
*_override.tf.json
|
||||||
|
|
||||||
|
# Secrets - NEVER commit these
|
||||||
|
*.tfvars
|
||||||
|
!*.tfvars.example
|
||||||
|
secrets.yaml
|
||||||
|
secrets*.yaml
|
||||||
|
*.pem
|
||||||
|
*.key
|
||||||
|
|
||||||
|
# IDE
|
||||||
|
.idea/
|
||||||
|
.vscode/
|
||||||
|
*.swp
|
||||||
|
*.swo
|
||||||
|
*~
|
||||||
|
|
||||||
|
# OS
|
||||||
|
.DS_Store
|
||||||
|
Thumbs.db
|
||||||
162
README.md
Normal file
162
README.md
Normal file
@@ -0,0 +1,162 @@
|
|||||||
|
# Authentik Terraform Configuration
|
||||||
|
|
||||||
|
Infrastructure as Code for Authentik identity provider - manage applications, providers, and SSO via Terraform.
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
- **OAuth2/OIDC Applications**: ArgoCD, Grafana
|
||||||
|
- **Proxy Authentication**: Home Assistant, Immich, Uptime Kuma, *arr stack
|
||||||
|
- **LDAP Outpost**: For legacy application support
|
||||||
|
- **Google OAuth Source**: Social login integration
|
||||||
|
|
||||||
|
## Quick Start
|
||||||
|
|
||||||
|
### 1. Fork/Clone This Repo
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git clone https://github.com/ghndrx/authentik-terraform.git
|
||||||
|
cd authentik-terraform
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Configure GitHub Secrets
|
||||||
|
|
||||||
|
Go to **Settings > Secrets and variables > Actions** and add:
|
||||||
|
|
||||||
|
| Secret | Description | Example |
|
||||||
|
|--------|-------------|---------|
|
||||||
|
| `AUTHENTIK_URL` | Your Authentik server URL | `https://auth.example.com` |
|
||||||
|
| `AUTHENTIK_TOKEN` | API token from Authentik | `ak-...` |
|
||||||
|
| `GOOGLE_CLIENT_ID` | Google OAuth client ID | `xxx.apps.googleusercontent.com` |
|
||||||
|
| `GOOGLE_CLIENT_SECRET` | Google OAuth secret | `GOCSPX-...` |
|
||||||
|
| `ARGOCD_URL` | ArgoCD URL | `https://argocd.example.com` |
|
||||||
|
| `GRAFANA_URL` | Grafana URL | `https://grafana.example.com` |
|
||||||
|
| `HOME_ASSISTANT_URL` | Home Assistant URL | `https://home.example.com` |
|
||||||
|
| `IMMICH_URL` | Immich URL | `https://photos.example.com` |
|
||||||
|
| `UPTIME_KUMA_URL` | Uptime Kuma URL | `https://status.example.com` |
|
||||||
|
| `SONARR_URL` | Sonarr URL | `https://sonarr.example.com` |
|
||||||
|
| `RADARR_URL` | Radarr URL | `https://radarr.example.com` |
|
||||||
|
| `PROWLARR_URL` | Prowlarr URL | `https://prowlarr.example.com` |
|
||||||
|
|
||||||
|
### 3. Create Authentik API Token
|
||||||
|
|
||||||
|
1. Log into Authentik as admin
|
||||||
|
2. Go to **Directory > Tokens and App passwords**
|
||||||
|
3. Create a new token with **API Access** intent
|
||||||
|
4. Copy the token value
|
||||||
|
|
||||||
|
### 4. (Optional) Set Up Google OAuth
|
||||||
|
|
||||||
|
1. Go to [Google Cloud Console](https://console.cloud.google.com)
|
||||||
|
2. Create OAuth 2.0 credentials
|
||||||
|
3. Add authorized redirect URI: `https://auth.example.com/source/oauth/callback/google/`
|
||||||
|
|
||||||
|
### 5. Deploy
|
||||||
|
|
||||||
|
Push to `main` branch to trigger deployment, or run manually:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Local development
|
||||||
|
cp terraform.tfvars.example terraform.tfvars
|
||||||
|
# Edit terraform.tfvars with your values
|
||||||
|
|
||||||
|
terraform init
|
||||||
|
terraform plan
|
||||||
|
terraform apply
|
||||||
|
```
|
||||||
|
|
||||||
|
## GitHub Actions Workflow
|
||||||
|
|
||||||
|
- **On PR**: Runs `terraform plan` for review
|
||||||
|
- **On Push to main**: Runs `terraform apply` automatically
|
||||||
|
- **Manual**: Can trigger via Actions tab
|
||||||
|
|
||||||
|
## File Structure
|
||||||
|
|
||||||
|
```
|
||||||
|
├── .github/workflows/deploy.yml # CI/CD pipeline
|
||||||
|
├── main.tf # Authentik provider & brand config
|
||||||
|
├── variables.tf # All configurable variables
|
||||||
|
├── app-*.tf # Application configurations
|
||||||
|
├── ldap-outpost.tf # LDAP outpost config
|
||||||
|
├── source-google.tf # Google OAuth source
|
||||||
|
└── outputs.tf # Useful outputs
|
||||||
|
```
|
||||||
|
|
||||||
|
## Adding New Applications
|
||||||
|
|
||||||
|
### OAuth2/OIDC Application
|
||||||
|
|
||||||
|
```hcl
|
||||||
|
# app-myapp.tf
|
||||||
|
resource "authentik_provider_oauth2" "myapp" {
|
||||||
|
name = "MyApp"
|
||||||
|
authorization_flow = data.authentik_flow.default_authorization.id
|
||||||
|
client_id = "myapp"
|
||||||
|
client_type = "confidential"
|
||||||
|
|
||||||
|
redirect_uris = [
|
||||||
|
"${var.myapp_url}/oauth/callback"
|
||||||
|
]
|
||||||
|
|
||||||
|
property_mappings = data.authentik_property_mapping_provider_scope.oauth2.ids
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "authentik_application" "myapp" {
|
||||||
|
name = "MyApp"
|
||||||
|
slug = "myapp"
|
||||||
|
protocol_provider = authentik_provider_oauth2.myapp.id
|
||||||
|
|
||||||
|
meta_launch_url = var.myapp_url
|
||||||
|
meta_icon = "https://example.com/icon.png"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Proxy Authentication
|
||||||
|
|
||||||
|
```hcl
|
||||||
|
resource "authentik_provider_proxy" "myapp" {
|
||||||
|
name = "MyApp Proxy"
|
||||||
|
authorization_flow = data.authentik_flow.default_authorization.id
|
||||||
|
external_host = var.myapp_url
|
||||||
|
mode = "forward_single"
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "authentik_application" "myapp" {
|
||||||
|
name = "MyApp"
|
||||||
|
slug = "myapp"
|
||||||
|
protocol_provider = authentik_provider_proxy.myapp.id
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Terraform State
|
||||||
|
|
||||||
|
By default, state is stored locally. For production, configure remote backend:
|
||||||
|
|
||||||
|
```hcl
|
||||||
|
# backend.tf
|
||||||
|
terraform {
|
||||||
|
backend "s3" {
|
||||||
|
bucket = "your-terraform-state"
|
||||||
|
key = "authentik/terraform.tfstate"
|
||||||
|
region = "us-east-1"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Security Notes
|
||||||
|
|
||||||
|
- Never commit `terraform.tfvars` or any file with secrets
|
||||||
|
- Use GitHub Actions secrets for CI/CD
|
||||||
|
- API tokens should have minimal required permissions
|
||||||
|
- Rotate tokens periodically
|
||||||
|
|
||||||
|
## Requirements
|
||||||
|
|
||||||
|
| Name | Version |
|
||||||
|
|------|---------|
|
||||||
|
| terraform | >= 1.5.0 |
|
||||||
|
| authentik | >= 2024.0 |
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
MIT
|
||||||
52
app-argocd.tf
Normal file
52
app-argocd.tf
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
# =============================================================================
|
||||||
|
# ArgoCD - GitOps Continuous Delivery
|
||||||
|
# =============================================================================
|
||||||
|
|
||||||
|
data "authentik_property_mapping_provider_scope" "argocd" {
|
||||||
|
managed_list = [
|
||||||
|
"goauthentik.io/providers/oauth2/scope-openid",
|
||||||
|
"goauthentik.io/providers/oauth2/scope-email",
|
||||||
|
"goauthentik.io/providers/oauth2/scope-profile",
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "authentik_provider_oauth2" "argocd" {
|
||||||
|
name = "ArgoCD"
|
||||||
|
client_id = "argocd"
|
||||||
|
client_type = "confidential"
|
||||||
|
authorization_flow = data.authentik_flow.default_authorization.id
|
||||||
|
invalidation_flow = data.authentik_flow.default_invalidation.id
|
||||||
|
|
||||||
|
access_token_validity = "hours=1"
|
||||||
|
refresh_token_validity = "days=30"
|
||||||
|
|
||||||
|
property_mappings = data.authentik_property_mapping_provider_scope.argocd.ids
|
||||||
|
|
||||||
|
# ArgoCD callback URLs - TODO: Update to your domains
|
||||||
|
allowed_redirect_uris = [
|
||||||
|
{ matching_mode = "strict", url = "https://argo.your-tailnet.ts.net/auth/callback" },
|
||||||
|
{ matching_mode = "strict", url = "https://argocd.example.com/auth/callback" },
|
||||||
|
]
|
||||||
|
|
||||||
|
signing_key = data.authentik_certificate_key_pair.generated.id
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "authentik_application" "argocd" {
|
||||||
|
name = "ArgoCD"
|
||||||
|
slug = "argocd"
|
||||||
|
protocol_provider = authentik_provider_oauth2.argocd.id
|
||||||
|
|
||||||
|
meta_description = "GitOps Continuous Delivery"
|
||||||
|
meta_launch_url = "https://argocd.your-tailnet.ts.net" # TODO: Update
|
||||||
|
|
||||||
|
group = "DevOps"
|
||||||
|
}
|
||||||
|
|
||||||
|
output "argocd_client_id" {
|
||||||
|
value = authentik_provider_oauth2.argocd.client_id
|
||||||
|
}
|
||||||
|
|
||||||
|
output "argocd_client_secret" {
|
||||||
|
value = authentik_provider_oauth2.argocd.client_secret
|
||||||
|
sensitive = true
|
||||||
|
}
|
||||||
52
app-grafana.tf
Normal file
52
app-grafana.tf
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
# =============================================================================
|
||||||
|
# Grafana - Monitoring Dashboards
|
||||||
|
# =============================================================================
|
||||||
|
|
||||||
|
data "authentik_property_mapping_provider_scope" "grafana" {
|
||||||
|
managed_list = [
|
||||||
|
"goauthentik.io/providers/oauth2/scope-openid",
|
||||||
|
"goauthentik.io/providers/oauth2/scope-email",
|
||||||
|
"goauthentik.io/providers/oauth2/scope-profile",
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "authentik_provider_oauth2" "grafana" {
|
||||||
|
name = "Grafana"
|
||||||
|
client_id = "grafana"
|
||||||
|
client_type = "confidential"
|
||||||
|
authorization_flow = data.authentik_flow.default_authorization.id
|
||||||
|
invalidation_flow = data.authentik_flow.default_invalidation.id
|
||||||
|
|
||||||
|
access_token_validity = "hours=1"
|
||||||
|
refresh_token_validity = "days=30"
|
||||||
|
|
||||||
|
property_mappings = data.authentik_property_mapping_provider_scope.grafana.ids
|
||||||
|
|
||||||
|
# TODO: Update to your domains
|
||||||
|
allowed_redirect_uris = [
|
||||||
|
{ matching_mode = "strict", url = "https://grafana.your-tailnet.ts.net/login/generic_oauth" },
|
||||||
|
{ matching_mode = "strict", url = "https://grafana.example.com/login/generic_oauth" },
|
||||||
|
]
|
||||||
|
|
||||||
|
signing_key = data.authentik_certificate_key_pair.generated.id
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "authentik_application" "grafana" {
|
||||||
|
name = "Grafana"
|
||||||
|
slug = "grafana"
|
||||||
|
protocol_provider = authentik_provider_oauth2.grafana.id
|
||||||
|
|
||||||
|
meta_description = "Monitoring & Observability Dashboards"
|
||||||
|
meta_launch_url = "https://grafana.your-tailnet.ts.net" # TODO: Update
|
||||||
|
|
||||||
|
group = "Monitoring"
|
||||||
|
}
|
||||||
|
|
||||||
|
output "grafana_client_id" {
|
||||||
|
value = authentik_provider_oauth2.grafana.client_id
|
||||||
|
}
|
||||||
|
|
||||||
|
output "grafana_client_secret" {
|
||||||
|
value = authentik_provider_oauth2.grafana.client_secret
|
||||||
|
sensitive = true
|
||||||
|
}
|
||||||
51
app-home-assistant.tf
Normal file
51
app-home-assistant.tf
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
# =============================================================================
|
||||||
|
# Home Assistant - Smart Home
|
||||||
|
# =============================================================================
|
||||||
|
|
||||||
|
data "authentik_property_mapping_provider_scope" "home_assistant" {
|
||||||
|
managed_list = [
|
||||||
|
"goauthentik.io/providers/oauth2/scope-openid",
|
||||||
|
"goauthentik.io/providers/oauth2/scope-email",
|
||||||
|
"goauthentik.io/providers/oauth2/scope-profile",
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "authentik_provider_oauth2" "home_assistant" {
|
||||||
|
name = "Home Assistant"
|
||||||
|
client_id = "home-assistant"
|
||||||
|
client_type = "confidential"
|
||||||
|
authorization_flow = data.authentik_flow.default_authorization.id
|
||||||
|
invalidation_flow = data.authentik_flow.default_invalidation.id
|
||||||
|
|
||||||
|
access_token_validity = "hours=1"
|
||||||
|
refresh_token_validity = "days=30"
|
||||||
|
|
||||||
|
property_mappings = data.authentik_property_mapping_provider_scope.home_assistant.ids
|
||||||
|
|
||||||
|
# TODO: Update to your domain
|
||||||
|
allowed_redirect_uris = [
|
||||||
|
{ matching_mode = "strict", url = "https://home.your-tailnet.ts.net/auth/external/callback" },
|
||||||
|
]
|
||||||
|
|
||||||
|
signing_key = data.authentik_certificate_key_pair.generated.id
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "authentik_application" "home_assistant" {
|
||||||
|
name = "Home Assistant"
|
||||||
|
slug = "home-assistant"
|
||||||
|
protocol_provider = authentik_provider_oauth2.home_assistant.id
|
||||||
|
|
||||||
|
meta_description = "Smart Home Control"
|
||||||
|
meta_launch_url = "https://home.your-tailnet.ts.net" # TODO: Update
|
||||||
|
|
||||||
|
group = "Home"
|
||||||
|
}
|
||||||
|
|
||||||
|
output "home_assistant_client_id" {
|
||||||
|
value = authentik_provider_oauth2.home_assistant.client_id
|
||||||
|
}
|
||||||
|
|
||||||
|
output "home_assistant_client_secret" {
|
||||||
|
value = authentik_provider_oauth2.home_assistant.client_secret
|
||||||
|
sensitive = true
|
||||||
|
}
|
||||||
53
app-immich.tf
Normal file
53
app-immich.tf
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
# =============================================================================
|
||||||
|
# Immich - Photo Management
|
||||||
|
# =============================================================================
|
||||||
|
|
||||||
|
data "authentik_property_mapping_provider_scope" "immich" {
|
||||||
|
managed_list = [
|
||||||
|
"goauthentik.io/providers/oauth2/scope-openid",
|
||||||
|
"goauthentik.io/providers/oauth2/scope-email",
|
||||||
|
"goauthentik.io/providers/oauth2/scope-profile",
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "authentik_provider_oauth2" "immich" {
|
||||||
|
name = "Immich"
|
||||||
|
client_id = "immich"
|
||||||
|
client_type = "confidential"
|
||||||
|
authorization_flow = data.authentik_flow.default_authorization.id
|
||||||
|
invalidation_flow = data.authentik_flow.default_invalidation.id
|
||||||
|
|
||||||
|
access_token_validity = "hours=1"
|
||||||
|
refresh_token_validity = "days=30"
|
||||||
|
|
||||||
|
property_mappings = data.authentik_property_mapping_provider_scope.immich.ids
|
||||||
|
|
||||||
|
# TODO: Update to your domain
|
||||||
|
allowed_redirect_uris = [
|
||||||
|
{ matching_mode = "strict", url = "https://immich.your-tailnet.ts.net/auth/login" },
|
||||||
|
{ matching_mode = "strict", url = "https://immich.your-tailnet.ts.net/user-settings" },
|
||||||
|
{ matching_mode = "strict", url = "app.immich:///oauth-callback" },
|
||||||
|
]
|
||||||
|
|
||||||
|
signing_key = data.authentik_certificate_key_pair.generated.id
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "authentik_application" "immich" {
|
||||||
|
name = "Immich"
|
||||||
|
slug = "immich"
|
||||||
|
protocol_provider = authentik_provider_oauth2.immich.id
|
||||||
|
|
||||||
|
meta_description = "Photo & Video Backup"
|
||||||
|
meta_launch_url = "https://immich.your-tailnet.ts.net" # TODO: Update
|
||||||
|
|
||||||
|
group = "Media"
|
||||||
|
}
|
||||||
|
|
||||||
|
output "immich_client_id" {
|
||||||
|
value = authentik_provider_oauth2.immich.client_id
|
||||||
|
}
|
||||||
|
|
||||||
|
output "immich_client_secret" {
|
||||||
|
value = authentik_provider_oauth2.immich.client_secret
|
||||||
|
sensitive = true
|
||||||
|
}
|
||||||
66
app-proxy-arr-stack.tf
Normal file
66
app-proxy-arr-stack.tf
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
# =============================================================================
|
||||||
|
# Proxy Provider for Arr Stack (Sonarr, Radarr, Prowlarr)
|
||||||
|
# These apps don't support OIDC natively, use Authentik proxy auth
|
||||||
|
#
|
||||||
|
# Note: Each app needs its own provider in Authentik due to 1:1 mapping
|
||||||
|
# =============================================================================
|
||||||
|
|
||||||
|
# Forward auth provider - Sonarr
|
||||||
|
resource "authentik_provider_proxy" "sonarr" {
|
||||||
|
name = "Sonarr Proxy"
|
||||||
|
authorization_flow = data.authentik_flow.default_authorization.id
|
||||||
|
invalidation_flow = data.authentik_flow.default_invalidation.id
|
||||||
|
mode = "forward_single"
|
||||||
|
external_host = "https://sonarr.your-tailnet.ts.net" # TODO: Update
|
||||||
|
access_token_validity = "hours=24"
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "authentik_application" "sonarr" {
|
||||||
|
name = "Sonarr"
|
||||||
|
slug = "sonarr"
|
||||||
|
protocol_provider = authentik_provider_proxy.sonarr.id
|
||||||
|
meta_description = "TV Show Automation"
|
||||||
|
meta_launch_url = "https://sonarr.your-tailnet.ts.net" # TODO: Update
|
||||||
|
group = "Media"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Forward auth provider - Radarr
|
||||||
|
resource "authentik_provider_proxy" "radarr" {
|
||||||
|
name = "Radarr Proxy"
|
||||||
|
authorization_flow = data.authentik_flow.default_authorization.id
|
||||||
|
invalidation_flow = data.authentik_flow.default_invalidation.id
|
||||||
|
mode = "forward_single"
|
||||||
|
external_host = "https://radarr.your-tailnet.ts.net" # TODO: Update
|
||||||
|
access_token_validity = "hours=24"
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "authentik_application" "radarr" {
|
||||||
|
name = "Radarr"
|
||||||
|
slug = "radarr"
|
||||||
|
protocol_provider = authentik_provider_proxy.radarr.id
|
||||||
|
meta_description = "Movie Automation"
|
||||||
|
meta_launch_url = "https://radarr.your-tailnet.ts.net" # TODO: Update
|
||||||
|
group = "Media"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Forward auth provider - Prowlarr
|
||||||
|
resource "authentik_provider_proxy" "prowlarr" {
|
||||||
|
name = "Prowlarr Proxy"
|
||||||
|
authorization_flow = data.authentik_flow.default_authorization.id
|
||||||
|
invalidation_flow = data.authentik_flow.default_invalidation.id
|
||||||
|
mode = "forward_single"
|
||||||
|
external_host = "https://prowlarr.your-tailnet.ts.net" # TODO: Update
|
||||||
|
access_token_validity = "hours=24"
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "authentik_application" "prowlarr" {
|
||||||
|
name = "Prowlarr"
|
||||||
|
slug = "prowlarr"
|
||||||
|
protocol_provider = authentik_provider_proxy.prowlarr.id
|
||||||
|
meta_description = "Indexer Manager"
|
||||||
|
meta_launch_url = "https://prowlarr.your-tailnet.ts.net" # TODO: Update
|
||||||
|
group = "Media"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Note: To use forward auth with Traefik/nginx, configure the embedded outpost
|
||||||
|
# and add middleware to forward auth requests to Authentik
|
||||||
30
app-uptime-kuma.tf
Normal file
30
app-uptime-kuma.tf
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
# =============================================================================
|
||||||
|
# Uptime Kuma - Status Monitoring
|
||||||
|
# Uses proxy authentication (UK doesn't support native OIDC login)
|
||||||
|
# =============================================================================
|
||||||
|
|
||||||
|
resource "authentik_provider_proxy" "uptime_kuma" {
|
||||||
|
name = "Uptime Kuma Proxy"
|
||||||
|
authorization_flow = data.authentik_flow.default_authorization.id
|
||||||
|
invalidation_flow = data.authentik_flow.default_invalidation.id
|
||||||
|
mode = "forward_single"
|
||||||
|
|
||||||
|
external_host = "https://uptime.example.com" # TODO: Update
|
||||||
|
access_token_validity = "hours=24"
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "authentik_application" "uptime_kuma" {
|
||||||
|
name = "Uptime Kuma"
|
||||||
|
slug = "uptime-kuma"
|
||||||
|
protocol_provider = authentik_provider_proxy.uptime_kuma.id
|
||||||
|
|
||||||
|
meta_description = "Service Status Monitoring"
|
||||||
|
meta_launch_url = "https://uptime.example.com" # TODO: Update
|
||||||
|
|
||||||
|
group = "Monitoring"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Note: Configure your reverse proxy (nginx/traefik/cloudflare)
|
||||||
|
# to use Authentik forward auth before proxying to Uptime Kuma
|
||||||
|
#
|
||||||
|
# With disableAuth=true in UK, Authentik handles all authentication
|
||||||
49
ldap-outpost.tf
Normal file
49
ldap-outpost.tf
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
# =============================================================================
|
||||||
|
# LDAP Provider and Outpost for TrueNAS
|
||||||
|
# Allows LDAP-based authentication against Authentik
|
||||||
|
# =============================================================================
|
||||||
|
|
||||||
|
# LDAP Provider
|
||||||
|
resource "authentik_provider_ldap" "truenas" {
|
||||||
|
name = "TrueNAS LDAP"
|
||||||
|
base_dn = "dc=ldap,dc=example,dc=com" # TODO: Update to your domain
|
||||||
|
bind_flow = data.authentik_flow.default_authentication.id
|
||||||
|
unbind_flow = data.authentik_flow.default_invalidation.id
|
||||||
|
|
||||||
|
# Bind mode - direct means users bind with their own credentials
|
||||||
|
bind_mode = "direct"
|
||||||
|
|
||||||
|
# Search mode
|
||||||
|
search_mode = "direct"
|
||||||
|
|
||||||
|
# MFA support (optional)
|
||||||
|
mfa_support = false
|
||||||
|
}
|
||||||
|
|
||||||
|
# Application for LDAP
|
||||||
|
resource "authentik_application" "truenas_ldap" {
|
||||||
|
name = "TrueNAS LDAP"
|
||||||
|
slug = "truenas-ldap"
|
||||||
|
protocol_provider = authentik_provider_ldap.truenas.id
|
||||||
|
|
||||||
|
meta_description = "LDAP authentication for TrueNAS"
|
||||||
|
|
||||||
|
group = "Infrastructure"
|
||||||
|
}
|
||||||
|
|
||||||
|
# LDAP Outpost (standalone container needed for LDAP)
|
||||||
|
resource "authentik_outpost" "ldap" {
|
||||||
|
name = "LDAP Outpost"
|
||||||
|
type = "ldap"
|
||||||
|
protocol_providers = [authentik_provider_ldap.truenas.id]
|
||||||
|
|
||||||
|
config = jsonencode({
|
||||||
|
authentik_host = "https://authentik.example.com" # TODO: Update
|
||||||
|
authentik_host_insecure = false
|
||||||
|
log_level = "info"
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
output "ldap_base_dn" {
|
||||||
|
value = "dc=ldap,dc=example,dc=com" # TODO: Update to match above
|
||||||
|
}
|
||||||
80
main.tf
Normal file
80
main.tf
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
# =============================================================================
|
||||||
|
# Authentik Terraform Configuration
|
||||||
|
# Update the domain below to match your Authentik instance
|
||||||
|
# =============================================================================
|
||||||
|
|
||||||
|
# Decrypt secrets with SOPS
|
||||||
|
data "sops_file" "secrets" {
|
||||||
|
source_file = "secrets.enc.yaml"
|
||||||
|
}
|
||||||
|
|
||||||
|
provider "authentik" {
|
||||||
|
url = var.authentik_url
|
||||||
|
token = data.sops_file.secrets.data["authentik_token"]
|
||||||
|
}
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# Data Sources - Existing Resources
|
||||||
|
# =============================================================================
|
||||||
|
|
||||||
|
# Default authentication flow
|
||||||
|
data "authentik_flow" "default_authentication" {
|
||||||
|
slug = "default-authentication-flow"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Default authorization flow (implicit consent)
|
||||||
|
data "authentik_flow" "default_authorization" {
|
||||||
|
slug = "default-provider-authorization-implicit-consent"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Default invalidation flow
|
||||||
|
data "authentik_flow" "default_invalidation" {
|
||||||
|
slug = "default-invalidation-flow"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Default enrollment flow (for social login)
|
||||||
|
data "authentik_flow" "default_enrollment" {
|
||||||
|
slug = "default-source-enrollment"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Get certificate for signing
|
||||||
|
data "authentik_certificate_key_pair" "generated" {
|
||||||
|
name = "authentik Self-signed Certificate"
|
||||||
|
}
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# Brand Configuration
|
||||||
|
# =============================================================================
|
||||||
|
|
||||||
|
data "authentik_brand" "default" {
|
||||||
|
domain = "authentik-default"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Update brand with proper domain
|
||||||
|
resource "authentik_brand" "main" {
|
||||||
|
domain = "authentik.example.com" # TODO: Update to your domain
|
||||||
|
default = false
|
||||||
|
branding_title = "My Lab" # TODO: Update to your org name
|
||||||
|
branding_logo = "/static/dist/assets/icons/icon_left_brand.svg"
|
||||||
|
branding_favicon = "/static/dist/assets/icons/icon.png"
|
||||||
|
|
||||||
|
flow_authentication = data.authentik_flow.default_authentication.id
|
||||||
|
flow_invalidation = data.authentik_flow.default_invalidation.id
|
||||||
|
}
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# Groups
|
||||||
|
# =============================================================================
|
||||||
|
|
||||||
|
resource "authentik_group" "admins" {
|
||||||
|
name = "Admins"
|
||||||
|
is_superuser = true
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "authentik_group" "users" {
|
||||||
|
name = "Users"
|
||||||
|
}
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# Applications are defined in applications/*.tf
|
||||||
|
# =============================================================================
|
||||||
14
outputs.tf
Normal file
14
outputs.tf
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
output "authentik_url" {
|
||||||
|
description = "Authentik instance URL"
|
||||||
|
value = var.authentik_url
|
||||||
|
}
|
||||||
|
|
||||||
|
output "admin_group_id" {
|
||||||
|
description = "Admin group ID for RBAC"
|
||||||
|
value = authentik_group.admins.id
|
||||||
|
}
|
||||||
|
|
||||||
|
output "users_group_id" {
|
||||||
|
description = "Users group ID"
|
||||||
|
value = authentik_group.users.id
|
||||||
|
}
|
||||||
31
source-google.tf
Normal file
31
source-google.tf
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
# =============================================================================
|
||||||
|
# Google Workspace Federation
|
||||||
|
# Allow users to sign in with their Google Workspace accounts
|
||||||
|
# =============================================================================
|
||||||
|
|
||||||
|
# Google OAuth Source
|
||||||
|
resource "authentik_source_oauth" "google" {
|
||||||
|
name = "Google Workspace"
|
||||||
|
slug = "google"
|
||||||
|
authentication_flow = data.authentik_flow.default_authentication.id
|
||||||
|
enrollment_flow = data.authentik_flow.default_enrollment.id
|
||||||
|
|
||||||
|
provider_type = "google"
|
||||||
|
consumer_key = data.sops_file.secrets.data["google_client_id"]
|
||||||
|
consumer_secret = data.sops_file.secrets.data["google_client_secret"]
|
||||||
|
|
||||||
|
# PKCE method - S256 is recommended
|
||||||
|
pkce = "S256"
|
||||||
|
|
||||||
|
# User matching - link by email
|
||||||
|
user_matching_mode = "email_link"
|
||||||
|
|
||||||
|
# Policy engine
|
||||||
|
policy_engine_mode = "any"
|
||||||
|
|
||||||
|
# Enable for login page
|
||||||
|
enabled = true
|
||||||
|
}
|
||||||
|
|
||||||
|
# Note: After applying, the Google login button will appear on the Authentik login page.
|
||||||
|
# Users with matching emails will be linked; new users will be enrolled.
|
||||||
23
terraform.tfvars.example
Normal file
23
terraform.tfvars.example
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
# Example terraform.tfvars - Copy to terraform.tfvars and fill in your values
|
||||||
|
# NEVER commit terraform.tfvars to git!
|
||||||
|
|
||||||
|
# Authentik Connection (required)
|
||||||
|
authentik_url = "https://auth.example.com"
|
||||||
|
authentik_token = "your-api-token-here"
|
||||||
|
|
||||||
|
# Google OAuth (optional - leave empty to skip)
|
||||||
|
google_client_id = ""
|
||||||
|
google_client_secret = ""
|
||||||
|
|
||||||
|
# Application URLs (set the ones you want to configure)
|
||||||
|
argocd_url = "https://argocd.example.com"
|
||||||
|
grafana_url = "https://grafana.example.com"
|
||||||
|
home_assistant_url = "https://home.example.com"
|
||||||
|
immich_url = "https://photos.example.com"
|
||||||
|
uptime_kuma_url = "https://status.example.com"
|
||||||
|
sonarr_url = "https://sonarr.example.com"
|
||||||
|
radarr_url = "https://radarr.example.com"
|
||||||
|
prowlarr_url = "https://prowlarr.example.com"
|
||||||
|
|
||||||
|
# LDAP Configuration
|
||||||
|
ldap_base_dn = "dc=ldap,dc=example,dc=com"
|
||||||
90
variables.tf
Normal file
90
variables.tf
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
################################################################################
|
||||||
|
# Authentik Terraform Variables
|
||||||
|
#
|
||||||
|
# Set these via:
|
||||||
|
# - GitHub Actions secrets (recommended)
|
||||||
|
# - terraform.tfvars (local dev only - never commit!)
|
||||||
|
# - Environment variables (TF_VAR_*)
|
||||||
|
################################################################################
|
||||||
|
|
||||||
|
# Authentik Connection
|
||||||
|
variable "authentik_url" {
|
||||||
|
type = string
|
||||||
|
description = "Authentik server URL (e.g., https://auth.example.com)"
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "authentik_token" {
|
||||||
|
type = string
|
||||||
|
sensitive = true
|
||||||
|
description = "Authentik API token"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Google OAuth (optional)
|
||||||
|
variable "google_client_id" {
|
||||||
|
type = string
|
||||||
|
default = ""
|
||||||
|
description = "Google OAuth client ID"
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "google_client_secret" {
|
||||||
|
type = string
|
||||||
|
sensitive = true
|
||||||
|
default = ""
|
||||||
|
description = "Google OAuth client secret"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Application URLs
|
||||||
|
variable "argocd_url" {
|
||||||
|
type = string
|
||||||
|
default = ""
|
||||||
|
description = "ArgoCD URL for SSO"
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "grafana_url" {
|
||||||
|
type = string
|
||||||
|
default = ""
|
||||||
|
description = "Grafana URL for SSO"
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "home_assistant_url" {
|
||||||
|
type = string
|
||||||
|
default = ""
|
||||||
|
description = "Home Assistant URL for proxy auth"
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "immich_url" {
|
||||||
|
type = string
|
||||||
|
default = ""
|
||||||
|
description = "Immich URL for proxy auth"
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "uptime_kuma_url" {
|
||||||
|
type = string
|
||||||
|
default = ""
|
||||||
|
description = "Uptime Kuma URL for proxy auth"
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "sonarr_url" {
|
||||||
|
type = string
|
||||||
|
default = ""
|
||||||
|
description = "Sonarr URL for proxy auth"
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "radarr_url" {
|
||||||
|
type = string
|
||||||
|
default = ""
|
||||||
|
description = "Radarr URL for proxy auth"
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "prowlarr_url" {
|
||||||
|
type = string
|
||||||
|
default = ""
|
||||||
|
description = "Prowlarr URL for proxy auth"
|
||||||
|
}
|
||||||
|
|
||||||
|
# LDAP Configuration
|
||||||
|
variable "ldap_base_dn" {
|
||||||
|
type = string
|
||||||
|
default = "dc=ldap,dc=example,dc=com"
|
||||||
|
description = "LDAP base DN"
|
||||||
|
}
|
||||||
14
versions.tf
Normal file
14
versions.tf
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
terraform {
|
||||||
|
required_version = ">= 1.5.0"
|
||||||
|
|
||||||
|
required_providers {
|
||||||
|
authentik = {
|
||||||
|
source = "goauthentik/authentik"
|
||||||
|
version = "~> 2025.2"
|
||||||
|
}
|
||||||
|
sops = {
|
||||||
|
source = "carlpett/sops"
|
||||||
|
version = "~> 1.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user