added caddy provider support
This commit is contained in:
1 parent
91e80ae6f8
commit
408b5d8652
6 files changed
+193
-4
No files matched your search
@@ -2,6 +2,18 @@
|
|||||||
- name: Set up caddy LXC
|
- name: Set up caddy LXC
|
||||||
hosts: caddy
|
hosts: caddy
|
||||||
become: yes
|
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:
|
tasks:
|
||||||
- name: default_packages
|
- name: default_packages
|
||||||
ansible.builtin.include_tasks: ../../templates/default_packages.yml
|
ansible.builtin.include_tasks: ../../templates/default_packages.yml
|
||||||
@@ -12,9 +24,52 @@
|
|||||||
username: nick
|
username: nick
|
||||||
github_user: Chewbaccalakis
|
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
|
- name: install caddy
|
||||||
block:
|
block:
|
||||||
- name: install xcaddy prerequisites
|
- name: install apt prerequisites
|
||||||
ansible.builtin.apt:
|
ansible.builtin.apt:
|
||||||
name:
|
name:
|
||||||
- debian-keyring
|
- debian-keyring
|
||||||
@@ -22,6 +77,16 @@
|
|||||||
- apt-transport-https
|
- apt-transport-https
|
||||||
state: present
|
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
|
- name: download xcaddy gpg key
|
||||||
ansible.builtin.shell:
|
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
|
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
|
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
|
creates: /etc/apt/sources.list.d/caddy-xcaddy.list
|
||||||
|
|
||||||
- name: install xcaddy
|
- name: install caddy and xcaddy
|
||||||
ansible.builtin.apt:
|
ansible.builtin.apt:
|
||||||
name: xcaddy
|
name:
|
||||||
|
- caddy
|
||||||
|
- xcaddy
|
||||||
state: present
|
state: present
|
||||||
update_cache: yes
|
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"], "") != ""
|
technitium_enabled = try(data.external.infisical.result["technitium_api_token"], "") != ""
|
||||||
ipam_enabled = try(data.external.infisical.result["ipam_username"], "") != ""
|
ipam_enabled = try(data.external.infisical.result["ipam_username"], "") != ""
|
||||||
|
caddy_enabled = var.caddy_host != ""
|
||||||
|
|
||||||
# Shared inputs passed to every proxmox_node module call.
|
# Shared inputs passed to every proxmox_node module call.
|
||||||
node_common = {
|
node_common = {
|
||||||
@@ -102,6 +103,17 @@ module "technitium" {
|
|||||||
records = var.dns_records
|
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" {
|
resource "local_file" "ansible_inventory" {
|
||||||
filename = "${path.module}/${var.ansible_inventory_path}"
|
filename = "${path.module}/${var.ansible_inventory_path}"
|
||||||
content = templatefile("${path.module}/opentofu/inventory.tpl", {
|
content = templatefile("${path.module}/opentofu/inventory.tpl", {
|
||||||
|
|||||||
@@ -12,6 +12,10 @@ terraform {
|
|||||||
source = "kenske/technitium"
|
source = "kenske/technitium"
|
||||||
version = "~> 0.2"
|
version = "~> 0.2"
|
||||||
}
|
}
|
||||||
|
caddy = {
|
||||||
|
source = "conradludgate/caddy"
|
||||||
|
version = "~> 0.2"
|
||||||
|
}
|
||||||
# phpipam = {
|
# phpipam = {
|
||||||
# source = "lord-kyron/phpipam"
|
# source = "lord-kyron/phpipam"
|
||||||
# version = "1.6.2"
|
# version = "1.6.2"
|
||||||
@@ -27,6 +31,26 @@ provider "technitium" {
|
|||||||
token = coalesce(try(data.external.infisical.result["technitium_api_token"], ""), "token-not-yet-provisioned")
|
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" {
|
data "external" "infisical" {
|
||||||
program = ["bash", "-c", "infisical export --template=./secrets.tmpl"]
|
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 = "proxmox", type = "A", value = "192.168.0.240" },
|
||||||
{ zone = "oswaldtheotter.lan", name = "nas", type = "CNAME", value = "portanas.oswaldtheotter.lan" },
|
{ 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 = "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 ───────────────────────────────────────────────────────────
|
# ── Node definitions ───────────────────────────────────────────────────────────
|
||||||
|
|
||||||
nodes = {
|
nodes = {
|
||||||
|
|||||||
@@ -62,6 +62,35 @@ variable "dns_records" {
|
|||||||
default = []
|
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" {
|
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."
|
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
|
type = string
|
||||||
|
|||||||
Reference in new issue
Block a user