Add Caddy reverse proxy ansible role
- Create caddy role following zot pattern (manual build, ansible deploy) - Caddy built with Gandi DNS plugin for ACME DNS-01 challenges - Gandi PAT fetched from 1Password and written to secured file on indri - Configure wildcard TLS for *.ops.eblu.me - Initial services: forge, registry (indri-local) - Uses port 8443 during testing to avoid Tailscale serve conflicts Build instructions (on indri): cd ~/code/3rd/caddy && mise run build Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
b08faa50cc
commit
df003e214f
6 changed files with 228 additions and 0 deletions
|
|
@ -78,6 +78,23 @@
|
|||
no_log: true
|
||||
tags: [forgejo_runner]
|
||||
|
||||
# Caddy Gandi token for ACME DNS-01 challenges
|
||||
- name: Fetch Gandi PAT for Caddy
|
||||
ansible.builtin.command:
|
||||
cmd: op --vault vg6xf6vvfmoh5hqjjhlhbeoaie item get mco6ka3dc3rmw7zkg2dhia5d2m --fields pat --reveal
|
||||
delegate_to: localhost
|
||||
register: _caddy_gandi_token
|
||||
changed_when: false
|
||||
no_log: true
|
||||
check_mode: false
|
||||
tags: [caddy]
|
||||
|
||||
- name: Set Caddy Gandi token fact
|
||||
ansible.builtin.set_fact:
|
||||
caddy_gandi_token: "{{ _caddy_gandi_token.stdout }}"
|
||||
no_log: true
|
||||
tags: [caddy]
|
||||
|
||||
roles:
|
||||
- role: alloy
|
||||
tags: alloy
|
||||
|
|
@ -101,3 +118,5 @@
|
|||
tags: tailscale-serve
|
||||
- role: forgejo_runner
|
||||
tags: forgejo_runner
|
||||
- role: caddy
|
||||
tags: caddy
|
||||
|
|
|
|||
37
ansible/roles/caddy/defaults/main.yml
Normal file
37
ansible/roles/caddy/defaults/main.yml
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
---
|
||||
# Caddy reverse proxy configuration
|
||||
# Caddy is built manually from ~/code/3rd/caddy with the Gandi DNS plugin
|
||||
|
||||
caddy_repo_dir: /Users/erichblume/code/3rd/caddy
|
||||
caddy_binary: "{{ caddy_repo_dir }}/bin/caddy"
|
||||
caddy_config_dir: /Users/erichblume/.config/caddy
|
||||
caddy_data_dir: /Users/erichblume/.local/share/caddy
|
||||
caddy_log_dir: /Users/erichblume/Library/Logs
|
||||
|
||||
# Gandi API token file (written by ansible, chmod 0600)
|
||||
# Caddy reads this file for ACME DNS-01 challenges
|
||||
caddy_gandi_token_file: /Users/erichblume/.config/caddy/gandi-token
|
||||
|
||||
# Domain configuration
|
||||
caddy_domain: ops.eblu.me
|
||||
|
||||
# Listen on Tailscale interface only (port 443)
|
||||
# Use 8443 during testing to avoid conflicts with Tailscale serve
|
||||
caddy_https_port: 8443
|
||||
|
||||
# Services to proxy
|
||||
# Format: { name: "service", host: "hostname", backend: "url" }
|
||||
caddy_services:
|
||||
# Indri-local services
|
||||
- name: forge
|
||||
host: "forge.{{ caddy_domain }}"
|
||||
backend: "http://localhost:3001"
|
||||
- name: registry
|
||||
host: "registry.{{ caddy_domain }}"
|
||||
backend: "http://localhost:5050"
|
||||
|
||||
# K8s services (via minikube NodePort or ClusterIP)
|
||||
# These will be configured once we determine the correct backend URLs
|
||||
# - name: grafana
|
||||
# host: "grafana.{{ caddy_domain }}"
|
||||
# backend: "http://minikube-ip:nodeport"
|
||||
13
ansible/roles/caddy/handlers/main.yml
Normal file
13
ansible/roles/caddy/handlers/main.yml
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
---
|
||||
- name: Restart caddy
|
||||
block:
|
||||
- name: Unload caddy LaunchAgent
|
||||
ansible.builtin.command:
|
||||
cmd: launchctl unload ~/Library/LaunchAgents/mcquack.eblume.caddy.plist
|
||||
failed_when: false
|
||||
changed_when: true
|
||||
|
||||
- name: Load caddy LaunchAgent
|
||||
ansible.builtin.command:
|
||||
cmd: launchctl load ~/Library/LaunchAgents/mcquack.eblume.caddy.plist
|
||||
changed_when: true
|
||||
73
ansible/roles/caddy/tasks/main.yml
Normal file
73
ansible/roles/caddy/tasks/main.yml
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
---
|
||||
# Caddy reverse proxy deployment
|
||||
# Binary is built manually - see ~/code/3rd/caddy/mise.toml
|
||||
|
||||
- name: Verify caddy binary exists
|
||||
ansible.builtin.stat:
|
||||
path: "{{ caddy_binary }}"
|
||||
register: caddy_bin
|
||||
failed_when: not caddy_bin.stat.exists
|
||||
changed_when: false
|
||||
|
||||
- name: Create caddy config directory
|
||||
ansible.builtin.file:
|
||||
path: "{{ caddy_config_dir }}"
|
||||
state: directory
|
||||
mode: "0755"
|
||||
|
||||
- name: Create caddy data directory
|
||||
ansible.builtin.file:
|
||||
path: "{{ caddy_data_dir }}"
|
||||
state: directory
|
||||
mode: "0755"
|
||||
|
||||
- name: Fetch Gandi PAT (when running with --tags caddy)
|
||||
ansible.builtin.command:
|
||||
cmd: op --vault vg6xf6vvfmoh5hqjjhlhbeoaie item get mco6ka3dc3rmw7zkg2dhia5d2m --fields pat --reveal
|
||||
delegate_to: localhost
|
||||
register: _caddy_gandi_token_fallback
|
||||
changed_when: false
|
||||
no_log: true
|
||||
check_mode: false
|
||||
when: caddy_gandi_token is not defined
|
||||
|
||||
- name: Set Gandi token fact (fallback)
|
||||
ansible.builtin.set_fact:
|
||||
caddy_gandi_token: "{{ _caddy_gandi_token_fallback.stdout }}"
|
||||
no_log: true
|
||||
when: caddy_gandi_token is not defined
|
||||
|
||||
- name: Write Gandi token file
|
||||
ansible.builtin.copy:
|
||||
content: "{{ caddy_gandi_token }}"
|
||||
dest: "{{ caddy_gandi_token_file }}"
|
||||
mode: "0600"
|
||||
no_log: true
|
||||
notify: Restart caddy
|
||||
|
||||
- name: Deploy Caddyfile
|
||||
ansible.builtin.template:
|
||||
src: Caddyfile.j2
|
||||
dest: "{{ caddy_config_dir }}/Caddyfile"
|
||||
mode: "0644"
|
||||
notify: Restart caddy
|
||||
|
||||
- name: Deploy caddy LaunchAgent plist
|
||||
ansible.builtin.template:
|
||||
src: caddy.plist.j2
|
||||
dest: ~/Library/LaunchAgents/mcquack.eblume.caddy.plist
|
||||
mode: "0644"
|
||||
notify: Restart caddy
|
||||
|
||||
- name: Check if caddy LaunchAgent is loaded
|
||||
ansible.builtin.command:
|
||||
cmd: launchctl list mcquack.eblume.caddy
|
||||
register: caddy_launchctl
|
||||
changed_when: false
|
||||
failed_when: false
|
||||
|
||||
- name: Load caddy LaunchAgent
|
||||
ansible.builtin.command:
|
||||
cmd: launchctl load ~/Library/LaunchAgents/mcquack.eblume.caddy.plist
|
||||
when: caddy_launchctl.rc != 0
|
||||
changed_when: true
|
||||
47
ansible/roles/caddy/templates/Caddyfile.j2
Normal file
47
ansible/roles/caddy/templates/Caddyfile.j2
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
# Caddy reverse proxy for blumeops services
|
||||
# Managed by ansible - do not edit manually
|
||||
#
|
||||
# All *.{{ caddy_domain }} requests are proxied to backend services.
|
||||
# TLS certificates are obtained via ACME DNS-01 challenge using Gandi.
|
||||
|
||||
{
|
||||
# Global options
|
||||
admin off
|
||||
|
||||
# Use ACME DNS-01 challenge with Gandi
|
||||
acme_dns gandi {
|
||||
api_token {file.{{ caddy_gandi_token_file }}}
|
||||
}
|
||||
}
|
||||
|
||||
# Wildcard certificate for all services
|
||||
*.{{ caddy_domain }} {
|
||||
tls {
|
||||
dns gandi {
|
||||
api_token {file.{{ caddy_gandi_token_file }}}
|
||||
}
|
||||
}
|
||||
|
||||
{% for service in caddy_services %}
|
||||
@{{ service.name }} host {{ service.host }}
|
||||
handle @{{ service.name }} {
|
||||
reverse_proxy {{ service.backend }}
|
||||
}
|
||||
|
||||
{% endfor %}
|
||||
# Fallback for unknown hosts
|
||||
handle {
|
||||
respond "Unknown service" 404
|
||||
}
|
||||
}
|
||||
|
||||
# Base domain (ops.eblu.me)
|
||||
{{ caddy_domain }} {
|
||||
tls {
|
||||
dns gandi {
|
||||
api_token {file.{{ caddy_gandi_token_file }}}
|
||||
}
|
||||
}
|
||||
|
||||
respond "blumeops services - use a subdomain (e.g., forge.{{ caddy_domain }})"
|
||||
}
|
||||
39
ansible/roles/caddy/templates/caddy.plist.j2
Normal file
39
ansible/roles/caddy/templates/caddy.plist.j2
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>Label</key>
|
||||
<string>mcquack.eblume.caddy</string>
|
||||
|
||||
<key>ProgramArguments</key>
|
||||
<array>
|
||||
<string>{{ caddy_binary }}</string>
|
||||
<string>run</string>
|
||||
<string>--config</string>
|
||||
<string>{{ caddy_config_dir }}/Caddyfile</string>
|
||||
</array>
|
||||
|
||||
<key>WorkingDirectory</key>
|
||||
<string>{{ caddy_data_dir }}</string>
|
||||
|
||||
<key>EnvironmentVariables</key>
|
||||
<dict>
|
||||
<key>XDG_DATA_HOME</key>
|
||||
<string>/Users/erichblume/.local/share</string>
|
||||
<key>XDG_CONFIG_HOME</key>
|
||||
<string>/Users/erichblume/.config</string>
|
||||
</dict>
|
||||
|
||||
<key>RunAtLoad</key>
|
||||
<true/>
|
||||
|
||||
<key>KeepAlive</key>
|
||||
<true/>
|
||||
|
||||
<key>StandardOutPath</key>
|
||||
<string>{{ caddy_log_dir }}/mcquack.caddy.out.log</string>
|
||||
|
||||
<key>StandardErrorPath</key>
|
||||
<string>{{ caddy_log_dir }}/mcquack.caddy.err.log</string>
|
||||
</dict>
|
||||
</plist>
|
||||
Loading…
Add table
Add a link
Reference in a new issue