Establish clean homelab infrastructure baseline
Reorganize the brownfield repository, remove retired and generated artifacts, harden ignore rules, and record the GitOps/IaC redesign.
This commit is contained in:
@@ -0,0 +1,41 @@
|
||||
---
|
||||
# openbao_acme role defaults. Cloudflare token comes from group_vars/all/vault.yml.
|
||||
|
||||
# --- lego (ACME client) release ---
|
||||
openbao_acme_version: "5.3.1"
|
||||
openbao_acme_checksum: "sha256:b3c71b122ee1947eacfe0b809b955647f6377239fe4bfc49f73b1a091ae1252a"
|
||||
openbao_acme_url: "https://github.com/go-acme/lego/releases/download/v{{ openbao_acme_version }}/lego_v{{ openbao_acme_version }}_linux_amd64.tar.gz"
|
||||
openbao_acme_bin: "/usr/local/bin/lego"
|
||||
|
||||
# --- Paths / identity ---
|
||||
openbao_acme_dir: "/etc/openbao/acme" # lego state (account, certs, cloudflare.env)
|
||||
openbao_acme_tls_dir: "/etc/openbao/tls" # where bao's listener reads cert.pem/key.pem
|
||||
openbao_user: "openbao"
|
||||
openbao_group: "openbao"
|
||||
|
||||
# --- Certificate ---
|
||||
openbao_acme_domain: "{{ openbao_fqdn }}" # bao.ad.ddupan.top (from group_vars/all)
|
||||
openbao_acme_email: "[email protected]" # ACME account / expiry-notice email
|
||||
|
||||
# Cloudflare API token with Zone:DNS:Edit on ddupan.top. Set vault_openbao_cf_dns_token
|
||||
# in group_vars/all/vault.yml (you can copy the value from the cloudflared tunnel's
|
||||
# terraform.tfvars, which is scoped the same).
|
||||
openbao_acme_cf_token: "{{ vault_openbao_cf_dns_token | default('') }}"
|
||||
|
||||
# Empty = Let's Encrypt production. To dry-run without burning rate limits, set:
|
||||
# https://acme-staging-v02.api.letsencrypt.org/directory
|
||||
openbao_acme_server: ""
|
||||
|
||||
# Resolvers for lego's zone/apex detection. Must give the PUBLIC view: both the DC AND
|
||||
# the LAN gateway forward ad.ddupan.top to the DC (split-horizon) → they'd resolve the
|
||||
# zone to the non-existent CF zone "ad.ddupan.top". Only real public resolvers see that
|
||||
# ad.ddupan.top isn't delegated and return the ddupan.top apex. List several so a flaky
|
||||
# WAN query to one falls through to another.
|
||||
openbao_acme_dns_resolvers: "1.1.1.1:53,1.0.0.1:53,8.8.8.8:53,9.9.9.9:53"
|
||||
openbao_acme_dns_timeout: 30 # per-query DNS timeout (s); default 10 is tight over a flaky WAN
|
||||
# Skip the 2-min propagation polling (many WAN DNS queries); just wait, then ask LE to
|
||||
# validate (LE queries public DNS itself, independent of this host's WAN).
|
||||
openbao_acme_propagation_wait: "120s"
|
||||
|
||||
# Renewal timer (lego only renews within --days of expiry).
|
||||
openbao_acme_renew_oncalendar: "*-*-* 03:17:00"
|
||||
@@ -0,0 +1,11 @@
|
||||
---
|
||||
galaxy_info:
|
||||
role_name: openbao_acme
|
||||
description: Publicly-trusted Let's Encrypt cert for bao's listener via lego + Cloudflare DNS-01, with SIGHUP reload and auto-renewal.
|
||||
min_ansible_version: "2.15"
|
||||
platforms:
|
||||
- name: Debian
|
||||
versions: [bookworm]
|
||||
- name: Ubuntu
|
||||
versions: [jammy, noble]
|
||||
dependencies: []
|
||||
@@ -0,0 +1,105 @@
|
||||
---
|
||||
# Install lego, obtain the initial cert (DNS-01 via Cloudflare), deploy it to bao's
|
||||
# listener + reload, and enable a renewal timer. Idempotent.
|
||||
|
||||
- name: Assert a Cloudflare DNS token is available
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- openbao_acme_cf_token | length > 0
|
||||
fail_msg: >-
|
||||
Set vault_openbao_cf_dns_token in group_vars/all/vault.yml — a Cloudflare API
|
||||
token with Zone:DNS:Edit on ddupan.top.
|
||||
|
||||
# --- Install lego (checksum-verified) -----------------------------------------
|
||||
- name: Check installed lego version
|
||||
ansible.builtin.command: "{{ openbao_acme_bin }} --version"
|
||||
register: lego_installed
|
||||
changed_when: false
|
||||
failed_when: false
|
||||
|
||||
- name: Install lego when missing or version mismatch
|
||||
when: openbao_acme_version not in (lego_installed.stdout | default(''))
|
||||
block:
|
||||
- name: Download lego release tarball (checksum-verified)
|
||||
ansible.builtin.get_url:
|
||||
url: "{{ openbao_acme_url }}"
|
||||
dest: "/tmp/lego_{{ openbao_acme_version }}.tar.gz"
|
||||
checksum: "{{ openbao_acme_checksum }}"
|
||||
mode: "0644"
|
||||
|
||||
- name: Create lego staging dir
|
||||
ansible.builtin.file:
|
||||
path: "/tmp/lego_{{ openbao_acme_version }}"
|
||||
state: directory
|
||||
mode: "0755"
|
||||
|
||||
- name: Extract lego
|
||||
ansible.builtin.unarchive:
|
||||
src: "/tmp/lego_{{ openbao_acme_version }}.tar.gz"
|
||||
dest: "/tmp/lego_{{ openbao_acme_version }}"
|
||||
remote_src: true
|
||||
|
||||
- name: Install lego binary
|
||||
ansible.builtin.copy:
|
||||
src: "/tmp/lego_{{ openbao_acme_version }}/lego"
|
||||
dest: "{{ openbao_acme_bin }}"
|
||||
remote_src: true
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0755"
|
||||
|
||||
# --- State dir + credentials + scripts ----------------------------------------
|
||||
- name: Create ACME state directory
|
||||
ansible.builtin.file:
|
||||
path: "{{ openbao_acme_dir }}"
|
||||
state: directory
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0700"
|
||||
|
||||
- name: Write Cloudflare credentials env file
|
||||
ansible.builtin.copy:
|
||||
content: "CLOUDFLARE_DNS_API_TOKEN={{ openbao_acme_cf_token }}\n"
|
||||
dest: "{{ openbao_acme_dir }}/cloudflare.env"
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0600"
|
||||
no_log: true
|
||||
|
||||
- name: Install the obtain/renew wrapper and deploy hook
|
||||
ansible.builtin.template:
|
||||
src: "{{ item }}.j2"
|
||||
dest: "/usr/local/bin/{{ item }}"
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0755"
|
||||
loop:
|
||||
- openbao-acme.sh
|
||||
- openbao-acme-deploy.sh
|
||||
|
||||
- name: Install the ACME systemd service + timer
|
||||
ansible.builtin.template:
|
||||
src: "{{ item }}.j2"
|
||||
dest: "/etc/systemd/system/{{ item }}"
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0644"
|
||||
loop:
|
||||
- openbao-acme.service
|
||||
- openbao-acme.timer
|
||||
|
||||
# --- Obtain the first cert (deploys to bao + reloads via SIGHUP) ---------------
|
||||
- name: Obtain the initial certificate and deploy it
|
||||
ansible.builtin.command: /usr/local/bin/openbao-acme.sh
|
||||
environment:
|
||||
CLOUDFLARE_DNS_API_TOKEN: "{{ openbao_acme_cf_token }}"
|
||||
args:
|
||||
creates: "{{ openbao_acme_dir }}/certificates/{{ openbao_acme_domain }}.crt"
|
||||
no_log: true
|
||||
|
||||
- name: Enable and start the renewal timer
|
||||
ansible.builtin.systemd:
|
||||
name: openbao-acme.timer
|
||||
state: started
|
||||
enabled: true
|
||||
daemon_reload: true
|
||||
@@ -0,0 +1,14 @@
|
||||
#!/usr/bin/env bash
|
||||
# {{ ansible_managed }}
|
||||
# Install the freshly issued/renewed cert into bao's listener and reload (SIGHUP —
|
||||
# no restart, no reseal). Invoked by lego's --deploy-hook on create/renew; lego passes
|
||||
# the cert paths in LEGO_CERT_PATH / LEGO_CERT_KEY_PATH.
|
||||
set -euo pipefail
|
||||
|
||||
CRT="${LEGO_CERT_PATH:-{{ openbao_acme_dir }}/certificates/{{ openbao_acme_domain }}.crt}"
|
||||
KEY="${LEGO_CERT_KEY_PATH:-{{ openbao_acme_dir }}/certificates/{{ openbao_acme_domain }}.key}"
|
||||
|
||||
install -o {{ openbao_user }} -g {{ openbao_group }} -m 0644 "${CRT}" "{{ openbao_acme_tls_dir }}/cert.pem"
|
||||
install -o {{ openbao_user }} -g {{ openbao_group }} -m 0640 "${KEY}" "{{ openbao_acme_tls_dir }}/key.pem"
|
||||
|
||||
systemctl reload openbao
|
||||
@@ -0,0 +1,10 @@
|
||||
# {{ ansible_managed }}
|
||||
[Unit]
|
||||
Description=OpenBao ACME certificate (lego, Cloudflare DNS-01)
|
||||
After=network-online.target openbao.service
|
||||
Wants=network-online.target
|
||||
|
||||
[Service]
|
||||
Type=oneshot
|
||||
EnvironmentFile={{ openbao_acme_dir }}/cloudflare.env
|
||||
ExecStart=/usr/local/bin/openbao-acme.sh
|
||||
@@ -0,0 +1,20 @@
|
||||
#!/usr/bin/env bash
|
||||
# {{ ansible_managed }}
|
||||
# Obtain or renew bao's Let's Encrypt cert via Cloudflare DNS-01. lego's `run` does both:
|
||||
# it renews only when due (ARI + --renew-days) and fires --deploy-hook on any actual
|
||||
# create/renew to install the cert and reload bao. CLOUDFLARE_DNS_API_TOKEN comes from
|
||||
# the environment (systemd EnvironmentFile, or Ansible on the first run).
|
||||
set -euo pipefail
|
||||
|
||||
exec {{ openbao_acme_bin }} run \
|
||||
--accept-tos \
|
||||
--email "{{ openbao_acme_email }}" \
|
||||
--dns cloudflare \
|
||||
--dns.resolvers "{{ openbao_acme_dns_resolvers }}" \
|
||||
--dns.timeout {{ openbao_acme_dns_timeout }} \
|
||||
--dns.propagation.wait "{{ openbao_acme_propagation_wait }}" \
|
||||
--domains "{{ openbao_acme_domain }}" \
|
||||
--path "{{ openbao_acme_dir }}" \
|
||||
--renew-days 30 \
|
||||
--deploy-hook /usr/local/bin/openbao-acme-deploy.sh{% if openbao_acme_server %} \
|
||||
--server "{{ openbao_acme_server }}"{% endif %}
|
||||
@@ -0,0 +1,11 @@
|
||||
# {{ ansible_managed }}
|
||||
[Unit]
|
||||
Description=OpenBao ACME renewal timer
|
||||
|
||||
[Timer]
|
||||
OnCalendar={{ openbao_acme_renew_oncalendar }}
|
||||
RandomizedDelaySec=3600
|
||||
Persistent=true
|
||||
|
||||
[Install]
|
||||
WantedBy=timers.target
|
||||
Reference in New Issue
Block a user