mirror of
https://github.com/ghndrx/authentik-terraform.git
synced 2026-02-10 06:44:58 +00:00
feat(security): add comprehensive security policies and RBAC
- Add security-policies.tf: - Strong password policy (12 chars, HIBP check, zxcvbn scoring) - Password reuse prevention (last 5 passwords) - Brute force protection (reputation policy, 5 attempt threshold) - MFA stages: TOTP, WebAuthn/Passkeys, recovery codes - MFA validation stage with configurable enforcement - Admin-only and MFA-required expression policies - Add rbac-groups.tf: - Media group (Sonarr, Radarr, etc.) - Infrastructure group (Grafana, ArgoCD, etc.) - Home Automation group (Home Assistant) - Group-based access policies - Fix main.tf: Remove SOPS, use variables for token - Fix versions.tf: Remove unused SOPS provider - Update README with security documentation
This commit is contained in:
54
README.md
54
README.md
@@ -8,6 +8,8 @@ Infrastructure as Code for Authentik identity provider - manage applications, pr
|
|||||||
- **Proxy Authentication**: Home Assistant, Immich, Uptime Kuma, *arr stack
|
- **Proxy Authentication**: Home Assistant, Immich, Uptime Kuma, *arr stack
|
||||||
- **LDAP Outpost**: For legacy application support
|
- **LDAP Outpost**: For legacy application support
|
||||||
- **Google OAuth Source**: Social login integration
|
- **Google OAuth Source**: Social login integration
|
||||||
|
- **Security Policies**: Strong passwords, MFA, brute-force protection
|
||||||
|
- **RBAC Groups**: Role-based access control for applications
|
||||||
|
|
||||||
## Quick Start
|
## Quick Start
|
||||||
|
|
||||||
@@ -76,6 +78,8 @@ terraform apply
|
|||||||
├── .github/workflows/deploy.yml # CI/CD pipeline
|
├── .github/workflows/deploy.yml # CI/CD pipeline
|
||||||
├── main.tf # Authentik provider & brand config
|
├── main.tf # Authentik provider & brand config
|
||||||
├── variables.tf # All configurable variables
|
├── variables.tf # All configurable variables
|
||||||
|
├── security-policies.tf # Password, MFA, brute-force policies
|
||||||
|
├── rbac-groups.tf # RBAC groups and access policies
|
||||||
├── app-*.tf # Application configurations
|
├── app-*.tf # Application configurations
|
||||||
├── ldap-outpost.tf # LDAP outpost config
|
├── ldap-outpost.tf # LDAP outpost config
|
||||||
├── source-google.tf # Google OAuth source
|
├── source-google.tf # Google OAuth source
|
||||||
@@ -143,12 +147,62 @@ terraform {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Security Policies (security-policies.tf)
|
||||||
|
|
||||||
|
This configuration includes enterprise-grade security controls:
|
||||||
|
|
||||||
|
### Password Policy
|
||||||
|
- Minimum 12 characters
|
||||||
|
- Requires uppercase, lowercase, digits, and symbols
|
||||||
|
- **Have I Been Pwned** integration - rejects breached passwords
|
||||||
|
- **zxcvbn** password strength scoring (requires "strong" level 3/4)
|
||||||
|
- Password reuse prevention (last 5 passwords)
|
||||||
|
|
||||||
|
### Multi-Factor Authentication
|
||||||
|
- TOTP authenticator apps (Google Authenticator, Authy, etc.)
|
||||||
|
- WebAuthn/Passkeys (YubiKey, Touch ID, Windows Hello)
|
||||||
|
- Static recovery codes (10 codes, 12 characters each)
|
||||||
|
- Configurable enforcement: skip, deny, or force configuration
|
||||||
|
|
||||||
|
### Brute Force Protection
|
||||||
|
- Reputation-based blocking after 5 failed attempts
|
||||||
|
- Blocks by IP address and username
|
||||||
|
- Execution logging for audit trail
|
||||||
|
|
||||||
|
### To Enable MFA Enforcement:
|
||||||
|
1. Deploy these policies with `terraform apply`
|
||||||
|
2. In Authentik UI: Edit your authentication flow
|
||||||
|
3. Add the `mfa-validation` stage after the password stage
|
||||||
|
4. Set `not_configured_action` to `deny` for strict enforcement
|
||||||
|
|
||||||
|
## RBAC Groups (rbac-groups.tf)
|
||||||
|
|
||||||
|
Role-based access control with three predefined groups:
|
||||||
|
|
||||||
|
| Group | Purpose | Example Apps |
|
||||||
|
|-------|---------|--------------|
|
||||||
|
| Media | Media server access | Sonarr, Radarr, Prowlarr, Plex |
|
||||||
|
| Infrastructure | DevOps/monitoring | Grafana, ArgoCD, Portainer |
|
||||||
|
| Home Automation | Smart home | Home Assistant |
|
||||||
|
|
||||||
|
Admins automatically have access to all groups. Bind policies to applications:
|
||||||
|
|
||||||
|
```hcl
|
||||||
|
resource "authentik_policy_binding" "grafana_infra_access" {
|
||||||
|
target = authentik_application.grafana.uuid
|
||||||
|
policy = authentik_policy_expression.infrastructure_access.id
|
||||||
|
order = 0
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
## Security Notes
|
## Security Notes
|
||||||
|
|
||||||
- Never commit `terraform.tfvars` or any file with secrets
|
- Never commit `terraform.tfvars` or any file with secrets
|
||||||
- Use GitHub Actions secrets for CI/CD
|
- Use GitHub Actions secrets for CI/CD
|
||||||
- API tokens should have minimal required permissions
|
- API tokens should have minimal required permissions
|
||||||
- Rotate tokens periodically
|
- Rotate tokens periodically
|
||||||
|
- Enable execution logging for security audit trails
|
||||||
|
- Review login events in Authentik's Events log regularly
|
||||||
|
|
||||||
## Requirements
|
## Requirements
|
||||||
|
|
||||||
|
|||||||
12
main.tf
12
main.tf
@@ -3,14 +3,14 @@
|
|||||||
# Update the domain below to match your Authentik instance
|
# Update the domain below to match your Authentik instance
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
|
|
||||||
# Decrypt secrets with SOPS
|
# Authentik Provider Configuration
|
||||||
data "sops_file" "secrets" {
|
# Token provided via:
|
||||||
source_file = "secrets.enc.yaml"
|
# - GitHub Actions secrets (CI/CD)
|
||||||
}
|
# - terraform.tfvars (local dev - never commit!)
|
||||||
|
# - TF_VAR_authentik_token environment variable
|
||||||
provider "authentik" {
|
provider "authentik" {
|
||||||
url = var.authentik_url
|
url = var.authentik_url
|
||||||
token = data.sops_file.secrets.data["authentik_token"]
|
token = var.authentik_token
|
||||||
}
|
}
|
||||||
|
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
|
|||||||
89
rbac-groups.tf
Normal file
89
rbac-groups.tf
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
# =============================================================================
|
||||||
|
# RBAC Groups and Application Permissions
|
||||||
|
# Defines user groups and their application access
|
||||||
|
# =============================================================================
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
# Core Groups (extend from main.tf)
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
# Media group - access to Sonarr, Radarr, Prowlarr, etc.
|
||||||
|
resource "authentik_group" "media" {
|
||||||
|
name = "Media"
|
||||||
|
parent = authentik_group.users.id
|
||||||
|
}
|
||||||
|
|
||||||
|
# Infrastructure group - access to monitoring, CI/CD tools
|
||||||
|
resource "authentik_group" "infrastructure" {
|
||||||
|
name = "Infrastructure"
|
||||||
|
parent = authentik_group.users.id
|
||||||
|
}
|
||||||
|
|
||||||
|
# Home Automation group - Home Assistant access
|
||||||
|
resource "authentik_group" "home_automation" {
|
||||||
|
name = "Home Automation"
|
||||||
|
parent = authentik_group.users.id
|
||||||
|
}
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
# Group-based Access Policies
|
||||||
|
# Bind these to applications to restrict access
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
resource "authentik_policy_expression" "media_access" {
|
||||||
|
name = "media-group-access"
|
||||||
|
expression = <<-EOT
|
||||||
|
return ak_is_group_member(request.user, name="Media") or ak_is_group_member(request.user, name="Admins")
|
||||||
|
EOT
|
||||||
|
execution_logging = true
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "authentik_policy_expression" "infrastructure_access" {
|
||||||
|
name = "infrastructure-group-access"
|
||||||
|
expression = <<-EOT
|
||||||
|
return ak_is_group_member(request.user, name="Infrastructure") or ak_is_group_member(request.user, name="Admins")
|
||||||
|
EOT
|
||||||
|
execution_logging = true
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "authentik_policy_expression" "home_automation_access" {
|
||||||
|
name = "home-automation-group-access"
|
||||||
|
expression = <<-EOT
|
||||||
|
return ak_is_group_member(request.user, name="Home Automation") or ak_is_group_member(request.user, name="Admins")
|
||||||
|
EOT
|
||||||
|
execution_logging = true
|
||||||
|
}
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
# Example: Bind policy to an application
|
||||||
|
# Uncomment and modify for your applications
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
# resource "authentik_policy_binding" "sonarr_media_access" {
|
||||||
|
# target = authentik_application.sonarr.uuid
|
||||||
|
# policy = authentik_policy_expression.media_access.id
|
||||||
|
# order = 0
|
||||||
|
# }
|
||||||
|
#
|
||||||
|
# resource "authentik_policy_binding" "grafana_infra_access" {
|
||||||
|
# target = authentik_application.grafana.uuid
|
||||||
|
# policy = authentik_policy_expression.infrastructure_access.id
|
||||||
|
# order = 0
|
||||||
|
# }
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
# Outputs
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
output "media_group_id" {
|
||||||
|
description = "ID of the Media group"
|
||||||
|
value = authentik_group.media.id
|
||||||
|
}
|
||||||
|
|
||||||
|
output "infrastructure_group_id" {
|
||||||
|
description = "ID of the Infrastructure group"
|
||||||
|
value = authentik_group.infrastructure.id
|
||||||
|
}
|
||||||
|
|
||||||
|
output "home_automation_group_id" {
|
||||||
|
description = "ID of the Home Automation group"
|
||||||
|
value = authentik_group.home_automation.id
|
||||||
|
}
|
||||||
178
security-policies.tf
Normal file
178
security-policies.tf
Normal file
@@ -0,0 +1,178 @@
|
|||||||
|
# =============================================================================
|
||||||
|
# Security Policies for Authentik
|
||||||
|
# Provides password requirements, MFA, and brute-force protection
|
||||||
|
# =============================================================================
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
# Password Policy - Strong requirements with breach checking
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
resource "authentik_policy_password" "strong_password" {
|
||||||
|
name = "strong-password-policy"
|
||||||
|
error_message = "Password does not meet security requirements"
|
||||||
|
|
||||||
|
# Minimum length
|
||||||
|
length_min = 12
|
||||||
|
|
||||||
|
# Character requirements
|
||||||
|
amount_digits = 1
|
||||||
|
amount_lowercase = 1
|
||||||
|
amount_uppercase = 1
|
||||||
|
amount_symbols = 1
|
||||||
|
|
||||||
|
# Enable Have I Been Pwned checking
|
||||||
|
check_have_i_been_pwned = true
|
||||||
|
hibp_allowed_count = 0 # Reject any password found in breaches
|
||||||
|
|
||||||
|
# Enable zxcvbn password strength checking
|
||||||
|
check_zxcvbn = true
|
||||||
|
zxcvbn_score_threshold = 3 # Require "strong" passwords (0-4 scale)
|
||||||
|
|
||||||
|
check_static_rules = true
|
||||||
|
execution_logging = true
|
||||||
|
}
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
# Unique Password Policy - Prevent password reuse
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
resource "authentik_policy_unique_password" "no_reuse" {
|
||||||
|
name = "no-password-reuse"
|
||||||
|
num_historical_passwords = 5 # Remember last 5 passwords
|
||||||
|
execution_logging = true
|
||||||
|
}
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
# Password Expiry Policy - Force periodic password changes (optional)
|
||||||
|
# Uncomment to enable password expiration
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
# resource "authentik_policy_expiry" "password_expiry" {
|
||||||
|
# name = "password-expiry-90-days"
|
||||||
|
# days = 90
|
||||||
|
# deny_only = false
|
||||||
|
# execution_logging = true
|
||||||
|
# }
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
# Reputation Policy - Brute force protection
|
||||||
|
# Blocks IPs/usernames after repeated failed attempts
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
resource "authentik_policy_reputation" "brute_force_protection" {
|
||||||
|
name = "brute-force-protection"
|
||||||
|
check_ip = true
|
||||||
|
check_username = true
|
||||||
|
threshold = 5 # Block after 5 failed attempts
|
||||||
|
execution_logging = true
|
||||||
|
}
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
# TOTP Setup Stage - Allow users to configure MFA
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
resource "authentik_stage_authenticator_totp" "totp_setup" {
|
||||||
|
name = "totp-setup"
|
||||||
|
digits = "6"
|
||||||
|
friendly_name = "Authenticator App (TOTP)"
|
||||||
|
}
|
||||||
|
|
||||||
|
# WebAuthn/Passkeys Setup Stage
|
||||||
|
resource "authentik_stage_authenticator_webauthn" "webauthn_setup" {
|
||||||
|
name = "webauthn-setup"
|
||||||
|
friendly_name = "Security Key / Passkey"
|
||||||
|
user_verification = "preferred"
|
||||||
|
resident_key_requirement = "preferred" # Support passkeys
|
||||||
|
}
|
||||||
|
|
||||||
|
# Static Recovery Codes Setup
|
||||||
|
resource "authentik_stage_authenticator_static" "static_setup" {
|
||||||
|
name = "static-recovery-codes"
|
||||||
|
friendly_name = "Recovery Codes"
|
||||||
|
token_count = 10
|
||||||
|
token_length = 12
|
||||||
|
}
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
# MFA Validation Stage - Require MFA during authentication
|
||||||
|
# Configure with "deny" to require MFA, or "configure" to prompt setup
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
resource "authentik_stage_authenticator_validate" "mfa_validation" {
|
||||||
|
name = "mfa-validation"
|
||||||
|
|
||||||
|
# Options: skip, deny, configure
|
||||||
|
# - skip: Don't require MFA if not configured
|
||||||
|
# - deny: Block login if MFA not configured (after enabling, users need MFA)
|
||||||
|
# - configure: Force users to set up MFA if not configured
|
||||||
|
not_configured_action = "configure"
|
||||||
|
|
||||||
|
# Supported authenticator types
|
||||||
|
device_classes = [
|
||||||
|
"totp", # Authenticator apps
|
||||||
|
"webauthn", # Security keys / Passkeys
|
||||||
|
"static", # Recovery codes
|
||||||
|
]
|
||||||
|
|
||||||
|
# Link to setup stages for "configure" action
|
||||||
|
configuration_stages = [
|
||||||
|
authentik_stage_authenticator_totp.totp_setup.id,
|
||||||
|
authentik_stage_authenticator_webauthn.webauthn_setup.id,
|
||||||
|
authentik_stage_authenticator_static.static_setup.id,
|
||||||
|
]
|
||||||
|
|
||||||
|
# Re-authenticate after 12 hours even if "remember this device" is used
|
||||||
|
last_auth_threshold = "hours=12"
|
||||||
|
|
||||||
|
# WebAuthn settings
|
||||||
|
webauthn_user_verification = "preferred"
|
||||||
|
}
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
# Expression Policy - Admin-only access for sensitive apps
|
||||||
|
# Example: Bind this to admin-only applications
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
resource "authentik_policy_expression" "admin_only" {
|
||||||
|
name = "admin-only-access"
|
||||||
|
expression = <<-EOT
|
||||||
|
return ak_is_group_member(request.user, name="Admins")
|
||||||
|
EOT
|
||||||
|
execution_logging = true
|
||||||
|
}
|
||||||
|
|
||||||
|
# Expression Policy - Require MFA for app access
|
||||||
|
resource "authentik_policy_expression" "require_mfa_configured" {
|
||||||
|
name = "require-mfa-configured"
|
||||||
|
expression = <<-EOT
|
||||||
|
from authentik.stages.authenticator.models import Device
|
||||||
|
|
||||||
|
# Check if user has any MFA device configured
|
||||||
|
devices = Device.objects.filter(user=request.user, confirmed=True)
|
||||||
|
return devices.exists()
|
||||||
|
EOT
|
||||||
|
execution_logging = true
|
||||||
|
}
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
# GeoIP Policy - Block logins from suspicious locations (optional)
|
||||||
|
# Requires GeoIP database configured in Authentik
|
||||||
|
# Uncomment to enable
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
# resource "authentik_policy_geoip" "block_high_risk_countries" {
|
||||||
|
# name = "block-high-risk-countries"
|
||||||
|
# asns = []
|
||||||
|
# countries = [] # Add country codes to block, e.g., ["RU", "CN", "KP"]
|
||||||
|
# execution_logging = true
|
||||||
|
# }
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
# Outputs
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
output "password_policy_id" {
|
||||||
|
description = "ID of the strong password policy"
|
||||||
|
value = authentik_policy_password.strong_password.id
|
||||||
|
}
|
||||||
|
|
||||||
|
output "mfa_validation_stage_id" {
|
||||||
|
description = "ID of the MFA validation stage - bind to authentication flow"
|
||||||
|
value = authentik_stage_authenticator_validate.mfa_validation.id
|
||||||
|
}
|
||||||
|
|
||||||
|
output "admin_policy_id" {
|
||||||
|
description = "ID of the admin-only policy - bind to sensitive applications"
|
||||||
|
value = authentik_policy_expression.admin_only.id
|
||||||
|
}
|
||||||
@@ -6,9 +6,5 @@ terraform {
|
|||||||
source = "goauthentik/authentik"
|
source = "goauthentik/authentik"
|
||||||
version = "~> 2025.2"
|
version = "~> 2025.2"
|
||||||
}
|
}
|
||||||
sops = {
|
|
||||||
source = "carlpett/sops"
|
|
||||||
version = "~> 1.0"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user