added caddy provider support
This commit is contained in:
1 parent
91e80ae6f8
commit
408b5d8652
6 files changed
+192
-3
No files matched your search
@@ -2,6 +2,18 @@
|
||||
- name: Set up caddy LXC
|
||||
hosts: caddy
|
||||
become: yes
|
||||
vars:
|
||||
# Where the Caddy admin API listens. OpenTofu manages reverse-proxy routes
|
||||
# through this endpoint (caddy_host / caddy_ssh_host in terraform.tfvars).
|
||||
caddy_admin_listen: "0.0.0.0:2019"
|
||||
|
||||
handlers:
|
||||
- name: restart caddy
|
||||
ansible.builtin.systemd:
|
||||
name: caddy
|
||||
state: restarted
|
||||
daemon_reload: yes
|
||||
|
||||
tasks:
|
||||
- name: default_packages
|
||||
ansible.builtin.include_tasks: ../../templates/default_packages.yml
|
||||
@@ -12,9 +24,52 @@
|
||||
username: nick
|
||||
github_user: Chewbaccalakis
|
||||
|
||||
# Config is deployed BEFORE the caddy package is installed: dpkg keeps a
|
||||
# pre-existing /etc/caddy/Caddyfile instead of the package default (which
|
||||
# serves a static page on :80 as server "srv0" — that would get autosaved
|
||||
# and collide with the OpenTofu-managed :80 server forever after).
|
||||
- name: configure caddy
|
||||
block:
|
||||
- name: create /etc/caddy
|
||||
ansible.builtin.file:
|
||||
path: /etc/caddy
|
||||
state: directory
|
||||
mode: '0755'
|
||||
|
||||
- name: deploy Caddyfile (seeds the admin API listener)
|
||||
ansible.builtin.template:
|
||||
src: templates/Caddyfile.j2
|
||||
dest: /etc/caddy/Caddyfile
|
||||
owner: root
|
||||
group: root
|
||||
mode: '0644'
|
||||
notify: restart caddy
|
||||
|
||||
- name: create caddy systemd override directory
|
||||
ansible.builtin.file:
|
||||
path: /etc/systemd/system/caddy.service.d
|
||||
state: directory
|
||||
mode: '0755'
|
||||
|
||||
- name: override caddy service to resume OpenTofu-applied config
|
||||
ansible.builtin.copy:
|
||||
dest: /etc/systemd/system/caddy.service.d/resume.conf
|
||||
content: |
|
||||
# Managed by Ansible. --resume makes Caddy prefer its autosaved
|
||||
# JSON config (written whenever OpenTofu applies changes via the
|
||||
# admin API) over the Caddyfile, so managed routes survive
|
||||
# restarts. The Caddyfile only seeds the initial config; to
|
||||
# re-seed from it, delete
|
||||
# /var/lib/caddy/.config/caddy/autosave.json and restart caddy.
|
||||
[Service]
|
||||
ExecStart=
|
||||
ExecStart=/usr/bin/caddy run --environ --resume --config /etc/caddy/Caddyfile --adapter caddyfile
|
||||
mode: '0644'
|
||||
notify: restart caddy
|
||||
|
||||
- name: install caddy
|
||||
block:
|
||||
- name: install xcaddy prerequisites
|
||||
- name: install apt prerequisites
|
||||
ansible.builtin.apt:
|
||||
name:
|
||||
- debian-keyring
|
||||
@@ -22,6 +77,16 @@
|
||||
- apt-transport-https
|
||||
state: present
|
||||
|
||||
- name: download caddy gpg key
|
||||
ansible.builtin.shell:
|
||||
cmd: curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg
|
||||
creates: /usr/share/keyrings/caddy-stable-archive-keyring.gpg
|
||||
|
||||
- name: add caddy apt repository
|
||||
ansible.builtin.shell:
|
||||
cmd: curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' | tee /etc/apt/sources.list.d/caddy-stable.list
|
||||
creates: /etc/apt/sources.list.d/caddy-stable.list
|
||||
|
||||
- name: download xcaddy gpg key
|
||||
ansible.builtin.shell:
|
||||
cmd: curl -1sLf 'https://dl.cloudsmith.io/public/caddy/xcaddy/gpg.key' | gpg --dearmor -o /usr/share/keyrings/caddy-xcaddy-archive-keyring.gpg
|
||||
@@ -32,8 +97,17 @@
|
||||
cmd: curl -1sLf 'https://dl.cloudsmith.io/public/caddy/xcaddy/debian.deb.txt' | tee /etc/apt/sources.list.d/caddy-xcaddy.list
|
||||
creates: /etc/apt/sources.list.d/caddy-xcaddy.list
|
||||
|
||||
- name: install xcaddy
|
||||
- name: install caddy and xcaddy
|
||||
ansible.builtin.apt:
|
||||
name: xcaddy
|
||||
name:
|
||||
- caddy
|
||||
- xcaddy
|
||||
state: present
|
||||
update_cache: yes
|
||||
|
||||
- name: enable and start caddy
|
||||
ansible.builtin.systemd:
|
||||
name: caddy
|
||||
enabled: yes
|
||||
state: started
|
||||
daemon_reload: yes
|
||||
@@ -0,0 +1,14 @@
|
||||
# {{ ansible_managed }}
|
||||
#
|
||||
# This file only seeds Caddy's initial configuration: the admin API listener
|
||||
# that OpenTofu talks to. Reverse-proxy routes are managed by the caddy module
|
||||
# via the admin API (caddy_proxies in terraform.tfvars); Caddy autosaves those
|
||||
# changes and the systemd unit starts with --resume, so API-applied config
|
||||
# survives restarts and this file is ignored once an autosave exists.
|
||||
#
|
||||
# To re-seed from this file (e.g. after changing the admin listener), delete
|
||||
# /var/lib/caddy/.config/caddy/autosave.json and restart caddy.
|
||||
{
|
||||
admin {{ caddy_admin_listen }}
|
||||
}
|
||||
}
|
||||
@@ -17,6 +17,7 @@ locals {
|
||||
|
||||
technitium_enabled = try(data.external.infisical.result["technitium_api_token"], "") != ""
|
||||
ipam_enabled = try(data.external.infisical.result["ipam_username"], "") != ""
|
||||
caddy_enabled = var.caddy_host != ""
|
||||
|
||||
# Shared inputs passed to every proxmox_node module call.
|
||||
node_common = {
|
||||
@@ -102,6 +103,17 @@ module "technitium" {
|
||||
records = var.dns_records
|
||||
}
|
||||
|
||||
module "caddy" {
|
||||
source = "./opentofu/modules/caddy"
|
||||
|
||||
enabled = local.caddy_enabled
|
||||
proxies = var.caddy_proxies
|
||||
|
||||
providers = {
|
||||
caddy = caddy
|
||||
}
|
||||
}
|
||||
|
||||
resource "local_file" "ansible_inventory" {
|
||||
filename = "${path.module}/${var.ansible_inventory_path}"
|
||||
content = templatefile("${path.module}/opentofu/inventory.tpl", {
|
||||
|
||||
@@ -12,6 +12,10 @@ terraform {
|
||||
source = "kenske/technitium"
|
||||
version = "~> 0.2"
|
||||
}
|
||||
caddy = {
|
||||
source = "conradludgate/caddy"
|
||||
version = "~> 0.2"
|
||||
}
|
||||
# phpipam = {
|
||||
# source = "lord-kyron/phpipam"
|
||||
# version = "1.6.2"
|
||||
@@ -27,6 +31,26 @@ provider "technitium" {
|
||||
token = coalesce(try(data.external.infisical.result["technitium_api_token"], ""), "token-not-yet-provisioned")
|
||||
}
|
||||
|
||||
provider "caddy" {
|
||||
# Endpoint as seen from wherever the request lands: with the SSH tunnel below
|
||||
# this is the caddy host's own loopback admin API; without it, a directly
|
||||
# reachable admin endpoint. Falls back to the Caddy default so the provider
|
||||
# configures cleanly before caddy_host is set (no resources use it until then).
|
||||
host = coalesce(var.caddy_host, "http://localhost:2019")
|
||||
|
||||
# Tunnel to the admin API over SSH when it only listens on loopback (the
|
||||
# default, recommended setup). Reuses the ansible key generated above. Leave
|
||||
# caddy_ssh_host empty to talk to caddy_host directly.
|
||||
dynamic "ssh" {
|
||||
for_each = var.caddy_ssh_host != "" ? [1] : []
|
||||
content {
|
||||
host = var.caddy_ssh_host
|
||||
key_file = local_sensitive_file.ssh_private_key.filename
|
||||
host_key = var.caddy_ssh_host_key
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
data "external" "infisical" {
|
||||
program = ["bash", "-c", "infisical export --template=./secrets.tmpl"]
|
||||
}
|
||||
|
||||
@@ -40,8 +40,44 @@ dns_records = [
|
||||
{ zone = "oswaldtheotter.lan", name = "proxmox", type = "A", value = "192.168.0.240" },
|
||||
{ zone = "oswaldtheotter.lan", name = "nas", type = "CNAME", value = "portanas.oswaldtheotter.lan" },
|
||||
{ zone = "oswaldtheotter.lan", name = "s3", type = "CNAME", value="portanas.oswaldtheotter.lan"},
|
||||
{ zone = "oswaldtheotter.lan", name = "hass", type = "A", value="192.168.0.70"},
|
||||
{ zone = "oswaldtheotter.lan", name = "homeassistant", type = "CNAME", value="hass.oswaldtheotter.lan"},
|
||||
{ zone = "oswaldtheotter.lan", name = "bookstack", type = "A", value="192.168.0.5"},
|
||||
]
|
||||
|
||||
|
||||
# ── Reverse proxy (Caddy) ───────────────────────────────────────────────────────
|
||||
# Managed on the caddy LXC (192.168.0.5). The admin API listens on loopback, so
|
||||
# reach it by tunneling SSH to the box as the ansible user. Clear caddy_host to
|
||||
# disable Caddy management entirely.
|
||||
|
||||
caddy_host = "http://localhost:2019"
|
||||
caddy_ssh_host = "[email protected]:22"
|
||||
# From: ssh-keyscan 192.168.0.5 — must be refreshed if the LXC is rebuilt.
|
||||
# Must be the ECDSA key: the provider's (old) Go SSH client prefers
|
||||
# ecdsa-sha2-nistp256 during negotiation and pins the exact key it receives.
|
||||
caddy_ssh_host_key = "192.168.0.5 ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBLjluBg9JCITeqG4NPad4VNTkAcZDY3y9FU9CP5BAuKIr3TDhTFYzuAdNnROCEhW1ddJI9ZFKI5y/3xqNTmkHbA="
|
||||
|
||||
# Each proxy serves plain HTTP on :80 by default; set protocol = "https" to
|
||||
# serve it on :443 instead. Note: .lan hostnames can't get public certificates,
|
||||
# so HTTPS sites use Caddy's internal CA — trust its root cert on your devices
|
||||
# to avoid browser warnings.
|
||||
caddy_proxies = {
|
||||
# dns = {
|
||||
# host = "dns.oswaldtheotter.lan"
|
||||
# upstreams = ["192.168.0.2:5380"]
|
||||
# }
|
||||
# secure-example = {
|
||||
# host = "secure.oswaldtheotter.lan"
|
||||
# upstreams = ["192.168.0.202:80"]
|
||||
# protocol = "https"
|
||||
# }
|
||||
bookstack = {
|
||||
host = "bookstack.oswaldtheotter.lan"
|
||||
upstreams = ["192.168.0.4:30214"]
|
||||
}
|
||||
}
|
||||
|
||||
# ── Node definitions ───────────────────────────────────────────────────────────
|
||||
|
||||
nodes = {
|
||||
|
||||
@@ -62,6 +62,35 @@ variable "dns_records" {
|
||||
default = []
|
||||
}
|
||||
|
||||
variable "caddy_host" {
|
||||
description = "Caddy admin API endpoint. When using the SSH tunnel this is the endpoint as seen from the caddy host (e.g. http://localhost:2019). Leave empty to disable Caddy management."
|
||||
type = string
|
||||
default = ""
|
||||
}
|
||||
|
||||
variable "caddy_ssh_host" {
|
||||
description = "SSH target (user@host:port) used to tunnel to the Caddy admin API. Empty to reach caddy_host directly."
|
||||
type = string
|
||||
default = ""
|
||||
}
|
||||
|
||||
variable "caddy_ssh_host_key" {
|
||||
description = "SSH host key of the caddy host, known_hosts format (get it with: ssh-keyscan <ip>). Required when caddy_ssh_host is set."
|
||||
type = string
|
||||
default = ""
|
||||
}
|
||||
|
||||
variable "caddy_proxies" {
|
||||
description = "Reverse-proxy sites keyed by name. Each maps a hostname to one or more upstream backends (Caddy dial targets, e.g. \"192.168.0.40:32400\"). protocol picks plain HTTP on :80 (default) or HTTPS on :443 with automatic certificates. Set path to restrict the route to specific request paths."
|
||||
type = map(object({
|
||||
host = string
|
||||
upstreams = list(string)
|
||||
protocol = optional(string, "http")
|
||||
path = optional(list(string))
|
||||
}))
|
||||
default = {}
|
||||
}
|
||||
|
||||
variable "ansible_inventory_path" {
|
||||
description = "Path where the generated Ansible inventory file is written. Defaults to ../ansible/inventories/generated.yml relative to this module, which points to the parent repo when used as a git submodule."
|
||||
type = string
|
||||
|
||||
Reference in new issue
Block a user