This commit implements a complete Zitadel identity provider deployment with automated DNS management using vrije.cloud domain. ## Infrastructure Changes ### DNS Management - Migrated from deprecated hetznerdns provider to modern hcloud provider v1.57+ - Automated DNS record creation for client subdomains (test.vrije.cloud) - Automated wildcard DNS for service subdomains (*.test.vrije.cloud) - Supports both IPv4 (A) and IPv6 (AAAA) records ### Zitadel Deployment - Added complete Zitadel role with PostgreSQL 16 database - Configured Zitadel v2.63.7 with proper external domain settings - Implemented first instance setup with admin user creation - Set up database connection with proper user and admin credentials - Configured email verification bypass for first admin user ### Traefik Updates - Upgraded from v3.0 to v3.2 for better Docker API compatibility - Added manual routing configuration in dynamic.yml for Zitadel - Configured HTTP/2 Cleartext (h2c) backend for Zitadel service - Added Zitadel-specific security headers middleware - Fixed Docker API version compatibility issues ### Secrets Management - Added Zitadel credentials to test client secrets - Generated proper 32-character masterkey (Zitadel requirement) - Created admin password with symbol complexity requirement - Added zitadel_domain configuration ## Deployment Details Test environment now accessible at: - Server: test.vrije.cloud (78.47.191.38) - Zitadel: https://zitadel.test.vrije.cloud/ - Admin user: admin@test.zitadel.test.vrije.cloud Successfully tested: - HTTPS with Let's Encrypt SSL certificate - Admin login with 2FA setup - First instance initialization Fixes #3 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Pieter <pieter@kolabnow.com> Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
parent
6bc8e508c6
commit
054e0e1e87
15 changed files with 383 additions and 66 deletions
43
ansible/playbooks/deploy.yml
Normal file
43
ansible/playbooks/deploy.yml
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
---
|
||||
# Deploy applications to client servers
|
||||
# This playbook deploys Zitadel, Nextcloud, and other applications
|
||||
|
||||
- name: Deploy applications to client servers
|
||||
hosts: all
|
||||
become: yes
|
||||
|
||||
pre_tasks:
|
||||
- name: Gather facts
|
||||
setup:
|
||||
|
||||
- name: Determine client name from hostname
|
||||
set_fact:
|
||||
client_name: "{{ inventory_hostname }}"
|
||||
|
||||
- name: Load client secrets
|
||||
community.sops.load_vars:
|
||||
file: "{{ playbook_dir }}/../../secrets/clients/{{ client_name }}.sops.yaml"
|
||||
name: client_secrets
|
||||
age_key: "{{ lookup('env', 'SOPS_AGE_KEY_FILE') }}"
|
||||
no_log: true
|
||||
|
||||
- name: Set Zitadel domain from secrets
|
||||
set_fact:
|
||||
zitadel_domain: "{{ client_secrets.zitadel_domain }}"
|
||||
when: client_secrets.zitadel_domain is defined
|
||||
|
||||
roles:
|
||||
- role: zitadel
|
||||
|
||||
post_tasks:
|
||||
- name: Display deployment summary
|
||||
debug:
|
||||
msg: |
|
||||
Deployment complete for client: {{ client_name }}
|
||||
|
||||
Zitadel: https://{{ zitadel_domain }}
|
||||
|
||||
Next steps:
|
||||
1. Login to Zitadel with the admin credentials
|
||||
2. Change the admin password
|
||||
3. Configure OIDC applications for Nextcloud (when deployed)
|
||||
|
|
@ -1,8 +1,8 @@
|
|||
---
|
||||
# Default variables for traefik role
|
||||
|
||||
# Traefik version
|
||||
traefik_version: "v3.0"
|
||||
# Traefik version (v3.2+ fixes Docker API compatibility)
|
||||
traefik_version: "v3.2"
|
||||
|
||||
# Let's Encrypt configuration
|
||||
traefik_acme_email: "admin@example.com" # Override this!
|
||||
|
|
|
|||
|
|
@ -6,6 +6,9 @@ services:
|
|||
image: traefik:{{ traefik_version }}
|
||||
container_name: traefik
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
# Fix Docker API version compatibility - use 1.44 for modern Docker
|
||||
- DOCKER_API_VERSION=1.44
|
||||
security_opt:
|
||||
- no-new-privileges:true
|
||||
ports:
|
||||
|
|
|
|||
|
|
@ -2,7 +2,33 @@
|
|||
# Managed by Ansible - do not edit manually
|
||||
|
||||
http:
|
||||
routers:
|
||||
# Zitadel identity provider
|
||||
zitadel:
|
||||
rule: "Host(`zitadel.test.vrije.cloud`)"
|
||||
service: zitadel
|
||||
entryPoints:
|
||||
- websecure
|
||||
tls:
|
||||
certResolver: letsencrypt
|
||||
middlewares:
|
||||
- zitadel-headers
|
||||
|
||||
services:
|
||||
# Zitadel service
|
||||
zitadel:
|
||||
loadBalancer:
|
||||
servers:
|
||||
- url: "h2c://zitadel:8080"
|
||||
|
||||
middlewares:
|
||||
# Zitadel-specific headers
|
||||
zitadel-headers:
|
||||
headers:
|
||||
stsSeconds: 31536000
|
||||
stsIncludeSubdomains: true
|
||||
stsPreload: true
|
||||
|
||||
# Security headers
|
||||
security-headers:
|
||||
headers:
|
||||
|
|
|
|||
33
ansible/roles/zitadel/defaults/main.yml
Normal file
33
ansible/roles/zitadel/defaults/main.yml
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
---
|
||||
# Zitadel Default Variables
|
||||
|
||||
# Zitadel version (pin explicitly)
|
||||
zitadel_version: "v2.63.7"
|
||||
|
||||
# PostgreSQL version for Zitadel database
|
||||
postgres_version: "16-alpine"
|
||||
|
||||
# Admin user (password from secrets)
|
||||
zitadel_admin_username: "admin"
|
||||
|
||||
# Console client ID (Zitadel's built-in admin console)
|
||||
zitadel_console_client_id: "251896714278772225@ptt"
|
||||
|
||||
# OIDC configuration
|
||||
zitadel_oidc_token_lifetime: "12h"
|
||||
zitadel_oidc_refresh_lifetime: "720h"
|
||||
|
||||
# Resource limits
|
||||
zitadel_memory_limit: "512M"
|
||||
zitadel_cpu_limit: "1.0"
|
||||
|
||||
# Database configuration
|
||||
zitadel_db_user: "zitadel"
|
||||
zitadel_db_name: "zitadel"
|
||||
|
||||
# Network configuration
|
||||
zitadel_network: "zitadel-internal"
|
||||
zitadel_traefik_network: "traefik"
|
||||
|
||||
# Directory for Zitadel configuration
|
||||
zitadel_config_dir: "/opt/docker/zitadel"
|
||||
9
ansible/roles/zitadel/handlers/main.yml
Normal file
9
ansible/roles/zitadel/handlers/main.yml
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
---
|
||||
# Handlers for Zitadel role
|
||||
|
||||
- name: Restart Zitadel
|
||||
community.docker.docker_compose_v2:
|
||||
project_src: "{{ zitadel_config_dir }}"
|
||||
services:
|
||||
- zitadel
|
||||
state: restarted
|
||||
32
ansible/roles/zitadel/tasks/bootstrap.yml
Normal file
32
ansible/roles/zitadel/tasks/bootstrap.yml
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
---
|
||||
# Bootstrap tasks for initial Zitadel configuration
|
||||
|
||||
- name: Check if bootstrap already completed
|
||||
stat:
|
||||
path: "{{ zitadel_config_dir }}/.bootstrap_complete"
|
||||
register: bootstrap_flag
|
||||
|
||||
- name: Bootstrap Zitadel instance
|
||||
when: not bootstrap_flag.stat.exists
|
||||
block:
|
||||
- name: Display admin credentials for first login
|
||||
debug:
|
||||
msg: |
|
||||
Zitadel is now running at https://{{ zitadel_domain }}
|
||||
|
||||
Login with:
|
||||
Username: {{ zitadel_admin_username }}
|
||||
Password: {{ client_secrets.zitadel_admin_password }}
|
||||
|
||||
IMPORTANT: Change this password after first login!
|
||||
|
||||
- name: Mark bootstrap as complete
|
||||
file:
|
||||
path: "{{ zitadel_config_dir }}/.bootstrap_complete"
|
||||
state: touch
|
||||
mode: '0600'
|
||||
|
||||
- name: Bootstrap already completed
|
||||
debug:
|
||||
msg: "Zitadel bootstrap already completed, skipping initialization"
|
||||
when: bootstrap_flag.stat.exists
|
||||
49
ansible/roles/zitadel/tasks/docker.yml
Normal file
49
ansible/roles/zitadel/tasks/docker.yml
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
---
|
||||
# Docker Compose setup for Zitadel
|
||||
|
||||
- name: Create Zitadel configuration directory
|
||||
file:
|
||||
path: "{{ zitadel_config_dir }}"
|
||||
state: directory
|
||||
mode: '0755'
|
||||
|
||||
- name: Create Zitadel internal network
|
||||
community.docker.docker_network:
|
||||
name: "{{ zitadel_network }}"
|
||||
driver: bridge
|
||||
internal: true
|
||||
|
||||
- name: Deploy Zitadel Docker Compose configuration
|
||||
template:
|
||||
src: docker-compose.zitadel.yml.j2
|
||||
dest: "{{ zitadel_config_dir }}/docker-compose.yml"
|
||||
mode: '0600'
|
||||
notify: Restart Zitadel
|
||||
|
||||
- name: Start Zitadel services
|
||||
community.docker.docker_compose_v2:
|
||||
project_src: "{{ zitadel_config_dir }}"
|
||||
state: present
|
||||
register: zitadel_deploy
|
||||
|
||||
- name: Wait for Zitadel database to be ready
|
||||
community.docker.docker_container_exec:
|
||||
container: zitadel-db
|
||||
command: pg_isready -U {{ zitadel_db_user }} -d {{ zitadel_db_name }}
|
||||
register: db_ready
|
||||
until: db_ready.rc == 0
|
||||
retries: 30
|
||||
delay: 2
|
||||
changed_when: false
|
||||
|
||||
- name: Wait for Zitadel to be healthy
|
||||
uri:
|
||||
url: "https://{{ zitadel_domain }}/debug/ready"
|
||||
method: GET
|
||||
status_code: 200
|
||||
validate_certs: yes
|
||||
register: zitadel_health
|
||||
until: zitadel_health.status == 200
|
||||
retries: 30
|
||||
delay: 10
|
||||
changed_when: false
|
||||
13
ansible/roles/zitadel/tasks/main.yml
Normal file
13
ansible/roles/zitadel/tasks/main.yml
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
---
|
||||
# Main tasks file for Zitadel role
|
||||
|
||||
- name: Include Docker Compose setup
|
||||
include_tasks: docker.yml
|
||||
|
||||
- name: Include bootstrap setup
|
||||
include_tasks: bootstrap.yml
|
||||
when: zitadel_bootstrap | default(true)
|
||||
|
||||
- name: Include OIDC applications setup
|
||||
include_tasks: oidc-apps.yml
|
||||
when: zitadel_create_oidc_apps | default(false)
|
||||
7
ansible/roles/zitadel/tasks/oidc-apps.yml
Normal file
7
ansible/roles/zitadel/tasks/oidc-apps.yml
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
---
|
||||
# OIDC Application creation tasks (for Nextcloud and other apps)
|
||||
# This will be implemented in a later phase when Nextcloud is deployed
|
||||
|
||||
- name: OIDC applications placeholder
|
||||
debug:
|
||||
msg: "OIDC application creation will be implemented when Nextcloud role is ready"
|
||||
|
|
@ -0,0 +1,99 @@
|
|||
services:
|
||||
zitadel:
|
||||
image: ghcr.io/zitadel/zitadel:{{ zitadel_version }}
|
||||
container_name: zitadel
|
||||
restart: unless-stopped
|
||||
command: start-from-init --masterkeyFromEnv --tlsMode external
|
||||
environment:
|
||||
# Masterkey for encryption
|
||||
ZITADEL_MASTERKEY: "{{ client_secrets.zitadel_masterkey }}"
|
||||
|
||||
# Database configuration
|
||||
ZITADEL_DATABASE_POSTGRES_HOST: zitadel-db
|
||||
ZITADEL_DATABASE_POSTGRES_PORT: 5432
|
||||
ZITADEL_DATABASE_POSTGRES_DATABASE: "{{ zitadel_db_name }}"
|
||||
ZITADEL_DATABASE_POSTGRES_USER_USERNAME: "{{ zitadel_db_user }}"
|
||||
ZITADEL_DATABASE_POSTGRES_USER_PASSWORD: "{{ client_secrets.zitadel_db_password }}"
|
||||
ZITADEL_DATABASE_POSTGRES_USER_SSL_MODE: disable
|
||||
ZITADEL_DATABASE_POSTGRES_ADMIN_USERNAME: "{{ zitadel_db_user }}"
|
||||
ZITADEL_DATABASE_POSTGRES_ADMIN_PASSWORD: "{{ client_secrets.zitadel_db_password }}"
|
||||
ZITADEL_DATABASE_POSTGRES_ADMIN_SSL_MODE: disable
|
||||
|
||||
# External domain configuration
|
||||
ZITADEL_EXTERNALSECURE: "true"
|
||||
ZITADEL_EXTERNALDOMAIN: "{{ zitadel_domain }}"
|
||||
ZITADEL_EXTERNALPORT: 443
|
||||
|
||||
# First instance configuration
|
||||
ZITADEL_FIRSTINSTANCE_ORG_NAME: "{{ client_name | title }}"
|
||||
ZITADEL_FIRSTINSTANCE_ORG_HUMAN_USERNAME: "{{ zitadel_admin_username }}"
|
||||
ZITADEL_FIRSTINSTANCE_ORG_HUMAN_PASSWORD: "{{ client_secrets.zitadel_admin_password }}"
|
||||
ZITADEL_FIRSTINSTANCE_ORG_HUMAN_EMAIL: "admin@{{ zitadel_domain }}"
|
||||
ZITADEL_FIRSTINSTANCE_ORG_HUMAN_EMAIL_VERIFIED: "true"
|
||||
|
||||
networks:
|
||||
- {{ zitadel_traefik_network }}
|
||||
- {{ zitadel_network }}
|
||||
|
||||
depends_on:
|
||||
zitadel-db:
|
||||
condition: service_healthy
|
||||
|
||||
labels:
|
||||
- "traefik.enable=true"
|
||||
- "traefik.http.routers.zitadel.rule=Host(`{{ zitadel_domain }}`)"
|
||||
- "traefik.http.routers.zitadel.tls=true"
|
||||
- "traefik.http.routers.zitadel.tls.certresolver=letsencrypt"
|
||||
- "traefik.http.routers.zitadel.entrypoints=websecure"
|
||||
- "traefik.http.services.zitadel.loadbalancer.server.port=8080"
|
||||
# gRPC support for API
|
||||
- "traefik.http.services.zitadel.loadbalancer.server.scheme=h2c"
|
||||
# Middleware for security headers
|
||||
- "traefik.http.routers.zitadel.middlewares=zitadel-headers"
|
||||
- "traefik.http.middlewares.zitadel-headers.headers.stsSeconds=31536000"
|
||||
- "traefik.http.middlewares.zitadel-headers.headers.stsIncludeSubdomains=true"
|
||||
- "traefik.http.middlewares.zitadel-headers.headers.stsPreload=true"
|
||||
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
memory: {{ zitadel_memory_limit }}
|
||||
cpus: "{{ zitadel_cpu_limit }}"
|
||||
|
||||
zitadel-db:
|
||||
image: postgres:{{ postgres_version }}
|
||||
container_name: zitadel-db
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
POSTGRES_USER: "{{ zitadel_db_user }}"
|
||||
POSTGRES_PASSWORD: "{{ client_secrets.zitadel_db_password }}"
|
||||
POSTGRES_DB: "{{ zitadel_db_name }}"
|
||||
|
||||
volumes:
|
||||
- zitadel-db-data:/var/lib/postgresql/data
|
||||
|
||||
networks:
|
||||
- {{ zitadel_network }}
|
||||
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U {{ zitadel_db_user }} -d {{ zitadel_db_name }}"]
|
||||
interval: 5s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
memory: 256M
|
||||
cpus: "0.5"
|
||||
|
||||
volumes:
|
||||
zitadel-db-data:
|
||||
driver: local
|
||||
|
||||
networks:
|
||||
{{ zitadel_traefik_network }}:
|
||||
external: true
|
||||
{{ zitadel_network }}:
|
||||
driver: bridge
|
||||
internal: true
|
||||
|
|
@ -1,32 +1,33 @@
|
|||
#ENC[AES256_GCM,data:nK9yR3JOQB56nTI8H8g2Mp7vnb8wArivoQ==,iv:Ke/n7VHkQs1X5b8/kj7Put6BNuZvK5A1WDLdVNJvPAg=,tag:kLvhJ3KCBnO6qd9KSD0OLw==,type:comment]
|
||||
#ENC[AES256_GCM,data:L8VurAyFOT0RdJXab18xpAgW0ZULY9nxw/DdJ9kisEBfT+m0FZU=,iv:xv1i5wLoOR7x2N2ukuasGCrK2N5xHlfDdnwhaL+XBm8=,tag:LIzSGObX5NMloLU01T0iwg==,type:comment]
|
||||
#ENC[AES256_GCM,data:vMG1ExYmlXI1RWbQWyUdmKNCqg==,iv:l9TGHsz2KOqF1i6j39ftXxUYvlfAzXY5Bi5nAJMWSQA=,tag:cJ3lrld4vL7uJOnqQRmJjQ==,type:comment]
|
||||
client_name: ENC[AES256_GCM,data:MZWftQ==,iv:f5MS6vLBC+tHJlB+VWTpOWTej7+sJZKbioMfA37ZjiA=,tag:/h+aYKOh4BPh96CJlNzpJw==,type:str]
|
||||
client_domain: ENC[AES256_GCM,data:CtSIb4/bQU8etWJpTqudxZwhuUk+hqU=,iv:FZhFwV88FglVQzjgPNJW5ZizJtHQbfdFaUbeLWaU1io=,tag:3VNoF543JENvPbiLU/todg==,type:str]
|
||||
#ENC[AES256_GCM,data:BdAVkrQXKwMUuNr+P8iqGA==,iv:8Acn7K+tR2b8mkPe5EugAKpV9A540FVJC0kIuDQIPD4=,tag:jdfmiZ5o4dy8Q/YbZqq/ng==,type:comment]
|
||||
zitadel_db_password: ENC[AES256_GCM,data:Jt6C7U88Ale90QxSm7E4ZwluIbuLq3tWl0+tOFDpzP2og7eDyQ==,iv:fqvUJcK8h0xRSAzxsVOwSUyyL2CKlyvszCihL4syot8=,tag:rDXQIQ/0MDRftkDuz50Tkw==,type:str]
|
||||
zitadel_admin_password: ENC[AES256_GCM,data:apcy1CuWpICWULo8VULqH47loeFB3eUKLvUBIuVXIuu7BPwtbw==,iv:U7JB0wDhGKPwpRs1RE0X8dfcuE7sa5b9ikc+0XDWKos=,tag:YcT3y+1wRLfHsYoM4c/2yg==,type:str]
|
||||
zitadel_masterkey: ENC[AES256_GCM,data:PZHiQK3Z2IGE0DUp/DRsQ7omfNM0xKmiaPAQHn1D4vU1XuJ2t54=,iv:K11E24TK6886crExpEWF/eDF53w5lQzIt5BG5jS557Q=,tag:NFNY7rTdxKLUOxglCfdYRw==,type:str]
|
||||
#ENC[AES256_GCM,data:UCxVKl+EGvZvHFHZa91rjlYI,iv:RmN4jI05bkM1uEE1TglzE5a54RYFYMzCMQvlpq2ydbg=,tag:POsM86j4Jj+8wjwF7ffWgA==,type:comment]
|
||||
nextcloud_db_password: ENC[AES256_GCM,data:oN+PC7pD07VyV9bKqZOGWLkdH6VhKOz+BBRmPYmm/8q4OQ47iQ==,iv:8ZAipySlXTgZm50R+AOKWQGszc+fcgKPMoa+TOFq+ig=,tag:6pEqOidt8Dz4P0QQ+7u+BQ==,type:str]
|
||||
nextcloud_admin_password: ENC[AES256_GCM,data:ZMoK4M6xAFK3DQIBMn0a1mtkKCjhW6P/dLfUUILccnjmqO8a3A==,iv:ctXQhhO5NK5/i2Hg73lnCy1bHlgXsgBjMxQPhJy2yrw=,tag:LzbWUwZ2AEQyx2lbshu73Q==,type:str]
|
||||
#ENC[AES256_GCM,data:J9fmtOMRn7VCA4qn3KN5L4QXuaNLBmk35q8NlqxMYg5TJg==,iv:f7DM3G1VN3rvIkFzAJrouG4d1A2jRtNWuJu5/+YezMQ=,tag:WA4qDSQH5+NSs/8tiHNM6Q==,type:comment]
|
||||
restic_repo_password: ENC[AES256_GCM,data:V3Pw2hZIFWD/uK+pXPETHNAula4SfmPQGEOEqw/v7KdcwMlhgQ==,iv:DukqTm/LtliLioALDwZI0BDW3sJwNfq/6vcHVIit0Gc=,tag:mNIHwcBtranAmJNBTl4thw==,type:str]
|
||||
#ENC[AES256_GCM,data:Jnxs5WoVDE31NgQmocYH80W569qK8yHDwY8ZDYeDyOY+Fn1mbK7xdilCB4aOn4vP5qtMzqCKa7paXm78BzZ9FpRgAkY=,iv:kgLwRvT5XhgDN7O4yEYkxMVFCuNtAdB+mmhYjar1pqY=,tag:fplXZLOILDOzh7n8WIWm/A==,type:comment]
|
||||
#ENC[AES256_GCM,data:xpilPXQdvCRTIBjWEfRZMfILlWi/gDGL7onkT9o=,iv:1XsMusNaqv80/TLLfdrBk56RqNCDTB0EavhQXBJVS3I=,tag:OXlrYZuldsJeyaJQBWeDPQ==,type:comment]
|
||||
#ENC[AES256_GCM,data:hwTLhDd5S4EWFFBcrkxGRazBVU50txHIKjKyOb3VJOqF,iv:7DGf0PvBKYN9NxhAiAi2bGThWf7jHmAhJDuqgGb+7+4=,tag:SO35c4iT8hBDlorx/6I8ww==,type:comment]
|
||||
#ENC[AES256_GCM,data:N5GrnX4oxwTmii6SiAdbZ6cNHYHS8COphg==,iv:nKwJFhRd+lKsKvTY/miXkvNYF/MoPOuTCcMOldB1e6o=,tag:Gu7VGqLmHnNFSBq23oiTKA==,type:comment]
|
||||
#ENC[AES256_GCM,data:OZwfwJ9O+xSygmBOirZ3OfKzRQ==,iv:2Oy+ZZnfVgKB9rm1Xr+4dVY0Ny3soiVdznYHT+KV2Mk=,tag:wBr1c3h+118FJE5zRB9D7g==,type:comment]
|
||||
client_name: ENC[AES256_GCM,data:vaMWvg==,iv:SNkcJsVq0QHnCku9WzOHZpY282OYK3NpWdnWpr9f0Cc=,tag:CMA6AY0I4F+EQ5Vw14Fo9Q==,type:str]
|
||||
client_domain: ENC[AES256_GCM,data:4QqtTVrKzr9RihL2MjCfdQ==,iv:6EiqWtRBuFfBO28NJkHfGaPUMAPkd6XpU9jKJyJN3AQ=,tag:sbm9ArjDeUjQnRUcberm3g==,type:str]
|
||||
#ENC[AES256_GCM,data:uoS37xiQ+sj9tL7TAPnY,iv:D+YkgPUiYQKgGMircnGZjhZ+9qqwDGiVDDfHpw76Irs=,tag:IF/n/bZJkXnnQLEZpMCjog==,type:comment]
|
||||
zitadel_domain: ENC[AES256_GCM,data:zBYb/6VV9w+WaRJ979rOAexrjKDWrKA3,iv:xTEbPUNSANoIMlIYnejj+DpSyg3G1zp9dLExUap0FiI=,tag:zc6PsNTAyVug1j7qV2QBLg==,type:str]
|
||||
#ENC[AES256_GCM,data:Et/LOKSvoypnWgOa+2BRDw==,iv:GnOn/0zgCWJyaxQU8EuVWf6JMvUishkOLgiX3u4firg=,tag:aSiwqpjeCaYkHfvK9yA2EA==,type:comment]
|
||||
zitadel_db_password: ENC[AES256_GCM,data:DYUfwlU+MmgMVhPNG2vWelP8AxoZGBBdST6Tu3qL9oo=,iv:6rUUndg7lKVUTBleDN296csG18Sge+jfcGAS8nLnvNQ=,tag:eZYsJajreqBBDQ9f62uEkg==,type:str]
|
||||
zitadel_admin_password: ENC[AES256_GCM,data:R63L/AasVX4U9JTk6TceQ3ssQmauqA==,iv:vdFpadKrbnYabbF1VHz9p1F1UAGTq8zGimfUcY1Q18I=,tag:lJzlPLjWim7un8bnu6Ag0w==,type:str]
|
||||
zitadel_masterkey: ENC[AES256_GCM,data:UJJvevSA3wOdiSsNhgd6FQyanGz0UlNY07PFw/A2/oM=,iv:YllkHETB84ymAdKlVwHRtFJELOU8J16Zk+YOJERA5o8=,tag:TEg1grtk1tLekZFMYXvoCQ==,type:str]
|
||||
#ENC[AES256_GCM,data:ezHDbKI3OWGK1g+Foy55zIO3,iv:qu+124Qr8HnSNITJ/KLQPfiKk+tsylPc/6pXfJus7Rw=,tag:jdK4CG1ID9vEf3fwGX21qA==,type:comment]
|
||||
nextcloud_db_password: ENC[AES256_GCM,data:XcMlkgQEFPDjupgkLN29Kv9/h9zHqgbVFBESpNsNQcc=,iv:qGXo/un4a7Zvbwkfe9SalqRhYHA8aK5R36j75uwI4As=,tag:49bXKFX4LJSG6qWGnAPmOA==,type:str]
|
||||
nextcloud_admin_password: ENC[AES256_GCM,data:aPrihv33Jenj14X16xCM+ad5,iv:k/azAY3tYkgD3mTK66rl8xXCk6Q5WFPyAx3x42gsL8U=,tag:DP9aLEI5aLj9nxqU2fOe/w==,type:str]
|
||||
#ENC[AES256_GCM,data:+8XTcTojV2GEgJ0Vqgwi/M/dGTiQ/GjFaNtYHhfzg79pCg==,iv:kaf0tID2dgEZ34K+SStYwqXE457uYx1jJ/X/jj66QKk=,tag:Ffh4oJ/vlBDAt2ietzaIQg==,type:comment]
|
||||
restic_repo_password: ENC[AES256_GCM,data:XnJ6T1yFU+bMEqeZ2DlfwSrH8fDfvSECvbpPiajqWKY=,iv:dQFidsORFC8b6xkm/SxRoW86kXZLgVdw4R5eWK3Slek=,tag:LhHnkQu6fK6q+LGE5+jQAw==,type:str]
|
||||
#ENC[AES256_GCM,data:WJL9I1Ywhyj1zFLFpkzAeKJnTLcKqpXHvK6U8eReraXjll8cfDvBoy0gDjoaYJeiOUsNrUosgCowUkO5Rsh7qAkdxEM=,iv:feQbZyyVTtIEgq7r235977Qzv4Aw3ySnw1krZ4e+xbw=,tag:p5FKhxwh14rCZlr4UNtfNw==,type:comment]
|
||||
#ENC[AES256_GCM,data:H7uVvy0johFigCM6gXFJefRnaN8+aHIeP24aM3A=,iv:BYipDmfPR54zldX4FYz1Zd8CldPaaFMaJexgcSlLjSY=,tag:jbbitJgtVYvoxpqoHA6Rpw==,type:comment]
|
||||
#ENC[AES256_GCM,data:NSV3p9oBOEqAuunSfCOwj2IyL4NLKQ9jbWm2FyeJoQ2T,iv:ttn70OrdSNi76792DQR7cSRao8rhtygoYOoKNS86ASY=,tag:d6/OBfkwk0l/654F/8jeiA==,type:comment]
|
||||
sops:
|
||||
age:
|
||||
- recipient: age170jqy5pg6z62kevadqyxxekw8ryf3e394zaquw0nhs9ae3v9wd6qq2hxnk
|
||||
enc: |
|
||||
-----BEGIN AGE ENCRYPTED FILE-----
|
||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBWZko3UVJkSzRjK3J0bXBm
|
||||
Yk0rUTRRbmxYL001QXNEMHpIaGsvMUc2ZjFnCnZnTU8ySHc0QVB6amgrTjBPdG5w
|
||||
TFhucW9VZHNDWmdaVDdZWDRQbjhOQzAKLS0tIHJWWm00VWVIZlNXd04veGRoTkIw
|
||||
R0kyRC9VcTFoWkFCUnl6ZmlyRjh3bXcKCkAed8Gx9jxFmoFg7vyM4a3xO9N+FxtI
|
||||
CdpnZ9Wk1O498wPIV2meM3RFBclkWFgqGvAqzUNbzGuMnoSlRfJq+w==
|
||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBTUy9RZWdWV0lQbU5kQ1Vq
|
||||
SEpNVitYcmJ4YjZ2bmtMdE1WMVYyMXBrMXd3CkI3ZTBldDZrUjh0R2ROOG1LOUpR
|
||||
ZmVOaHNmeXdoQzJqRFk3UUJESDE3Uk0KLS0tIEo2YjRRWm9yamRoN1pRYWgwZTBx
|
||||
bUJ6cTFkWmlNRWxFS2FhRzNYbUFpb3cK27FBZIOevWweM5OUIAvM7A2ZJdI36aao
|
||||
1t8Ot5vfCh7p01Es+Sb1YlNbyTmZ1P3ZV9FNxVotEjxYRH6BZuovjg==
|
||||
-----END AGE ENCRYPTED FILE-----
|
||||
lastmodified: "2025-12-27T13:19:53Z"
|
||||
mac: ENC[AES256_GCM,data:XkD2lptFXJrRmKg/Rxd25D1Y2bGxKM+GqBcgXQTDXvr+BIrE5jC9AWzycmkB+GX0Ta5LYlcLk1mrXGp/SbNxE8rubCvqS7qZbpxEBQi8fsy+LX0kiCOgM5SSxMM6ON/gSJ2eivLzpEbeBGwXau77fNm/2MAAWZIdlfzeIN/9o4I=,iv:MU1pp6+rr7Gvs6mCPMUqz6VnPGLttUB0dgZsb43WyH4=,tag:ts54uUviBdmHif9KiQfrKQ==,type:str]
|
||||
lastmodified: "2026-01-05T14:44:48Z"
|
||||
mac: ENC[AES256_GCM,data:iNWtt7I33yQXTwrPf3GMJ4qC9HHmlRAVQrZyN7KFyOxT7L8iijCwbMThA8k+EVHIyQU10rYo5nbZtTkM4rJ7RiXqfwQVRpKMyLC+67hAiQBUwDhy7iVX4G1LzkJObTQnxAsldJ8O7gFReOFyTklf9WyUC2lRdcW4KkMnnDQdkao=,iv:QHJ4n75iGQ4mI4UoTUEPa/oXa4iLe7DtCzl14h5ENtU=,tag:14vV+JSLlXtxaEfwV3+Qzw==,type:str]
|
||||
unencrypted_suffix: _unencrypted
|
||||
version: 3.11.0
|
||||
|
|
|
|||
51
tofu/dns.tf
51
tofu/dns.tf
|
|
@ -1,44 +1,55 @@
|
|||
# DNS Configuration
|
||||
# OPTIONAL: Only used if you have a domain registered in Hetzner DNS
|
||||
# Comment out this entire file if you don't have a domain yet
|
||||
# DNS Configuration for vrije.cloud using hcloud provider
|
||||
# The zone already exists in Hetzner Console, so we reference it as a data source
|
||||
|
||||
# Uncomment below when you have a domain registered in Hetzner DNS
|
||||
/*
|
||||
# DNS Zone (must already exist in Hetzner DNS)
|
||||
data "hetznerdns_zone" "main" {
|
||||
# Reference the existing DNS zone
|
||||
data "hcloud_zone" "main" {
|
||||
name = var.base_domain
|
||||
}
|
||||
|
||||
# A Records for client servers
|
||||
resource "hetznerdns_record" "client_a" {
|
||||
# A Records for client servers (e.g., test.vrije.cloud -> 78.47.191.38)
|
||||
resource "hcloud_zone_rrset" "client_a" {
|
||||
for_each = var.clients
|
||||
|
||||
zone_id = data.hetznerdns_zone.main.id
|
||||
zone = data.hcloud_zone.main.name
|
||||
name = each.value.subdomain
|
||||
type = "A"
|
||||
value = hcloud_server.client[each.key].ipv4_address
|
||||
ttl = 300
|
||||
records = [
|
||||
{
|
||||
value = hcloud_server.client[each.key].ipv4_address
|
||||
comment = "Client ${each.key} server"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
# Wildcard A record for each client (for subdomains like auth.alpha.platform.nl)
|
||||
resource "hetznerdns_record" "client_wildcard" {
|
||||
# Wildcard A record for each client (e.g., *.test.vrije.cloud for zitadel.test.vrije.cloud)
|
||||
resource "hcloud_zone_rrset" "client_wildcard" {
|
||||
for_each = var.clients
|
||||
|
||||
zone_id = data.hetznerdns_zone.main.id
|
||||
zone = data.hcloud_zone.main.name
|
||||
name = "*.${each.value.subdomain}"
|
||||
type = "A"
|
||||
value = hcloud_server.client[each.key].ipv4_address
|
||||
ttl = 300
|
||||
records = [
|
||||
{
|
||||
value = hcloud_server.client[each.key].ipv4_address
|
||||
comment = "Wildcard for ${each.key} subdomains (Zitadel, Nextcloud, etc)"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
# AAAA Records for IPv6
|
||||
resource "hetznerdns_record" "client_aaaa" {
|
||||
# AAAA Records for IPv6 (e.g., test.vrije.cloud IPv6)
|
||||
resource "hcloud_zone_rrset" "client_aaaa" {
|
||||
for_each = var.clients
|
||||
|
||||
zone_id = data.hetznerdns_zone.main.id
|
||||
zone = data.hcloud_zone.main.name
|
||||
name = each.value.subdomain
|
||||
type = "AAAA"
|
||||
value = hcloud_server.client[each.key].ipv6_address
|
||||
ttl = 300
|
||||
records = [
|
||||
{
|
||||
value = hcloud_server.client[each.key].ipv6_address
|
||||
comment = "Client ${each.key} server IPv6"
|
||||
}
|
||||
]
|
||||
}
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -3,10 +3,7 @@ provider "hcloud" {
|
|||
token = var.hcloud_token
|
||||
}
|
||||
|
||||
# DNS provider - uncomment when using Hetzner DNS
|
||||
# provider "hetznerdns" {
|
||||
# apitoken = var.hetznerdns_token
|
||||
# }
|
||||
# hcloud provider handles both Cloud and DNS resources
|
||||
|
||||
# SSH Key Resource
|
||||
resource "hcloud_ssh_key" "default" {
|
||||
|
|
|
|||
|
|
@ -4,13 +4,7 @@ terraform {
|
|||
required_providers {
|
||||
hcloud = {
|
||||
source = "hetznercloud/hcloud"
|
||||
version = "~> 1.45"
|
||||
version = "~> 1.57"
|
||||
}
|
||||
# DNS provider - optional, only needed if using Hetzner DNS
|
||||
# Commented out since DNS is not required initially
|
||||
# hetznerdns = {
|
||||
# source = "timohirt/hetznerdns"
|
||||
# version = "~> 2.4"
|
||||
# }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue