From 1fb7bfacc6b06853535bbbe12f1cdd4f4556866d Mon Sep 17 00:00:00 2001 From: Konstantin Renner Date: Sat, 27 Jun 2026 11:20:36 +0200 Subject: [PATCH] cleanup(ingress): drop legacy Caddy/cloudflared/netbird-gateway tiers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The netbird-ingress DaemonSet, netbird reverse-proxy, and Traefik have fully superseded the old ingress tiers, which have been stopped (not deleted) since the 2026-05-09 PVE cutover. Remove the dead code: - Caddy HA: LXCs 232/233/234 (opentofu/ingress.tf), caddy-ha role, install-caddy-ha + install-crowdsec-caddy plays, caddy cred vars, proxmox_caddy_ha group_vars, build-caddy/deploy-caddy pipelines, ci-caddy-builder image. - cloudflared: LXC 117, cloudflared-lxc role, install-cloudflared play, cloudflare_tunnel/variables_cloudflare disabled tofu stubs. - netbird-gateway: LXC 260 (replaced by the in-cluster netbird-ingress DaemonSet), netbird-gateway role + play. - backup_jobs.tf: drop VMIDs 117/232/233/234/260 from the nightly vzdump include list (those LXCs are being destroyed). - netbird-server: point the setup-key hint at netbird-ingress/netbird-proxy. Kept deliberately: - crowdsec role (reusable; basis for the pending Traefik bouncer). - netbird-client role + play (loki/prometheus LXCs are tagged netbird_client, so it remains live architecture). Note: applying the opentofu change will plan a DESTROY of stopped LXCs 117/232/233/234/260 — review the plan before apply. Co-Authored-By: Claude Opus 4.8 (1M context) --- .woodpecker/build-caddy.yml | 35 -- .woodpecker/build-ci-images.yml | 20 - .woodpecker/deploy-caddy.yml | 35 -- .../group_vars/proxmox_caddy_ha/vars.yml | 4 - .../group_vars/proxmox_caddy_ha/vault.yml | 9 - ansible/plays/install-caddy-ha.yml | 18 - ansible/plays/install-cloudflared.yml | 7 - ansible/plays/install-crowdsec-caddy.yml | 25 -- ansible/plays/install-netbird-gateway.yml | 17 - .../vars/caddy_generated_credentials.yml | 18 - .../plays/vars/caddy_setup_credentials.yml | 10 - ansible/roles/caddy-ha/defaults/main.yml | 37 -- ansible/roles/caddy-ha/files/setup_s3.py | 199 --------- ansible/roles/caddy-ha/handlers/main.yml | 10 - ansible/roles/caddy-ha/tasks/main.yml | 201 --------- ansible/roles/caddy-ha/templates/Caddyfile.j2 | 395 ------------------ .../templates/caddy-dashboard.service.j2 | 12 - .../templates/caddy-dashboard.timer.j2 | 9 - .../roles/caddy-ha/templates/caddy.service.j2 | 35 -- .../templates/generate_dashboard.py.j2 | 199 --------- .../caddy-ha/templates/keepalived.conf.j2 | 23 - .../roles/cloudflared-lxc/defaults/main.yml | 2 - ansible/roles/cloudflared-lxc/tasks/main.yml | 43 -- .../roles/netbird-gateway/defaults/main.yml | 12 - .../roles/netbird-gateway/handlers/main.yml | 12 - ansible/roles/netbird-gateway/tasks/main.yml | 82 ---- ansible/roles/netbird-server/tasks/main.yml | 3 +- ci/images/caddy-builder/Containerfile | 5 - opentofu/backup_jobs.tf | 5 - opentofu/cloudflare_tunnel.tf.disabled | 43 -- opentofu/ingress.tf | 106 ----- opentofu/lxc_117_cloudflared_lxc.tf | 34 -- opentofu/lxc_260_netbird_gateway.tf | 28 -- opentofu/variables_cloudflare.tf.disabled | 11 - 34 files changed, 2 insertions(+), 1702 deletions(-) delete mode 100644 .woodpecker/build-caddy.yml delete mode 100644 .woodpecker/deploy-caddy.yml delete mode 100644 ansible/inventory/group_vars/proxmox_caddy_ha/vars.yml delete mode 100644 ansible/inventory/group_vars/proxmox_caddy_ha/vault.yml delete mode 100644 ansible/plays/install-caddy-ha.yml delete mode 100644 ansible/plays/install-cloudflared.yml delete mode 100644 ansible/plays/install-crowdsec-caddy.yml delete mode 100644 ansible/plays/install-netbird-gateway.yml delete mode 100644 ansible/plays/vars/caddy_generated_credentials.yml delete mode 100644 ansible/plays/vars/caddy_setup_credentials.yml delete mode 100644 ansible/roles/caddy-ha/defaults/main.yml delete mode 100644 ansible/roles/caddy-ha/files/setup_s3.py delete mode 100644 ansible/roles/caddy-ha/handlers/main.yml delete mode 100644 ansible/roles/caddy-ha/tasks/main.yml delete mode 100644 ansible/roles/caddy-ha/templates/Caddyfile.j2 delete mode 100644 ansible/roles/caddy-ha/templates/caddy-dashboard.service.j2 delete mode 100644 ansible/roles/caddy-ha/templates/caddy-dashboard.timer.j2 delete mode 100644 ansible/roles/caddy-ha/templates/caddy.service.j2 delete mode 100644 ansible/roles/caddy-ha/templates/generate_dashboard.py.j2 delete mode 100644 ansible/roles/caddy-ha/templates/keepalived.conf.j2 delete mode 100644 ansible/roles/cloudflared-lxc/defaults/main.yml delete mode 100644 ansible/roles/cloudflared-lxc/tasks/main.yml delete mode 100644 ansible/roles/netbird-gateway/defaults/main.yml delete mode 100644 ansible/roles/netbird-gateway/handlers/main.yml delete mode 100644 ansible/roles/netbird-gateway/tasks/main.yml delete mode 100644 ci/images/caddy-builder/Containerfile delete mode 100644 opentofu/cloudflare_tunnel.tf.disabled delete mode 100644 opentofu/ingress.tf delete mode 100644 opentofu/lxc_117_cloudflared_lxc.tf delete mode 100644 opentofu/lxc_260_netbird_gateway.tf delete mode 100644 opentofu/variables_cloudflare.tf.disabled diff --git a/.woodpecker/build-caddy.yml b/.woodpecker/build-caddy.yml deleted file mode 100644 index 6987c562..00000000 --- a/.woodpecker/build-caddy.yml +++ /dev/null @@ -1,35 +0,0 @@ -when: - - event: push - path: ".woodpecker/build-caddy.yml" - - event: manual - evaluate: 'PIPELINE == "build-caddy"' - -steps: - - name: build - image: gitea.renner.uno/gitea_admin/ci-caddy-builder:2026-05-01 - commands: - - xcaddy build v2.11.2 - --with github.com/caddy-dns/cloudflare - --with github.com/ss098/certmagic-s3 - --with github.com/hslatman/caddy-crowdsec-bouncer/http@v0.10.0 - --with github.com/hslatman/caddy-crowdsec-bouncer/appsec@v0.10.0 - --with github.com/mholt/caddy-l4 - --output caddy - - ./caddy version - - ./caddy list-modules | grep -c layer4 - - - name: release - image: gitea.renner.uno/gitea_admin/ci-k8s:2026-04-29 - environment: - GITEA_TOKEN: - from_secret: gitea_token - commands: - # Delete old asset from 'latest' release, then upload new one - - "RELEASE=$(curl -s -H \"Authorization: token $GITEA_TOKEN\" https://gitea.renner.uno/api/v1/repos/gitea_admin/homelab/releases/tags/latest)" - - "RELEASE_ID=$(echo $RELEASE | jq -r '.id')" - - "echo \"Release ID: $RELEASE_ID\"" - # Delete existing caddy asset if present - - "ASSET_ID=$(echo $RELEASE | jq -r '.assets[]? | select(.name==\"caddy\") | .id')" - - "[ -n \"$ASSET_ID\" ] && curl -s -X DELETE -H \"Authorization: token $GITEA_TOKEN\" https://gitea.renner.uno/api/v1/repos/gitea_admin/homelab/releases/$RELEASE_ID/assets/$ASSET_ID || true" - # Upload new binary - - "curl -s -X POST -H \"Authorization: token $GITEA_TOKEN\" -F attachment=@caddy https://gitea.renner.uno/api/v1/repos/gitea_admin/homelab/releases/$RELEASE_ID/assets?name=caddy | jq '{name: .name, size: .size}'" diff --git a/.woodpecker/build-ci-images.yml b/.woodpecker/build-ci-images.yml index 6c80280f..55ad4342 100644 --- a/.woodpecker/build-ci-images.yml +++ b/.woodpecker/build-ci-images.yml @@ -67,26 +67,6 @@ steps: --destination gitea.renner.uno/gitea_admin/ci-tofu:$TAG \ --destination gitea.renner.uno/gitea_admin/ci-tofu:latest - - name: build-caddy-builder - image: gcr.io/kaniko-project/executor:v1.23.2-debug - directory: ci/images/caddy-builder - depends_on: [] - environment: - GITEA_USER: gitea_admin - GITEA_TOKEN: - from_secret: gitea_token - commands: - - mkdir -p /kaniko/.docker - - AUTH=$(printf '%s:%s' "$GITEA_USER" "$GITEA_TOKEN" | base64 | tr -d '\n') - - printf '{"auths":{"gitea.renner.uno":{"auth":"%s"}}}' "$AUTH" > /kaniko/.docker/config.json - - TAG=$(date +%Y-%m-%d) - - | - /kaniko/executor \ - --context . \ - --dockerfile Containerfile \ - --destination gitea.renner.uno/gitea_admin/ci-caddy-builder:$TAG \ - --destination gitea.renner.uno/gitea_admin/ci-caddy-builder:latest - - name: build-claude image: gcr.io/kaniko-project/executor:v1.23.2-debug directory: ci/images/claude diff --git a/.woodpecker/deploy-caddy.yml b/.woodpecker/deploy-caddy.yml deleted file mode 100644 index 8fbb959a..00000000 --- a/.woodpecker/deploy-caddy.yml +++ /dev/null @@ -1,35 +0,0 @@ -when: - - event: push - path: - - "ansible/roles/caddy-ha/**" - - "ansible/plays/install-caddy-ha.yml" - - ".woodpecker/deploy-caddy.yml" - - event: manual - evaluate: 'PIPELINE == "deploy-caddy"' - -timeout: 30 - -steps: - - name: deploy-caddy - image: gitea.renner.uno/gitea_admin/ci-ansible:2026-04-29 - environment: - ANSIBLE_FORCE_COLOR: "true" - ANSIBLE_HOST_KEY_CHECKING: "false" - ANSIBLE_ROLES_PATH: "../roles:./roles" - SSH_PRIVATE_KEY: - from_secret: ssh_private_key - VAULT_PASS: - from_secret: vault_pass - PROXMOX_TOKEN_ID: - from_secret: proxmox_token_id - PROXMOX_TOKEN_SECRET: - from_secret: proxmox_token_secret - commands: - - mkdir -p ~/.ssh - - echo "$SSH_PRIVATE_KEY" | base64 -d > ~/.ssh/id_ed25519 - - chmod 600 ~/.ssh/id_ed25519 - - printf "Host *\n\tStrictHostKeyChecking no\n\tUserKnownHostsFile /dev/null\n" > ~/.ssh/config - - cd ansible - - echo "$VAULT_PASS" > .vault_pass && chmod 600 .vault_pass - - export ANSIBLE_VAULT_PASSWORD_FILE=.vault_pass - - ansible-playbook -i inventory/ plays/install-caddy-ha.yml -v diff --git a/ansible/inventory/group_vars/proxmox_caddy_ha/vars.yml b/ansible/inventory/group_vars/proxmox_caddy_ha/vars.yml deleted file mode 100644 index 97cc88dd..00000000 --- a/ansible/inventory/group_vars/proxmox_caddy_ha/vars.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -# NetBird client configuration for Caddy HA servers -# Setup key is stored in vault.yml -netbird_setup_key: "{{ vault_caddy_netbird_setup_key }}" diff --git a/ansible/inventory/group_vars/proxmox_caddy_ha/vault.yml b/ansible/inventory/group_vars/proxmox_caddy_ha/vault.yml deleted file mode 100644 index 21550435..00000000 --- a/ansible/inventory/group_vars/proxmox_caddy_ha/vault.yml +++ /dev/null @@ -1,9 +0,0 @@ -$ANSIBLE_VAULT;1.1;AES256 -37363434633432396531333038633664663632333963646630353239316332646638323364383162 -3338306237343739306231613164383064383037616463320a363961313964326438373762666365 -35313464333033653130316539356431303063323861333734313766373061323365613431343966 -3766636265626530370a643261653134313563663035623164353566653139383832313161386562 -62376364336565313739306130336434323638306137326263663030393633333831646562623138 -37313937363338393161313964396462346264633339636464663539383034373837376430366237 -36626664623762623130316163373933383335396463616161333434303863636330633439376131 -62393862393065396238 diff --git a/ansible/plays/install-caddy-ha.yml b/ansible/plays/install-caddy-ha.yml deleted file mode 100644 index 7260c458..00000000 --- a/ansible/plays/install-caddy-ha.yml +++ /dev/null @@ -1,18 +0,0 @@ -- name: Configure Caddy High Availability - hosts: proxmox_caddy_ha - serial: 1 - become: true - - tasks: - - name: Apply Caddy High Availability Role - import_role: - name: caddy-ha - vars: - caddy_ha_vip: 10.2.45.100 - caddy_ha_keepalived_auth_pass: "SecureCaddyHA" - caddy_ha_primary_host: caddy.dmz.renner.uno - caddy_ha_priorities: - "caddy.dmz.renner.uno": 100 - "caddy2.dmz.renner.uno": 90 - "caddy3.dmz.renner.uno": 80 - diff --git a/ansible/plays/install-cloudflared.yml b/ansible/plays/install-cloudflared.yml deleted file mode 100644 index f39d0aad..00000000 --- a/ansible/plays/install-cloudflared.yml +++ /dev/null @@ -1,7 +0,0 @@ ---- -- name: Setup Cloudflared LXC - hosts: proxmox_cloudflared - remote_user: root - become: true - roles: - - cloudflared-lxc diff --git a/ansible/plays/install-crowdsec-caddy.yml b/ansible/plays/install-crowdsec-caddy.yml deleted file mode 100644 index 9a10aadf..00000000 --- a/ansible/plays/install-crowdsec-caddy.yml +++ /dev/null @@ -1,25 +0,0 @@ ---- -- name: Deploy CrowdSec on Caddy HA servers - hosts: proxmox_caddy_ha - serial: 1 - become: true - vars_files: - - vars/rustfs_vault.yml - - vars/caddy_setup_credentials.yml - - [ "vars/caddy_generated_credentials.yml", "vars/empty.yml" ] - - tasks: - - name: Install and configure CrowdSec - import_role: - name: crowdsec - - - name: Apply Caddy High Availability Role with CrowdSec - import_role: - name: caddy-ha - vars: - caddy_ha_vip: 10.2.45.100 - caddy_ha_keepalived_auth_pass: "SecureCaddyHA" - caddy_ha_primary_host: caddy.srv.renner.uno - crowdsec_enabled: true - crowdsec_lapi_url: "http://127.0.0.1:8080" - crowdsec_appsec_url: "http://127.0.0.1:7422" diff --git a/ansible/plays/install-netbird-gateway.yml b/ansible/plays/install-netbird-gateway.yml deleted file mode 100644 index 838b698d..00000000 --- a/ansible/plays/install-netbird-gateway.yml +++ /dev/null @@ -1,17 +0,0 @@ ---- -# Deploy NetBird Gateway to Homelab LXC -# Usage: ansible-playbook -i inventory/ plays/install-netbird-gateway.yml -# -# Prerequisites: -# - LXC 260 created via OpenTofu -# - NetBird server running at netbird.renner.uno -# - Setup key generated from NetBird management UI -# - Vault secrets configured: -# - netbird_gateway_setup_key - -- name: Deploy NetBird Gateway - hosts: netbird-gateway.srv.renner.uno - become: true - roles: - - common - - netbird-gateway diff --git a/ansible/plays/vars/caddy_generated_credentials.yml b/ansible/plays/vars/caddy_generated_credentials.yml deleted file mode 100644 index 0d997334..00000000 --- a/ansible/plays/vars/caddy_generated_credentials.yml +++ /dev/null @@ -1,18 +0,0 @@ ---- -caddy_ha_s3_access_key: !vault | - $ANSIBLE_VAULT;1.1;AES256 - 62306337353164323163363334613963396563333133313634316164643632646537386463393730 - 3934363964613331326663363762303833393766353537650a663139623966316362646437653362 - 62356231623736333735643639326434333737323737623336366433373433643733323463396132 - 6230626633666137370a316239363837303038633738376531373261303633343635323737323732 - 6166 - -caddy_ha_s3_secret_key: !vault | - $ANSIBLE_VAULT;1.1;AES256 - 35353431396362333634613134643733383562366230303035383563653131383134376238373539 - 3765623535373439343637373733303531653565663561380a306364303962613265623363623231 - 31303430646131313737306361343661373238346166386562636361383437376561373161303234 - 6164313036613065370a393662373466326361396134633434633034616235386366323132373964 - 37386361373262396438396633613865643235346563396434336330636330643438383638343661 - 3365333063663835393635306439613137313161343232333565 - diff --git a/ansible/plays/vars/caddy_setup_credentials.yml b/ansible/plays/vars/caddy_setup_credentials.yml deleted file mode 100644 index b9f8a964..00000000 --- a/ansible/plays/vars/caddy_setup_credentials.yml +++ /dev/null @@ -1,10 +0,0 @@ -$ANSIBLE_VAULT;1.1;AES256 -66343132346639356335353436323062303930326233386334653465663837326237643939623132 -6431623632316361656564333137323038323034326365370a396262643664396533616139623162 -64626635626364383561623964373236316335343633616230353530633933356661326532616666 -3639663032636630330a653264623562303433343830663437313239373135643362373864396537 -62323733373461336164323864346530386262623764386433326163663561626466666634326234 -33356639373364393431373236656537613434323638333033336137306535326133636666373565 -66333363316339376131633434373435343139393666623264366463653262346237333564643536 -39636335376330383133333062616637656530333366633266663531386537626163646566303637 -64653834653439316133323034303764643535353165343635343539613162636635 diff --git a/ansible/roles/caddy-ha/defaults/main.yml b/ansible/roles/caddy-ha/defaults/main.yml deleted file mode 100644 index 9d9b4301..00000000 --- a/ansible/roles/caddy-ha/defaults/main.yml +++ /dev/null @@ -1,37 +0,0 @@ ---- -caddy_ha_primary_host: caddy.dmz.renner.uno -caddy_ha_vip: 10.2.45.100 -caddy_ha_keepalived_auth_pass: "SecureCaddyHA" -caddy_ha_keepalived_interface: "{{ ansible_default_ipv4.interface | default('ens18') }}" - -# Keepalived priorities per host (higher = more preferred) -caddy_ha_priorities: - "caddy.dmz.renner.uno": 100 - "caddy2.dmz.renner.uno": 90 - "caddy3.dmz.renner.uno": 80 - -# RustFS / S3 Storage for Certificates -caddy_ha_s3_endpoint: "s3.renner.uno:9000" -caddy_ha_s3_bucket: "caddy-certificates" -# Default credentials for Caddy (will be overridden by vault or setup script) -# caddy_ha_s3_access_key: "" -# caddy_ha_s3_secret_key: "" -caddy_ha_s3_insecure: true - -# Admin credentials for setup (should be loaded from secure vars/vault) -# caddy_ha_s3_admin_access_key: "..." -# caddy_ha_s3_admin_secret_key: "..." - -caddy_version: "v2.11.2" - -# Caddy Binary Download URL -# Replace 'owner' with the user/org where the caddy-builder repo is located -caddy_ha_caddy_binary_url: "https://git.renner.uno/gitea_admin/homelab/releases/download/latest/caddy" - -# CrowdSec Integration -crowdsec_enabled: true -crowdsec_lapi_url: "http://127.0.0.1:8080" -crowdsec_appsec_url: "http://127.0.0.1:7422" -crowdsec_ticker_interval: "15s" -# API key is set per-host from the crowdsec role output -# crowdsec_caddy_api_key: "" diff --git a/ansible/roles/caddy-ha/files/setup_s3.py b/ansible/roles/caddy-ha/files/setup_s3.py deleted file mode 100644 index 50e2f7cd..00000000 --- a/ansible/roles/caddy-ha/files/setup_s3.py +++ /dev/null @@ -1,199 +0,0 @@ -import boto3 -import sys -import os -import json -import subprocess -import requests -from botocore.client import Config -from botocore.auth import SigV4Auth -from botocore.awsrequest import AWSRequest -from botocore.credentials import Credentials -from botocore.exceptions import ClientError -from urllib.parse import urlparse - -endpoint = os.environ.get("S3_ENDPOINT") -admin_access_key = os.environ.get("S3_ADMIN_ACCESS_KEY") -admin_secret_key = os.environ.get("S3_ADMIN_SECRET_KEY") -existing_access_key = os.environ.get("S3_EXISTING_ACCESS_KEY") -existing_secret_key = os.environ.get("S3_EXISTING_SECRET_KEY") -bucket_name = os.environ.get("S3_BUCKET") -insecure = os.environ.get("S3_INSECURE", "false").lower() == "true" -vault_output_file = os.environ.get("S3_VAULT_FILE") -target_user = "caddy-ha" - -if not endpoint.startswith("http"): - endpoint = f"http://{endpoint}" if insecure else f"https://{endpoint}" - -# Security Check: Ensure existing keys are not the admin keys -if existing_access_key and existing_access_key == admin_access_key: - sys.stderr.write("Warning: Provided existing access key matches admin access key. Ignoring to prevent using admin credentials for Caddy.\n") - existing_access_key = None - existing_secret_key = None - -# Test existing keys if provided -if existing_access_key and existing_secret_key: - try: - s3_check = boto3.client('s3', - endpoint_url=endpoint, - aws_access_key_id=existing_access_key, - aws_secret_access_key=existing_secret_key, - config=Config(signature_version='s3v4'), - region_name='us-east-1', - verify=not insecure) - s3_check.head_bucket(Bucket=bucket_name) - # If successful, return existing keys - print(json.dumps({ - "bucket_created": False, - "access_key": existing_access_key, - "secret_key": existing_secret_key, - "used_existing": True - })) - sys.exit(0) - except Exception as e: - sys.stderr.write(f"Warning: Existing keys invalid or insufficient permissions: {e}. Generating new ones.\n") - -# Clients (Admin) -s3 = boto3.client('s3', - endpoint_url=endpoint, - aws_access_key_id=admin_access_key, - aws_secret_access_key=admin_secret_key, - config=Config(signature_version='s3v4'), - region_name='us-east-1', - verify=not insecure) - -# RustFS Admin API helper -def rustfs_admin_request(method, path, params=None, data=None): - import hashlib - if params is None: - params = {} - - url = f"{endpoint}{path}" - - # Calculate Payload Hash - content = data.encode('utf-8') if data else b'' - payload_hash = hashlib.sha256(content).hexdigest() - - headers = {} - if data: - headers["Content-Type"] = "application/json" - headers["x-amz-content-sha256"] = payload_hash - - request = AWSRequest( - method=method, - url=url, - data=data, - params=params, - headers=headers - ) - - # Sign Request - creds = Credentials(admin_access_key, admin_secret_key) - auth = SigV4Auth(creds, "s3", "us-east-1") - auth.add_auth(request) - - # Send Request - response = requests.request( - method=method, - url=request.url, - headers=dict(request.headers), - data=data, - params=params, - verify=not insecure - ) - return response - -def ensure_bucket(): - sys.stderr.write(f"Checking bucket {bucket_name}...\n") - try: - s3.head_bucket(Bucket=bucket_name) - sys.stderr.write(f"Bucket {bucket_name} exists.\n") - except ClientError as e: - error_code = int(e.response['Error']['Code']) - if error_code == 404: - sys.stderr.write(f"Bucket {bucket_name} not found. Creating...\n") - s3.create_bucket(Bucket=bucket_name) - sys.stderr.write(f"Bucket {bucket_name} created.\n") - else: - raise - -def generate_random_key(length=20): - import secrets - import string - alphabet = string.ascii_letters + string.digits - return ''.join(secrets.choice(alphabet) for i in range(length)) - -def ensure_user_and_keys(): - # RustFS specific implementation - - new_secret_key = generate_random_key(40) - - payload = json.dumps({ - "secretKey": new_secret_key, - "status": "enabled" - }) - - # 1. Add User (or update password) - sys.stderr.write(f"Updating user {target_user}...\n") - resp = rustfs_admin_request("PUT", "/rustfs/admin/v3/add-user", params={"accessKey": target_user}, data=payload) - - if resp.status_code != 200: - sys.stderr.write(f"Warning: Failed to create/update user via RustFS API: {resp.status_code} {resp.text}\n") - return None, None - - # 2. Set Policy (Assign 'readwrite' canned policy) - # Note: Ideally we should create a custom policy restricted to the bucket, - # but for now we use 'readwrite' to ensure functionality. - sys.stderr.write(f"Setting policy for user {target_user}...\n") - resp_policy = rustfs_admin_request("PUT", "/rustfs/admin/v3/set-user-or-group-policy", - params={ - "userOrGroup": target_user, - "policyName": "readwrite", - "isGroup": "false" - } - ) - - if resp_policy.status_code != 200: - sys.stderr.write(f"Warning: Failed to set policy for user: {resp_policy.status_code} {resp_policy.text}\n") - # Proceed anyway? Or fail? The user might not work. - # We'll return keys but warn. - - return target_user, new_secret_key - -def encrypt_string(value, var_name): - cmd = ["ansible-vault", "encrypt_string", value, "--name", var_name] - result = subprocess.run(cmd, capture_output=True, text=True) - if result.returncode != 0: - raise Exception(f"Failed to encrypt string: {result.stderr}") - return result.stdout - -try: - ensure_bucket() - ak, sk = ensure_user_and_keys() - - if ak and sk and vault_output_file: - # Write to vault file - with open(vault_output_file, "w") as f: - f.write("---\n") - f.write(encrypt_string(ak, "caddy_ha_s3_access_key")) - f.write("\n") - f.write(encrypt_string(sk, "caddy_ha_s3_secret_key")) - f.write("\n") - print(f"Credentials encrypted and written to {vault_output_file}", file=sys.stderr) - elif not ak or not sk: - raise Exception("Failed to generate access keys") - - result = { - "bucket_created": True, - "access_key": ak, - "secret_key": sk, - "used_existing": False - } - - print(json.dumps(result)) - -except Exception as e: - import traceback - traceback.print_exc() - sys.stderr.write(f"Error: {e}\n") - sys.exit(1) - diff --git a/ansible/roles/caddy-ha/handlers/main.yml b/ansible/roles/caddy-ha/handlers/main.yml deleted file mode 100644 index 564894dc..00000000 --- a/ansible/roles/caddy-ha/handlers/main.yml +++ /dev/null @@ -1,10 +0,0 @@ ---- -- name: Restart Keepalived - service: - name: keepalived - state: restarted - -- name: Reload Caddy - service: - name: caddy - state: restarted \ No newline at end of file diff --git a/ansible/roles/caddy-ha/tasks/main.yml b/ansible/roles/caddy-ha/tasks/main.yml deleted file mode 100644 index 8768e1af..00000000 --- a/ansible/roles/caddy-ha/tasks/main.yml +++ /dev/null @@ -1,201 +0,0 @@ ---- -- name: Determine HA State - set_fact: - keepalived_state: "{{ 'MASTER' if inventory_hostname == caddy_ha_primary_host else 'BACKUP' }}" - keepalived_priority: "{{ caddy_ha_priorities[inventory_hostname] | default(80) }}" - -- name: Install dependencies - apt: - pkg: - - debian-keyring - - debian-archive-keyring - - apt-transport-https - - keepalived - - curl - - tar - - libcap2-bin - state: present - update_cache: yes - register: result - -- name: Ensure caddy group exists - group: - name: caddy - system: yes - state: present - -- name: Ensure caddy user exists - user: - name: caddy - group: caddy - system: yes - home: /var/lib/caddy - shell: /usr/sbin/nologin - create_home: yes - state: present - -- name: Download Caddy Custom Binary - get_url: - url: "{{ caddy_ha_caddy_binary_url }}" - dest: /usr/bin/caddy - mode: '0755' - owner: root - group: root - notify: Reload Caddy - register: caddy_download - ignore_errors: true - -- name: Build Caddy with xcaddy (Fallback) - when: caddy_download is failed - block: - - name: Install Build Dependencies (git, curl) - apt: - pkg: - - curl - - git - - tar - state: present - update_cache: yes - - - name: Download Go - get_url: - url: "https://go.dev/dl/go1.24.0.linux-amd64.tar.gz" - dest: "/tmp/go1.24.0.linux-amd64.tar.gz" - - - name: Remove existing Go installation - file: - path: /usr/local/go - state: absent - - - name: Extract Go - unarchive: - src: "/tmp/go1.24.0.linux-amd64.tar.gz" - dest: "/usr/local" - remote_src: yes - - - name: Link Go binary - file: - src: /usr/local/go/bin/go - dest: /usr/bin/go - state: link - force: yes - - - name: Install xcaddy (Add GPG key) - get_url: - url: https://dl.cloudsmith.io/public/caddy/xcaddy/gpg.key - dest: /usr/share/keyrings/caddy-xcaddy-archive-keyring.asc - mode: '0644' - - - name: De-armor GPG key - shell: cat /usr/share/keyrings/caddy-xcaddy-archive-keyring.asc | gpg --dearmor -o /usr/share/keyrings/caddy-xcaddy-archive-keyring.gpg - args: - creates: /usr/share/keyrings/caddy-xcaddy-archive-keyring.gpg - - - name: Install xcaddy (Add Repository) - copy: - content: "deb [signed-by=/usr/share/keyrings/caddy-xcaddy-archive-keyring.gpg] https://dl.cloudsmith.io/public/caddy/xcaddy/deb/debian any-version main" - dest: /etc/apt/sources.list.d/caddy-xcaddy.list - - - name: Install xcaddy - apt: - name: xcaddy - state: present - update_cache: yes - - - name: Build Caddy - command: - cmd: > - xcaddy build {{ caddy_version }} - --output /usr/bin/caddy - --with github.com/caddy-dns/cloudflare - --with github.com/ss098/certmagic-s3 - --with github.com/hslatman/caddy-crowdsec-bouncer/http@v0.10.0 - --with github.com/hslatman/caddy-crowdsec-bouncer/appsec@v0.10.0 - environment: - GOCACHE: "/tmp/.gocache" - PATH: "/usr/local/go/bin:{{ ansible_env.PATH }}" - register: xcaddy_build - notify: Reload Caddy - -# Compatibility: Allow binding to 80/443 without root -- name: Set capabilities for Caddy - command: setcap cap_net_bind_service=+ep /usr/bin/caddy - when: caddy_download.changed or (xcaddy_build is defined and xcaddy_build.changed) - -- name: Create Caddy config directory - file: - path: /etc/caddy - state: directory - owner: caddy - group: caddy - mode: '0755' - -- name: Ensure Caddy log directory exists - file: - path: /var/log/caddy - state: directory - owner: caddy - group: caddy - mode: '0755' - -- name: Configure Caddyfile - template: - src: Caddyfile.j2 - dest: /etc/caddy/Caddyfile - owner: caddy - group: caddy - mode: '0644' - notify: Reload Caddy - -- name: Install Systemd Service - template: - src: caddy.service.j2 - dest: /etc/systemd/system/caddy.service - owner: root - group: root - mode: '0644' - notify: Reload Caddy - -- name: Setup Caddy Dashboard Generator - template: - src: generate_dashboard.py.j2 - dest: /usr/local/bin/generate_dashboard.py - mode: '0755' - -- name: Setup Dashboard Service - template: - src: caddy-dashboard.service.j2 - dest: /etc/systemd/system/caddy-dashboard.service - mode: '0644' - -- name: Setup Dashboard Timer - template: - src: caddy-dashboard.timer.j2 - dest: /etc/systemd/system/caddy-dashboard.timer - mode: '0644' - -- name: Enable Dashboard Timer - systemd: - name: caddy-dashboard.timer - state: started - enabled: yes - daemon_reload: yes - -- name: Enable and Start Caddy Service - systemd: - name: caddy - state: started - enabled: yes - daemon_reload: yes - -- name: Configure Keepalived - template: - src: keepalived.conf.j2 - dest: /etc/keepalived/keepalived.conf - owner: root - group: root - mode: '0644' - notify: Restart Keepalived - vars: - keepalived_vip: "{{ caddy_ha_vip }}" - keepalived_auth_pass: "{{ caddy_ha_keepalived_auth_pass }}" diff --git a/ansible/roles/caddy-ha/templates/Caddyfile.j2 b/ansible/roles/caddy-ha/templates/Caddyfile.j2 deleted file mode 100644 index ddc57e09..00000000 --- a/ansible/roles/caddy-ha/templates/Caddyfile.j2 +++ /dev/null @@ -1,395 +0,0 @@ -# The Caddyfile is an easy way to configure your Caddy web server. -# -# Unless the file starts with a global options block, the first -# uncommented line is always the address of your site. -# -# To use your own domain name (with automatic HTTPS), first make -# sure your domain's A/AAAA DNS records are properly pointed to -# this machine's public IP, then replace ":80" below with your -# domain name. - -{ -# BEGIN ANSIBLE MANAGED BLOCK - # Admin API for metrics - listen on all interfaces for Prometheus scraping - admin :2019 - - servers { - trusted_proxies static 127.0.0.1 10.0.0.0/8 172.16.0.0/12 192.168.0.0/16 - metrics - } - - # Layer4 TCP proxy — Gitea SSH on port 2222 - layer4 { - :2222 { - route { - proxy gitea.srv.renner.uno:22 - } - } - } - -{% if crowdsec_enabled | default(true) and crowdsec_caddy_api_key is defined %} - # CrowdSec configuration - crowdsec { - api_url {{ crowdsec_lapi_url }} - api_key {{ crowdsec_caddy_api_key }} - ticker_interval {{ crowdsec_ticker_interval }} -{% if crowdsec_appsec_url is defined %} - appsec_url {{ crowdsec_appsec_url }} -{% endif %} - } - - # Log configuration for CrowdSec analysis - log { - output file /var/log/caddy/access.log { - roll_size 30MiB - roll_keep 5 - } - } -{% endif %} -# END ANSIBLE MANAGED BLOCK - email tls@renner.uno -# acme_dns cloudflare { -# zone_token 8NcEXu8fSEv-gRVnh1kdL_sCh0cRCcUnRLgPGTA0 -# api_token 9Oncv6wU-R85y0FmgPnMQHhrANLiO2osRMNdRNeJ -# } - - default_sni caddy.renner.uno - -} - -# BEGIN ANSIBLE MANAGED BLOCK: Error Page Snippet -(custom_error) { - handle_errors { - rewrite * /custom_error.html - file_server { - root /var/www/html/errors - } - } -} -# END ANSIBLE MANAGED BLOCK: Error Page Snippet - -{% if crowdsec_enabled | default(true) and crowdsec_caddy_api_key is defined %} -# BEGIN ANSIBLE MANAGED BLOCK: CrowdSec Snippet -(crowdsec_protect) { - crowdsec -{% if crowdsec_appsec_url is defined %} - appsec -{% endif %} -} -# END ANSIBLE MANAGED BLOCK: CrowdSec Snippet -{% endif %} - -(cloudflare) { - tls { - dns cloudflare { - zone_token 8NcEXu8fSEv-gRVnh1kdL_sCh0cRCcUnRLgPGTA0 - api_token 9Oncv6wU-R85y0FmgPnMQHhrANLiO2osRMNdRNeJ - } - resolvers 1.1.1.1 - } - -} - -(loadbalancer) { - lb_policy ip_hash - #fail_duration 30s - health_interval 15s - health_timeout 10s -} - - -caddy.nerdcave.eu { - import cloudflare - # Set this path to your site's directory. - root * /usr/share/caddy - - # Enable the static file server. - file_server - - # Another common task is to set up a reverse proxy: - # reverse_proxy localhost:8080 - - # Or serve a PHP site through php-fpm: - # php_fastcgi localhost:9000 -} - -caddy.renner.uno { - import cloudflare - root * /var/www/caddy-dashboard - file_server -} - -# Internal Dashboard Access (Port 80) for Monitoring Script -:80 { - root * /var/www/caddy-dashboard - file_server -} - -pve.nerdcave.eu { - import cloudflare -# import cloudflare - redir https://pve.renner.uno{uri} permanent -} - -pve.renner.uno { - import cloudflare -# import cloudflare - #import authelia - reverse_proxy { - to https://jupiter.renner.uno:8006 https://neptune.renner.uno:8006 https://saturn.renner.uno:8006 - health_port 8006 - #dynamic srv srv+https://_pveweb._tcp.nerdcave.eu { - # resolvers 10.2.40.12 10.2.0.11 - #} - import loadbalancer - } -} - -pbs.renner.uno { - import cloudflare - - reverse_proxy https://pbs.obg.renner.uno:8007 - } - -home.nerdcave.eu { - import cloudflare - log -# import cloudflare - redir https://home.renner.uno{uri} permanent -} - -home.renner.uno { - import cloudflare - reverse_proxy http://10.2.30.11:8123 { - flush_interval -1 - transport http { - keepalive 30s - keepalive_idle_conns 10 - } - } -} - -home.internal.renner.uno { - import cloudflare - reverse_proxy http://10.2.30.11:8123 { - flush_interval -1 - transport http { - keepalive 30s - keepalive_idle_conns 10 - } - } -} - -photos.nerdcave.eu { - import cloudflare - redir https://photos.renner.uno{uri} permanent -} - -photos.renner.uno { - import cloudflare -# import cloudflare - redir /mo/sharing/mGwOwC3ZW https://immich.renner.uno/share/i8gRTJ6Kj0IczirRP6y-BvKcq3zXUnSIEYdtWjlcRJvDaHjStUgBmdKYeU58xZkzTWE - redir /mo/sharing/4qry70DZG https://immich.renner.uno/share/i8gRTJ6Kj0IczirRP6y-BvKcq3zXUnSIEYdtWjlcRJvDaHjStUgBmdKYeU58xZkzTWE - #reverse_proxy https://immich.renner.uno - redir https://immich.renner.uno{uri} permanent -} - -file.nerdcave.eu { - import cloudflare -# import cloudflare - reverse_proxy https://nas.renner.uno:7001 -} -file.renner.uno { - import cloudflare -# import cloudflare - reverse_proxy https://nas.renner.uno:7001 -} - -docs.renner.uno { - import cloudflare - - @local { - remote_ip 10.3.0.0/16 - #vars {tls_client_subject} "" - } - #@trusted { - # vars {tls_client_issuer} "CN=Renner Home Intermediate CA,O=Renner Home" - #} - #import cloudflare - handle @local { - reverse_proxy http://paperless.srv.renner.uno:8000 - } - handle { - route { - # always forward outpost path to actual outpost - reverse_proxy /outpost.goauthentik.io/* http://authentik-app-1.srv.renner.uno:9000 http://authentik-app-2.srv.renner.uno:9000 { - header_up Host {http.reverse_proxy.upstream.hostport} - lb_policy round_robin - } - - # forward authentication to outpost - forward_auth http://authentik-app-1.srv.renner.uno:9000 http://authentik-app-2.srv.renner.uno:9000 { - uri /outpost.goauthentik.io/auth/caddy - - # capitalization of the headers is important, otherwise they will be empty - copy_headers X-Authentik-Username X-Authentik-Groups X-Authentik-Email X-Authentik-Name X-Authentik-Uid X-Authentik-Jwt X-Authentik-Meta-Jwks X-Authentik-Meta-Outpost X-Authentik-Meta-Provider X-Authentik-Meta-App X-Authentik-Meta-Version - - # trust both authentik HA nodes - trusted_proxies 10.2.40.0/24 - } - reverse_proxy http://paperless.srv.renner.uno:8000 - } - } -} - - -influx.renner.uno { - import cloudflare -# import cloudflare - reverse_proxy http://influxdb.srv.renner.uno:8086 -} - -dash.renner.uno { - import cloudflare -# import cloudflare - reverse_proxy http://grafana.srv.renner.uno:3000 -} - - -media.renner.uno { - import cloudflare -# import cloudflare - #reverse_proxy http://10.2.40.15:8096 - reverse_proxy http://nas.renner.uno:8096 -} - -git.renner.uno { - import cloudflare -# import cloudflare - reverse_proxy http://gitea.srv.renner.uno:3000 -} - -auth.renner.uno { - import cloudflare - reverse_proxy http://authentik-app-1.srv.renner.uno:9000 http://authentik-app-2.srv.renner.uno:9000 { - lb_policy round_robin - health_uri /-/health/live/ - health_interval 10s - } -} - -audiobooks.renner.uno { - import cloudflare - reverse_proxy http://audiobookshelf.srv.renner.uno:13378 - } - -wazuh.renner.uno { - import cloudflare - log { - level info - } - reverse_proxy https://wazuh.srv.renner.uno:443 { - transport http { - tls_insecure_skip_verify - } - } - } - - -# Refer to the Caddy docs for more information: -# https://caddyserver.com/docs/caddyfile -# BEGIN ANSIBLE MANAGED BLOCK homeassistant-dev -homeassistant-dev.renner.uno { - import cloudflare - import custom_error - reverse_proxy http://10.2.40.121:8123 -} -# END ANSIBLE MANAGED BLOCK homeassistant-dev - -# BEGIN ANSIBLE MANAGED BLOCK GITEA -gitea.renner.uno { - reverse_proxy gitea.srv.renner.uno:3000 - import cloudflare -} -# END ANSIBLE MANAGED BLOCK GITEA - -# BEGIN ANSIBLE MANAGED BLOCK IMMICH -immich.renner.uno { - import cloudflare - import custom_error - reverse_proxy http://immich.srv.renner.uno:2283 -} -# END ANSIBLE MANAGED BLOCK IMMICH - -ical-proxy.renner.uno { - import cloudflare - reverse_proxy http://ical-proxy.srv.renner.uno:8080 -} - - -# BEGIN ANSIBLE MANAGED BLOCK MUSIC-ASSISTANT -music.renner.uno { - import cloudflare - import custom_error - reverse_proxy http://music-assistant.iot.renner.uno:8095 -} -# END ANSIBLE MANAGED BLOCK MUSIC-ASSISTANT - -# BEGIN ANSIBLE MANAGED BLOCK WIKI -wiki.renner.uno { - import cloudflare - import custom_error - reverse_proxy http://wiki.srv.renner.uno:80 -} -# END ANSIBLE MANAGED BLOCK WIKI - - -# BEGIN ANSIBLE MANAGED BLOCK WOODPECKER -ci.renner.uno { - import cloudflare - import custom_error - reverse_proxy http://woodpecker.srv.renner.uno:8000 -} -# END ANSIBLE MANAGED BLOCK WOODPECKER - -# BEGIN ANSIBLE MANAGED BLOCK OWNCLOUD -owncloud.renner.uno { - import cloudflare - import custom_error - reverse_proxy http://owncloud.srv.renner.uno:9200 -} -# END ANSIBLE MANAGED BLOCK OWNCLOUD - -# BEGIN ANSIBLE MANAGED BLOCK ONLYOFFICE -office.renner.uno { - import cloudflare - import custom_error - reverse_proxy http://owncloud.srv.renner.uno:9980 { - header_down X-Frame-Options "ALLOWALL" - header_down -Content-Security-Policy - } -} -# END ANSIBLE MANAGED BLOCK ONLYOFFICE - -# BEGIN ANSIBLE MANAGED BLOCK GATUS -status.renner.uno { - import cloudflare - import custom_error - reverse_proxy http://gatus.srv.renner.uno:8080 -} -# END ANSIBLE MANAGED BLOCK GATUS - -# BEGIN ANSIBLE MANAGED BLOCK KASM -kasm.renner.uno { - import cloudflare - import custom_error - reverse_proxy https://kasm.srv.renner.uno:443 { - transport http { - tls_insecure_skip_verify - keepalive 30s - } - flush_interval -1 - } -} -# END ANSIBLE MANAGED BLOCK KASM - diff --git a/ansible/roles/caddy-ha/templates/caddy-dashboard.service.j2 b/ansible/roles/caddy-ha/templates/caddy-dashboard.service.j2 deleted file mode 100644 index 9b47dd15..00000000 --- a/ansible/roles/caddy-ha/templates/caddy-dashboard.service.j2 +++ /dev/null @@ -1,12 +0,0 @@ -[Unit] -Description=Generate Caddy HA Dashboard -After=network.target - -[Service] -Type=oneshot -ExecStart=/usr/bin/python3 /usr/local/bin/generate_dashboard.py -User=root -Group=root - -[Install] -WantedBy=multi-user.target diff --git a/ansible/roles/caddy-ha/templates/caddy-dashboard.timer.j2 b/ansible/roles/caddy-ha/templates/caddy-dashboard.timer.j2 deleted file mode 100644 index 95a25086..00000000 --- a/ansible/roles/caddy-ha/templates/caddy-dashboard.timer.j2 +++ /dev/null @@ -1,9 +0,0 @@ -[Unit] -Description=Run Caddy Dashboard Generator every 5 seconds - -[Timer] -OnBootSec=10s -OnUnitActiveSec=5s - -[Install] -WantedBy=timers.target diff --git a/ansible/roles/caddy-ha/templates/caddy.service.j2 b/ansible/roles/caddy-ha/templates/caddy.service.j2 deleted file mode 100644 index 1292e12d..00000000 --- a/ansible/roles/caddy-ha/templates/caddy.service.j2 +++ /dev/null @@ -1,35 +0,0 @@ -# caddy.service -# -# For using Caddy with a config file. -# -# Make sure the ExecStart and ExecReload commands are correct -# for your installation. -# -# See https://caddyserver.com/docs/install for instructions. -# -# WARNING: This service does not use the --resume flag, so if you -# use the API to make changes, they will be overwritten by the -# Caddyfile next time the service is restarted. If you intend to -# use Caddy's API to configure it, add the --resume flag to the -# `caddy run` command or use the caddy-api.service file instead. - -[Unit] -Description=Caddy -Documentation=https://caddyserver.com/docs/ -After=network.target network-online.target -Requires=network-online.target - -[Service] -Type=notify -User=caddy -Group=caddy -ExecStart=/usr/bin/caddy run --environ --config /etc/caddy/Caddyfile -ExecReload=/usr/bin/caddy reload --config /etc/caddy/Caddyfile --force -TimeoutStopSec=5s -LimitNOFILE=1048576 -PrivateTmp=true -ProtectSystem=full -AmbientCapabilities=CAP_NET_ADMIN CAP_NET_BIND_SERVICE - -[Install] -WantedBy=multi-user.target diff --git a/ansible/roles/caddy-ha/templates/generate_dashboard.py.j2 b/ansible/roles/caddy-ha/templates/generate_dashboard.py.j2 deleted file mode 100644 index 0fb5c2b0..00000000 --- a/ansible/roles/caddy-ha/templates/generate_dashboard.py.j2 +++ /dev/null @@ -1,199 +0,0 @@ -#!/usr/bin/env python3 -import subprocess -import socket -import datetime -import os -import sys -import ssl -import urllib.request - -# Constants -HOSTNAME = socket.gethostname() -VIP_IP = "10.2.45.100" # Adjust if your VIP is different -OUTPUT_FILE = "/var/www/caddy-dashboard/index.html" -NODES = [ - {% for host in groups['proxmox_caddy_ha'] %} - { - "name": "{{ hostvars[host].ansible_host }} ({{ host }})", - "host": "{{ hostvars[host].ansible_host }}" - }, - {% endfor %} -] - -{% raw %} -def check_remote_node_status(host): - """Checks the status of a remote node by fetching its dashboard.""" - try: - # Use HTTP on port 80 to bypass TLS/SNI issues - url = f"http://{host}" - req = urllib.request.Request(url) - - with urllib.request.urlopen(req, timeout=1) as response: - html = response.read().decode('utf-8') - if "MASTER (Active Node)" in html: - return "Online (MASTER)" - elif "BACKUP (Standby Node)" in html: - return "Online (BACKUP)" - else: - return "Online" - except Exception: - return "Offline" - -def get_service_status(service): - """Checks if a systemd service is active.""" - try: - subprocess.check_call(["systemctl", "is-active", "--quiet", service]) - return "Active" - except subprocess.CalledProcessError: - return "Inactive" - except FileNotFoundError: - return "Not Installed" - -def get_keepalived_role(): - """Checks if the VIP is present on any interface.""" - try: - output = subprocess.check_output(["ip", "addr", "show"]).decode() - if VIP_IP in output: - return "MASTER (Active Node)" - return "BACKUP (Standby Node)" - except Exception: - return "Unknown" - -def generate_html(): - caddy_status = get_service_status("caddy") - keepalived_status = get_service_status("keepalived") - node_role = get_keepalived_role() - last_updated = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S") - - # Generate Node Status List - nodes_html = "" - for node in NODES: - status = check_remote_node_status(node["host"]) - - status_class = "inactive" - if "Online" in status: - status_class = "active" - if "MASTER" in status: - status_class = "master" - elif "BACKUP" in status: - status_class = "backup" - - nodes_html += f""" -
- {node['name']} - {status} -
- """ - - # CSS classes based on status - role_class = "master" if "MASTER" in node_role else "backup" - caddy_class = "active" if caddy_status == "Active" else "inactive" - keepalived_class = "active" if keepalived_status == "Active" else "inactive" - - html_content = f""" - - - - - Caddy HA Status - {HOSTNAME} - - - - -
-

{HOSTNAME}

- -
- HA Role - {node_role} -
- -
- Caddy Service - {caddy_status} -
- -
- Keepalived - {keepalived_status} -
- -

Cluster Nodes

- {nodes_html} - - -
- - -""" - - # Ensure directory exists - os.makedirs(os.path.dirname(OUTPUT_FILE), exist_ok=True) - - with open(OUTPUT_FILE, "w") as f: - f.write(html_content) - -if __name__ == "__main__": - try: - generate_html() - except Exception as e: - print(f"Error generating dashboard: {e}", file=sys.stderr) - sys.exit(1) -{% endraw %} diff --git a/ansible/roles/caddy-ha/templates/keepalived.conf.j2 b/ansible/roles/caddy-ha/templates/keepalived.conf.j2 deleted file mode 100644 index acbd60e4..00000000 --- a/ansible/roles/caddy-ha/templates/keepalived.conf.j2 +++ /dev/null @@ -1,23 +0,0 @@ -vrrp_script chk_caddy { - script "/usr/bin/pgrep caddy" - interval 2 - weight -50 -} - -vrrp_instance VI_1 { - state {{ keepalived_state }} - interface {{ ansible_facts["default_ipv4"].interface | default('ens18') }} - virtual_router_id 51 - priority {{ keepalived_priority }} - advert_int 1 - authentication { - auth_type PASS - auth_pass {{ keepalived_auth_pass | default('42secret') }} - } - virtual_ipaddress { - {{ keepalived_vip }} - } - track_script { - chk_caddy - } -} diff --git a/ansible/roles/cloudflared-lxc/defaults/main.yml b/ansible/roles/cloudflared-lxc/defaults/main.yml deleted file mode 100644 index 5dd1141b..00000000 --- a/ansible/roles/cloudflared-lxc/defaults/main.yml +++ /dev/null @@ -1,2 +0,0 @@ ---- -cloudflared_token: "{{ vault_cloudflared_token }}" diff --git a/ansible/roles/cloudflared-lxc/tasks/main.yml b/ansible/roles/cloudflared-lxc/tasks/main.yml deleted file mode 100644 index a4ff64b6..00000000 --- a/ansible/roles/cloudflared-lxc/tasks/main.yml +++ /dev/null @@ -1,43 +0,0 @@ ---- -- name: Install dependencies - apt: - name: - - curl - - lsb-release - state: present - update_cache: yes - -- name: Create keyring directory - file: - path: /usr/share/keyrings - state: directory - mode: '0755' - -- name: Add Cloudflare GPG key - get_url: - url: https://pkg.cloudflare.com/cloudflare-main.gpg - dest: /usr/share/keyrings/cloudflare-main.gpg - mode: '0644' - -- name: Add Cloudflare repository - apt_repository: - repo: "deb [signed-by=/usr/share/keyrings/cloudflare-main.gpg] https://pkg.cloudflare.com/cloudflared {{ ansible_distribution_release | default('bookworm') }} main" - filename: cloudflare - state: present - -- name: Install cloudflared - apt: - name: cloudflared - state: present - update_cache: yes - -- name: Install cloudflared service - command: cloudflared service install {{ cloudflared_token }} - args: - creates: /etc/systemd/system/cloudflared.service - -- name: Enable and start cloudflared service - service: - name: cloudflared - state: started - enabled: yes diff --git a/ansible/roles/netbird-gateway/defaults/main.yml b/ansible/roles/netbird-gateway/defaults/main.yml deleted file mode 100644 index 37311b0e..00000000 --- a/ansible/roles/netbird-gateway/defaults/main.yml +++ /dev/null @@ -1,12 +0,0 @@ ---- -# NetBird Gateway Configuration -netbird_server_url: "https://netbird.renner.uno" -netbird_setup_key: "" # Set in vault: netbird_gateway_setup_key - -# Routes to advertise to the NetBird network -netbird_routes: - - "10.2.40.0/24" # v40srv - services network - - "10.2.45.0/24" # v42ing - ingress network - -# DNS settings for split DNS -netbird_dns_server: "10.2.40.53" # Proxmox DNS VIP diff --git a/ansible/roles/netbird-gateway/handlers/main.yml b/ansible/roles/netbird-gateway/handlers/main.yml deleted file mode 100644 index 9bd1e30a..00000000 --- a/ansible/roles/netbird-gateway/handlers/main.yml +++ /dev/null @@ -1,12 +0,0 @@ ---- -- name: Save iptables rules - ansible.builtin.shell: | - iptables-save > /etc/iptables/rules.v4 || true - ip6tables-save > /etc/iptables/rules.v6 || true - args: - creates: /etc/iptables/rules.v4 - -- name: Restart netbird - ansible.builtin.service: - name: netbird - state: restarted diff --git a/ansible/roles/netbird-gateway/tasks/main.yml b/ansible/roles/netbird-gateway/tasks/main.yml deleted file mode 100644 index 0c59e457..00000000 --- a/ansible/roles/netbird-gateway/tasks/main.yml +++ /dev/null @@ -1,82 +0,0 @@ ---- -- name: Install required packages - ansible.builtin.apt: - name: - - curl - - ca-certificates - - iptables - - gnupg - state: present - update_cache: true - -- name: Enable IP forwarding - ansible.posix.sysctl: - name: "{{ item }}" - value: "1" - sysctl_set: true - state: present - reload: true - loop: - - net.ipv4.ip_forward - - net.ipv6.conf.all.forwarding - -- name: Add NetBird repository key - ansible.builtin.shell: - cmd: | - curl -sSL https://pkgs.netbird.io/debian/public.key | gpg --dearmor -o /usr/share/keyrings/netbird-archive-keyring.gpg - creates: /usr/share/keyrings/netbird-archive-keyring.gpg - -- name: Add NetBird repository - ansible.builtin.apt_repository: - repo: "deb [signed-by=/usr/share/keyrings/netbird-archive-keyring.gpg] https://pkgs.netbird.io/debian stable main" - state: present - filename: netbird - -- name: Install NetBird client - ansible.builtin.apt: - name: netbird - state: present - update_cache: true - -- name: Check if NetBird is already connected - ansible.builtin.command: netbird status - register: netbird_status - changed_when: false - failed_when: false - -- name: Connect NetBird to management server - ansible.builtin.command: > - netbird up - --management-url {{ netbird_server_url }} - --setup-key {{ netbird_gateway_setup_key }} - when: "'Connected' not in netbird_status.stdout" - register: netbird_connect - changed_when: netbird_connect.rc == 0 - -- name: Enable NetBird service - ansible.builtin.service: - name: netbird - state: started - enabled: true - -- name: Configure iptables for NAT (masquerade) - ansible.builtin.iptables: - table: nat - chain: POSTROUTING - source: "100.64.0.0/10" - jump: MASQUERADE - comment: "NetBird NAT for VPN clients" - notify: Save iptables rules - -- name: Display gateway status - ansible.builtin.debug: - msg: | - NetBird gateway configured. - Server: {{ netbird_server_url }} - Routes advertised: {{ netbird_routes | join(', ') }} - - To add routes in NetBird management: - 1. Go to {{ netbird_server_url }} - 2. Navigate to Network Routes - 3. Add routes for: {{ netbird_routes | join(', ') }} - 4. Select this peer as the routing peer diff --git a/ansible/roles/netbird-server/tasks/main.yml b/ansible/roles/netbird-server/tasks/main.yml index 56123c43..4e0277e6 100644 --- a/ansible/roles/netbird-server/tasks/main.yml +++ b/ansible/roles/netbird-server/tasks/main.yml @@ -92,4 +92,5 @@ To create a setup key: 1. Log into https://{{ netbird_server_host }} with your Authentik credentials 2. Go to Setup Keys > Create Setup Key - 3. Use the key with the netbird-gateway role + 3. Use the key with the netbird-ingress DaemonSet + (kubernetes/infrastructure/netbird-ingress) or the netbird-proxy role diff --git a/ci/images/caddy-builder/Containerfile b/ci/images/caddy-builder/Containerfile deleted file mode 100644 index 3e39f98e..00000000 --- a/ci/images/caddy-builder/Containerfile +++ /dev/null @@ -1,5 +0,0 @@ -FROM golang:1.26 - -ARG XCADDY_VERSION=v0.4.5 - -RUN go install github.com/caddyserver/xcaddy/cmd/xcaddy@${XCADDY_VERSION} diff --git a/opentofu/backup_jobs.tf b/opentofu/backup_jobs.tf index f1d8de0c..0fe328fd 100644 --- a/opentofu/backup_jobs.tf +++ b/opentofu/backup_jobs.tf @@ -28,7 +28,6 @@ locals { # LXCs [ "114", # gitea - "117", # cloudflared "118", # proxmox-dns-1 (jupiter) "121", # ical-proxy "130", # authentik-app-1 @@ -42,10 +41,6 @@ locals { "227", # prometheus "229", # music-assistant "231", # woodpecker - "232", # caddy-1 - "233", # caddy-2 - "234", # caddy-3 - "260", # netbird-gateway "318", # proxmox-dns-3 (neptune) ], # Woodpecker CI agents (VMIDs 910..) diff --git a/opentofu/cloudflare_tunnel.tf.disabled b/opentofu/cloudflare_tunnel.tf.disabled deleted file mode 100644 index edaeadb5..00000000 --- a/opentofu/cloudflare_tunnel.tf.disabled +++ /dev/null @@ -1,43 +0,0 @@ -# Example: Manage a Cloudflare Tunnel -# 1. Create the Tunnel Resource -# resource "cloudflare_tunnel" "homelab_tunnel" { -# account_id = var.cloudflare_account_id -# name = "homelab-tunnel" -# secret = base64sha256(random_password.tunnel_secret.result) -# } -# -# 2. Generate a random secret for the tunnel -# resource "random_password" "tunnel_secret" { -# length = 32 -# special = false -# } -# -# 3. Configure the Tunnel (Ingress Rules) -# resource "cloudflare_tunnel_config" "homelab_config" { -# account_id = var.cloudflare_account_id -# tunnel_id = cloudflare_tunnel.homelab_tunnel.id -# -# config { -# ingress_rule { -# hostname = "gitea.renner.uno" -# service = "http://10.2.40.114:3000" -# origin_request { -# connect_timeout = "1m" -# } -# } -# -# # Catch-all rule (Required) -# ingress_rule { -# service = "http_status:404" -# } -# } -# } -# -# 4. Create DNS Records pointing to the Tunnel -# resource "cloudflare_record" "gitea" { -# zone_id = "your_zone_id_here" -# name = "gitea" -# value = "${cloudflare_tunnel.homelab_tunnel.id}.cfargotunnel.com" -# type = "CNAME" -# proxied = true -# } diff --git a/opentofu/ingress.tf b/opentofu/ingress.tf deleted file mode 100644 index 6b8a34a0..00000000 --- a/opentofu/ingress.tf +++ /dev/null @@ -1,106 +0,0 @@ -moved { - from = proxmox_lxc.caddy_srv_renner_uno - to = module.caddy_srv_renner_uno.proxmox_lxc.this -} - -module "caddy_srv_renner_uno" { - source = "./modules/proxmox_lxc" - - target_node = "neptune" - hostname = "caddy.dmz.renner.uno" - vmid = 232 - memory = 1024 - cores = 1 - - unprivileged = true - cpuunits = 1024 - tags = "caddy_ha,netbird_client" - - hagroup = "olymp" - hastate = "stopped" - - storage = "local-zfs" - disk_size = "8G" - network_bridge = "v42ing" - # ip_address = "10.2.45.101" - mac_address = "bc:24:11:de:8f:a4" - - ssh_public_keys = local.common_ssh_keys - - start = false - onboot = false - - # All Caddy ingress hostnames retired: - # pve/home/auth/dash dropped in the prior commit (5eb8ad5). - # file.renner.uno: now served by apps/file (ExternalName -> nas:7001). - # dir.renner.uno + caddy.renner.uno: stale (no live backend). - ingress_cnames = [] -} - -moved { - from = proxmox_lxc.caddy2_srv_renner_uno - to = module.caddy2_srv_renner_uno.proxmox_lxc.this -} - -module "caddy2_srv_renner_uno" { - source = "./modules/proxmox_lxc" - - target_node = "saturn" - hostname = "caddy2.dmz.renner.uno" - vmid = 233 - memory = 1024 - cores = 1 - - unprivileged = true - cpuunits = 1024 - tags = "caddy_ha,netbird_client" - - hagroup = "olymp" - hastate = "stopped" - - storage = "local-zfs" - disk_size = "8G" - network_bridge = "v42ing" - # ip_address = "10.2.45.103" - mac_address = "bc:24:11:85:43:9c" - - ssh_public_keys = local.common_ssh_keys - - start = false - onboot = false -} - -module "caddy3_srv_renner_uno" { - source = "./modules/proxmox_lxc" - - target_node = "jupiter" - hostname = "caddy3.dmz.renner.uno" - vmid = 234 - memory = 1024 - cores = 1 - - unprivileged = true - cpuunits = 1024 - tags = "caddy_ha,netbird_client" - - hagroup = "olymp" - hastate = "stopped" - - storage = "local-zfs" - disk_size = "8G" - network_bridge = "v42ing" - - ssh_public_keys = local.common_ssh_keys - - start = false - onboot = false -} - - -# caddy_vip A record removed: no CNAMEs point at ingress.renner.uno -# anymore (caddy module's ingress_cnames is empty + every per-LXC/VM -# ingress_cnames was emptied in the cleanup pass). Keepalived-managed -# 10.2.45.100 dies with the Caddy LXCs anyway. - -# Old authentik.renner.uno CNAME removed - migrated to auth.renner.uno with HA setup -# auth.renner.uno is handled via Caddy reverse proxy to authentik-app-1/2.srv.renner.uno diff --git a/opentofu/lxc_117_cloudflared_lxc.tf b/opentofu/lxc_117_cloudflared_lxc.tf deleted file mode 100644 index 7d4e3127..00000000 --- a/opentofu/lxc_117_cloudflared_lxc.tf +++ /dev/null @@ -1,34 +0,0 @@ -moved { - from = proxmox_lxc.lxc["117"] - to = proxmox_lxc.cloudflared_lxc -} - -module "cloudflared_lxc" { - source = "./modules/proxmox_lxc" - - target_node = "saturn" - hostname = "cloudflared.srv.renner.uno" - vmid = 117 - memory = 1024 - cores = 1 - - unprivileged = false - cpuunits = 1024 - - hagroup = "olymp" - hastate = "started" - - storage = "local-zfs" - disk_size = "4G" - network_bridge = "v42ing" - - start = false - onboot = false - - tags = "cloudflared" -} - -moved { - from = proxmox_lxc.cloudflared_lxc - to = module.cloudflared_lxc.proxmox_lxc.this -} diff --git a/opentofu/lxc_260_netbird_gateway.tf b/opentofu/lxc_260_netbird_gateway.tf deleted file mode 100644 index 3fc79785..00000000 --- a/opentofu/lxc_260_netbird_gateway.tf +++ /dev/null @@ -1,28 +0,0 @@ -module "netbird_gateway" { - source = "./modules/proxmox_lxc" - - target_node = "jupiter" - hostname = "netbird-gateway.srv.renner.uno" - vmid = 260 - memory = 512 - cores = 1 - - unprivileged = true - cpuunits = 1024 - - hagroup = "olymp" - hastate = "stopped" - - storage = "local-zfs" - disk_size = "4G" - network_bridge = "v40srv" - - start = false - onboot = false - - tags = "netbird,gateway" - - description = "NetBird gateway - routes VPN traffic to internal services" - - ansible_playbook = "plays/install-netbird-gateway.yml" -} diff --git a/opentofu/variables_cloudflare.tf.disabled b/opentofu/variables_cloudflare.tf.disabled deleted file mode 100644 index 8a896611..00000000 --- a/opentofu/variables_cloudflare.tf.disabled +++ /dev/null @@ -1,11 +0,0 @@ -variable "cloudflare_api_token" { - type = string - description = "The Cloudflare API Token" - sensitive = true -} - -variable "cloudflare_account_id" { - type = string - description = "The Cloudflare Account ID" - sensitive = true -} -- 2.54.0