initial commit

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Matthias Johnson 2026-02-27 15:09:25 -07:00
commit 75891c3271
129 changed files with 8046 additions and 0 deletions

View file

@ -0,0 +1,164 @@
---
# ============================================================
# Linderhof Configuration
# ============================================================
# Generated by setup.sh — edit freely to match your needs.
# Secrets are stored separately in vault.yml.
# Tunable defaults live in each role's defaults/main.yml.
#
# To override any variable for this stack without editing this file,
# create $LINDERHOF_DIR/group_vars/all/overrides.yml, e.g.:
# mail_hostname: mail2.$domain
# caddy_sites:
# - $domain
# - example2.com
# ============================================================
# ============================================================
# Services — set to false to disable
# ============================================================
enable_mail: true
enable_forgejo: true
enable_monitoring: true
enable_restic: true
enable_fail2ban: true
enable_tuwunel: true
enable_nebula: true
enable_diun: true
enable_goaccess: true
# ============================================================
# System
# ============================================================
domain: $domain
server_name: $server_name
server_ip: $server_ip
admin_user: $admin_user
admin_ssh_key: "{{ lookup('file', '$ssh_key_pub') }}"
timezone: UTC
# ============================================================
# Image versions (update when Diun notifies of new releases)
# ============================================================
caddy_version: "2"
mailserver_version: "latest"
rainloop_version: "latest"
forgejo_version: "11"
prometheus_version: "latest"
alloy_version: "latest"
grafana_version: "latest"
loki_version: "latest"
diun_version: "latest"
tuwunel_version: "latest"
radicale_version: "latest"
nebula_version: "1.9.5"
# ============================================================
# Caddy (web server / reverse proxy)
# ============================================================
# Static sites served as file servers — each gets /srv/caddy/sites/<domain>/
# Override in overrides.yml to add more domains.
caddy_sites:
- $domain
# Service subdomains — override individually in overrides.yml
webmail_domain: webmail.$domain
rspamd_domain: rspamd.$domain
grafana_domain: watch.$domain
goaccess_domain: stats.$domain
radicale_domain: cal.$domain
# Service ports — defined here so caddy can reference them when run standalone
rainloop_port: 8888
rspamd_port: 11334
forgejo_port: 3000
grafana_port: 3000
tuwunel_port: 6167
radicale_port: 5232
caddy_metrics_port: 9000
# ============================================================
# Mail (docker-mailserver + rainloop)
# ============================================================
# Override mail_hostname in overrides.yml if migrating (e.g. mail2.$domain)
mail_hostname: mail.$domain
mail_domains:
- $domain
# Add more domains this mail server should handle:
# mail_domains:
# - $domain
# - example2.com
mail_users:
- address: $admin_user@$domain
password: "{{ mail_passwords['$admin_user@$domain'] }}"
- address: git@$domain
password: "{{ mail_passwords['git@$domain'] }}"
- address: notifications@$domain
password: "{{ mail_passwords['notifications@$domain'] }}"
mail_aliases:
- from: root@$domain
to: $admin_user@$domain
- from: dmarc@$domain
to: $admin_user@$domain
- from: postmaster@$domain
to: $admin_user@$domain
- from: hostmaster@$domain
to: $admin_user@$domain
- from: webmaster@$domain
to: $admin_user@$domain
- from: abuse@$domain
to: $admin_user@$domain
# ============================================================
# Forgejo (git hosting)
# ============================================================
forgejo_domain: code.$domain
# ============================================================
# Monitoring
# ============================================================
grafana_root_url: "https://{{ grafana_domain }}"
# ============================================================
# Restic (encrypted backups)
# ============================================================
restic_backend_type: "sftp"
# restic_host: "uXXXXXX.your-storagebox.de"
# restic_user: uXXXXXX
# restic_ssh_port: 23
# restic_remote_path: "backups/$server_name"
# restic_ssh_key: "/root/.ssh/island_restic_backup"
# ============================================================
# GoAccess (web analytics)
# ============================================================
goaccess_sites:
- $domain
- code.$domain
- watch.$domain
- webmail.$domain
- rspamd.$domain
goaccess_user: admin
# ============================================================
# Diun (Docker Image Update Notifier)
# ============================================================
diun_notify_email: true
diun_email_user: notifications@$domain
## diun_email_password: defined in vault.yml
diun_email_to: $admin_user@$domain
# ============================================================
# Tuwunel (Matrix homeserver)
# ============================================================
tuwunel_server_name: $domain
tuwunel_domain: chat.$domain
# ============================================================
# Nebula (overlay network)
# ============================================================
nebula_subnet: "192.168.100.0/24"
nebula_lighthouse_ip: "192.168.100.1"

View file

@ -0,0 +1,128 @@
---
# ============================================================
# Linderhof DNS Zones
# ============================================================
# Generated by setup.sh — edit to match your DNS needs.
# This file is loaded automatically by Ansible as part of group_vars.
#
# After first mail deployment, retrieve DKIM keys with:
# docker exec mailserver cat /tmp/docker-mailserver/rspamd/dkim/$domain/mail.pub
# Add them to vault.yml and uncomment the mail._domainkey records below.
# ============================================================
dns_zones:
- zone: $domain
records:
# Root domain
- name: "@"
type: A
records:
- value: $server_ip
- name: "@"
type: MX
records:
- value: "10 {{ mail_hostname }}."
- name: "@"
type: TXT
records:
- value: "{{ 'v=spf1 mx -all' | hetzner.hcloud.txt_record }}"
# Server A record
- name: $server_name
type: A
records:
- value: $server_ip
- name: www
type: A
records:
- value: $server_ip
# Mail subdomain A record (for the mail hostname itself)
- name: "{{ mail_hostname.split('.')[0] }}"
type: A
records:
- value: $server_ip
# Service CNAMEs
- name: webmail
type: CNAME
records:
- value: $server_name.$domain.
- name: code
type: CNAME
records:
- value: $server_name.$domain.
- name: watch
type: CNAME
records:
- value: $server_name.$domain.
- name: rspamd
type: CNAME
records:
- value: $server_name.$domain.
- name: stats
type: CNAME
records:
- value: $server_name.$domain.
- name: chat
type: CNAME
records:
- value: $server_name.$domain.
- name: cal
type: CNAME
records:
- value: $server_name.$domain.
# DMARC
- name: _dmarc
type: TXT
records:
- value: "{{ 'v=DMARC1; p=none; rua=mailto:dmarc@$domain' | hetzner.hcloud.txt_record }}"
# DKIM — uncomment after first mail deployment and add key to vault.yml
# - name: mail._domainkey
# type: TXT
# records:
# - value: "{{ dkim_keys['$domain'] | hetzner.hcloud.txt_record }}"
# Extra domains (additional mail-hosted domains) — add as needed:
# - zone: example2.com
# records:
# - name: "@"
# type: A
# records:
# - value: $server_ip
#
# - name: "@"
# type: MX
# records:
# - value: "10 {{ mail_hostname }}."
#
# - name: "@"
# type: TXT
# records:
# - value: "{{ 'v=spf1 mx -all' | hetzner.hcloud.txt_record }}"
#
# - name: www
# type: CNAME
# records:
# - value: example2.com.
#
# - name: _dmarc
# type: TXT
# records:
# - value: "{{ 'v=DMARC1; p=none; rua=mailto:dmarc@example2.com' | hetzner.hcloud.txt_record }}"
#
# # - name: mail._domainkey
# # type: TXT
# # records:
# # - value: "{{ dkim_keys['example2.com'] | hetzner.hcloud.txt_record }}"

View file

@ -0,0 +1,55 @@
---
# ============================================================
# Linderhof Secrets
# ============================================================
# Generated by setup.sh
# Edit with: ansible-vault edit $LINDERHOF_DIR/group_vars/all/vault.yml
# ============================================================
# hetzner
hcloud_token: "$hcloud_token"
# mail
# passwords generated with: openssl rand -base64 32
mail_passwords:
$admin_user@$domain: "$admin_mail_password"
git@$domain: "$git_mail_password"
notifications@$domain: "$notifications_mail_password"
rspamd_web_password: "$rspamd_web_password"
rainloop_admin_password: "$rainloop_admin_password"
# forgejo
# keys generated with: openssl rand -hex 32
forgejo_secret_key: "$forgejo_secret_key"
forgejo_internal_token: "$forgejo_internal_token"
forgejo_jwt_secret: "$forgejo_jwt_secret"
forgejo_smtp_password: "$notifications_mail_password"
# monitoring
# password generated with: openssl rand -base64 32
grafana_admin_password: "$grafana_admin_password"
# tuwunel
# token generated with: openssl rand -base64 32
tuwunel_registration_token: "$tuwunel_registration_token"
# goaccess
# password generated with: openssl rand -base64 32
goaccess_password: "$goaccess_password"
# diun (uses the notifications mail account)
diun_email_password: "$notifications_mail_password"
# restic
# password generated with: openssl rand -base64 32
restic_password: "$restic_password"
# fail2ban (optional — IPs/CIDRs to whitelist)
# fail2ban_ignoreip: "your-home-ip/32"
# DKIM public keys — one entry per domain
# Retrieve after first mail deployment:
# docker exec mailserver cat /tmp/docker-mailserver/rspamd/dkim/$domain/mail.pub
# Format: "v=DKIM1; k=rsa; p=<base64 public key>"
dkim_keys:
$domain: ""

View file

@ -0,0 +1,21 @@
---
# ============================================================
# Linderhof Inventory
# ============================================================
# Copy this file to hosts.yml (gitignored) and fill in your values.
# For single-host deployments, just update the connection info.
# All configuration is in group_vars/all/config.yml
#
# For multi-host, add hosts and override variables per-host:
# hostname:
# ansible_host: 1.2.3.4
# mail_hostname: mail.example.com # override
# ============================================================
all:
hosts:
my-server:
ansible_host: 1.2.3.4
ansible_user: deploy
ansible_become: true
ansible_become_method: sudo