Refactor: migrate cmp/* and srv/* roles into sys-stk/* and sys-svc/* namespaces

- Removed obsolete 'cmp' category, introduced 'stk' category (fa-bars-staggered icon).
- Renamed roles:
  * cmp-db-docker → sys-stk-back-stateful
  * cmp-docker-oauth2 → sys-stk-back-stateless
  * srv-domain-provision → sys-stk-front
  * cmp-db-docker-proxy → sys-stk-full-stateful
  * cmp-docker-proxy → sys-stk-full-stateless
  * cmp-rdbms → sys-svc-rdbms
- Updated all include_role references, vars, templates and README.md files.
- Adjusted run_once comments and variable paths accordingly.
- Updated all web-app roles to use new sys-stk/* and sys-svc/* roles.

Conversation: https://chatgpt.com/share/68b0ba66-09f8-800f-86fc-76c47009d431
This commit is contained in:
2025-08-28 22:23:09 +02:00
parent 92f5bf6481
commit 6ea8301364
94 changed files with 112 additions and 114 deletions

View File

@@ -0,0 +1,35 @@
# Nginx Domain Setup 🚀
## Description
This role bootstraps **per-domain Nginx configuration**: it requests TLS certificates, applies global modifiers, deploys a ready-made vHost file, and can optionally lock down access via OAuth2.
## Overview
A higher-level orchestration wrapper, *sys-stk-front* ties together several lower-level roles:
1. **`sys-srv-web-inj-compose`** applies global tweaks and includes.
2. **`srv-tls-core`** obtains Lets Encrypt certificates.
3. **Domain template deployment** copies a Jinja2 vHost from *srv-proxy-core*.
4. **`web-app-oauth2-proxy`** *(optional)* protects the site with OAuth2.
The result is a complete, reproducible domain rollout in a single playbook task.
## Purpose
Provide **one-stop, idempotent domain provisioning** for Nginx-based homelabs or small production environments.
## Features
- **End-to-end TLS** — certificate retrieval and secure headers included.
- **Template-driven vHosts** — choose *basic* or *ws_generic* flavours (or your own).
- **Conditional OAuth2** — easily toggle authentication per application.
- **Handler-safe** — automatically triggers an Nginx reload when templates change.
- **Composable** — designed to be called repeatedly for many domains.
## Credits 📝
Developed and maintained by **Kevin Veen-Birkenbach**.
Learn more at <https://www.veen.world>
Part of the **Infinito.Nexus Project** — licensed under the [Infinito.Nexus NonCommercial License](https://s.infinito.nexus/license)

View File

@@ -0,0 +1,5 @@
# default vhost flavour
vhost_flavour: "basic" # valid: basic | ws_generic
# build the full template path from the flavour
vhost_template_src: "roles/srv-proxy-core/templates/vhost/{{ vhost_flavour }}.conf.j2"

View File

@@ -0,0 +1,24 @@
galaxy_info:
author: "Kevin Veen-Birkenbach"
description: "Automated domain provisioning (TLS, vHost, OAuth2) for Nginx."
license: "Infinito.Nexus NonCommercial License"
license_url: "https://s.infinito.nexus/license"
company: |
Kevin Veen-Birkenbach
Consulting & Coaching Solutions
https://www.veen.world
min_ansible_version: "2.9"
platforms:
- name: Archlinux
versions:
- rolling
galaxy_tags:
- nginx
- tls
- letsencrypt
- oauth2
- automation
- archlinux
repository: https://s.infinito.nexus/code
issue_tracker_url: https://s.infinito.nexus/issues
documentation: "https://docs.infinito.nexus/"

View File

@@ -0,0 +1,51 @@
# Initialize cache dict (works within the play; persists if fact cache is enabled)
- name: "Ensure cf_zone_ids cache dict exists"
set_fact:
cf_zone_ids: "{{ cf_zone_ids | default({}) }}"
# Use cached zone_id if available for the apex (to_primary_domain)
- name: "Load cf_zone_id from cache if present"
set_fact:
cf_zone_id: "{{ (cf_zone_ids | default({})).get(domain | to_primary_domain, false) }}"
# Only look up from Cloudflare if we still don't have it
- name: "Ensure Cloudflare Zone ID is known for '{{ domain }}'"
vars:
cf_api_url: "https://api.cloudflare.com/client/v4/zones"
ansible.builtin.uri:
url: "{{ cf_api_url }}?name={{ domain | to_primary_domain }}"
method: GET
headers:
Authorization: "Bearer {{ CLOUDFLARE_API_TOKEN }}"
Content-Type: "application/json"
return_content: yes
register: cf_zone_lookup_dev
changed_when: false
when:
- not cf_zone_id
no_log: "{{ MASK_CREDENTIALS_IN_LOGS | bool }}"
- name: "Set fact cf_zone_id and update cache dict"
set_fact:
cf_zone_id: "{{ cf_zone_lookup_dev.json.result[0].id }}"
cf_zone_ids: >-
{{ (cf_zone_ids | default({}))
| combine({ (domain | to_primary_domain): cf_zone_lookup_dev.json.result[0].id }) }}
when:
- not cf_zone_id
- cf_zone_lookup_dev.json.result | length > 0
- name: "Fail if no Cloudflare zone found for {{ domain | to_primary_domain }}"
ansible.builtin.fail:
msg: "No Cloudflare zone found for {{ domain | to_primary_domain }} — aborting!"
when:
- not cf_zone_id
- cf_zone_lookup_dev.json.result | length == 0
- name: activate cloudflare cache development mode
include_tasks: "cloudflare/02_enable_cf_dev_mode.yml"
when: (ENVIRONMENT | lower) == 'development'
- name: purge cloudflare domain cache
include_tasks: "cloudflare/01_cleanup.yml"
when: MODE_CLEANUP | bool

View File

@@ -0,0 +1,13 @@
- name: "Purge everything from Cloudflare cache for domain {{ domain }}"
ansible.builtin.uri:
url: "https://api.cloudflare.com/client/v4/zones/{{ cf_zone_id }}/purge_cache"
method: POST
headers:
Authorization: "Bearer {{ CLOUDFLARE_API_TOKEN }}"
Content-Type: "application/json"
body:
purge_everything: true
body_format: json
return_content: yes
async: "{{ ASYNC_TIME if ASYNC_ENABLED | bool else omit }}"
poll: "{{ ASYNC_POLL if ASYNC_ENABLED | bool else omit }}"

View File

@@ -0,0 +1,35 @@
---
# Enables Cloudflare Development Mode (bypasses cache for ~3 hours).
# Uses the same auth token as in 01_cleanup.yml: CLOUDFLARE_API_TOKEN
# Assumes `domain` and (optionally) `cf_zone_id` are available.
# Safe to run repeatedly; only changes when the mode is not already "on".
- name: "Read current Cloudflare development_mode setting"
ansible.builtin.uri:
url: "https://api.cloudflare.com/client/v4/zones/{{ cf_zone_id }}/settings/development_mode"
method: GET
headers:
Authorization: "Bearer {{ CLOUDFLARE_API_TOKEN }}"
Content-Type: "application/json"
return_content: yes
register: cf_dev_mode_current
when: ASYNC_ENABLED | bool
- name: "Enable Cloudflare Development Mode"
ansible.builtin.uri:
url: "https://api.cloudflare.com/client/v4/zones/{{ cf_zone_id }}/settings/development_mode"
method: PATCH
headers:
Authorization: "Bearer {{ CLOUDFLARE_API_TOKEN }}"
Content-Type: "application/json"
body:
value: "on"
body_format: json
return_content: yes
register: cf_dev_mode_enable
changed_when: >
ASYNC_ENABLED | bool and
cf_dev_mode_current.json.result.value is defined and
cf_dev_mode_current.json.result.value != 'on'
async: "{{ ASYNC_TIME if ASYNC_ENABLED | bool else omit }}"
poll: "{{ ASYNC_POLL if ASYNC_ENABLED | bool else omit }}"

View File

@@ -0,0 +1,42 @@
- block:
- name: Include dependency 'srv-proxy-core'
include_role:
name: srv-proxy-core
when: run_once_srv_proxy_core is not defined
- include_tasks: utils/run_once.yml
when: run_once_sys_stk_front is not defined
- include_tasks: "01_cloudflare.yml"
when: DNS_PROVIDER == "cloudflare"
- include_tasks: "{{ playbook_dir }}/tasks/utils/load_handlers.yml"
vars:
handler_role_name: "svc-prx-openresty"
- name: "include role for {{ domain }} to receive certificates and do the modification routines"
include_role:
name: srv-composer
- name: "Copy nginx config to {{ configuration_destination }}"
template:
src: "{{ vhost_template_src }}"
dest: "{{ configuration_destination }}"
register: nginx_conf
notify: restart openresty
- block:
- name: "Check if {{ domains | get_domain(application_id) }} is reachable (only if config unchanged)"
uri:
url: "{{ domains | get_url(application_id, WEB_PROTOCOL) }}"
register: site_check
failed_when: false
changed_when: false
- name: Restart nginx if site is down
command:
cmd: "true"
notify: restart openresty
when:
- site_check.status is defined
- not site_check.status in [200,301,302]
when: not nginx_conf.changed

View File

@@ -0,0 +1 @@
configuration_destination: "{{ NGINX.DIRECTORIES.HTTP.SERVERS }}{{ domain }}.conf"