diff --git a/docs/homelab_topology.md b/docs/homelab_topology.md index 5851aaa..8381b9c 100644 --- a/docs/homelab_topology.md +++ b/docs/homelab_topology.md @@ -133,6 +133,7 @@ This table records how subdomains on `*.snarfnet.net` route to internal IP addre | `mesh.snarfnet.net` | HTTPS | `140.44.4.70` (Swarm VIP) | 8086 | MeshCentral Remote Manager | | `peanut.snarfnet.net` | HTTP | `140.44.4.70` (Swarm VIP) | 8050 | Peanut Dashboard | | `homebox.snarfnet.net` | HTTP | `140.44.4.70` (Swarm VIP) | 3100 | Homebox Inventory Tracker | +| `vault.snarfnet.net` | HTTP | `140.44.4.70` (Swarm VIP) | 8085 | VaultConfigurator 3D Modular Storage Planner | | `pihole.snarfnet.net` | HTTP | `140.44.4.70` (Swarm VIP) | 8041 | Legacy Swarm Pi-hole 1 | | `pihole2.snarfnet.net` | HTTP | `140.44.4.70` (Swarm VIP) | 8042 | Legacy Swarm Pi-hole 2 | | `shipyard.snarfnet.net` | HTTPS | `140.44.4.71` (Swarm 01) | 9443 | Portainer Server Console | diff --git a/scripts/create_npm_vault.py b/scripts/create_npm_vault.py new file mode 100644 index 0000000..6c7258b --- /dev/null +++ b/scripts/create_npm_vault.py @@ -0,0 +1,161 @@ +import paramiko +import json +import time + +client = paramiko.SSHClient() +client.set_missing_host_key_policy(paramiko.AutoAddPolicy()) +client.connect("140.44.4.71", username="root", key_filename=r"C:\Users\Snarf\.ssh\id_ed25519_snarfnet", timeout=10) + +adv_config = """# Buffer tuning for Authentik headers +proxy_buffers 8 16k; +proxy_buffer_size 32k; + +# Outpost verification route - plain HTTP to internal VIP +location /outpost.goauthentik.io { + auth_request off; + proxy_pass http://140.44.4.29:9000/outpost.goauthentik.io; + proxy_set_header Host authentik.snarfnet.net; + proxy_set_header X-Original-URL $scheme://$http_host$request_uri; + add_header Set-Cookie $auth_cookie; + auth_request_set $auth_cookie $upstream_http_set_cookie; + proxy_pass_request_body off; + proxy_set_header Content-Length ""; +} + +# Main reverse proxy protected by Forward Auth +location / { + proxy_pass http://140.44.4.70:8085; + proxy_set_header Host $http_host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + + # Websocket support + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection $http_connection; + + # Forward Authentication against Authentik + auth_request /outpost.goauthentik.io/auth/nginx; + error_page 401 = @goauthentik_proxy_signin; + auth_request_set $auth_cookie $upstream_http_set_cookie; + add_header Set-Cookie $auth_cookie; + + # Inject Authentik user headers + auth_request_set $authentik_username $upstream_http_x_authentik_username; + auth_request_set $authentik_groups $upstream_http_x_authentik_groups; + auth_request_set $authentik_email $upstream_http_x_authentik_email; + proxy_set_header X-authentik-username $authentik_username; + proxy_set_header X-authentik-groups $authentik_groups; + proxy_set_header X-authentik-email $authentik_email; +} + +# Redirect to Authentik when unauthenticated +location @goauthentik_proxy_signin { + internal; + auth_request off; + add_header Set-Cookie $auth_cookie; + return 302 https://authentik.snarfnet.net/outpost.goauthentik.io/start?rd=$scheme://$http_host$request_uri; +} +""" + +nginx_conf = """# ------------------------------------------------------------ +# vault.snarfnet.net +# ------------------------------------------------------------ + +map $scheme $hsts_header { + https "max-age=63072000;includeSubDomains; preload"; +} + +server { + set $forward_scheme http; + set $server "140.44.4.70"; + set $port 8085; + + listen 80; + listen [::]:80; + + listen 443 ssl; + listen [::]:443 ssl; + + server_name vault.snarfnet.net; + http2 off; + + # Let's Encrypt SSL + include conf.d/include/letsencrypt-acme-challenge.conf; + include conf.d/include/ssl-cache.conf; + include conf.d/include/ssl-ciphers.conf; + ssl_certificate /etc/letsencrypt/live/npm-1/fullchain.pem; + ssl_certificate_key /etc/letsencrypt/live/npm-1/privkey.pem; + + # Asset Caching + include conf.d/include/assets.conf; + + # Block Exploits + include conf.d/include/block-exploits.conf; + + # HSTS + add_header Strict-Transport-Security $hsts_header always; + + # Force SSL + set $trust_forwarded_proto "F"; + include conf.d/include/force-ssl.conf; + + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection $http_connection; + proxy_http_version 1.1; + + access_log /data/logs/proxy-host-56_access.log proxy; + error_log /data/logs/proxy-host-56_error.log warn; + +""" + adv_config + """ + # Custom + include /data/nginx/custom/server_proxy[.]conf; +} +""" + +# 1. Insert into sqlite +insert_sql = """ +import sqlite3 +import datetime + +conn = sqlite3.connect('/var/lib/docker/volumes/nginx-proxy-manager_nginx-data/_data/database.sqlite') +now = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S') + +# Delete if exists +conn.execute('DELETE FROM proxy_host WHERE id=56;') + +conn.execute(''' +INSERT INTO proxy_host ( + id, created_on, modified_on, owner_user_id, is_deleted, + domain_names, forward_host, forward_port, access_list_id, certificate_id, + ssl_forced, caching_enabled, block_exploits, advanced_config, meta, + allow_websocket_upgrade, http2_support, forward_scheme, enabled, locations, + hsts_enabled, hsts_subdomains, trust_forwarded_proto +) VALUES ( + 56, ?, ?, 1, 0, + ?, '140.44.4.70', 8085, 0, 1, + 1, 1, 1, ?, '{"letsencrypt_agree":false,"dns_challenge":false,"nginx_online":true,"nginx_err":null}', + 1, 0, 'http', 1, '[]', + 1, 1, 0 +) +''', (now, now, '["vault.snarfnet.net"]', '''""" + adv_config.replace("'", "\\'") + """''')) +conn.commit() +print("Inserted proxy_host 56 successfully") +""" + +cmd = f'python3 -c "{insert_sql}"' +stdin, stdout, stderr = client.exec_command(cmd) +print("DB update:", stdout.read().decode().strip(), stderr.read().decode().strip()) + +# 2. Write 56.conf +cmd = f"cat << 'EOF' > /var/lib/docker/volumes/nginx-proxy-manager_nginx-data/_data/nginx/proxy_host/56.conf\n{nginx_conf}\nEOF" +stdin, stdout, stderr = client.exec_command(cmd) +print("Wrote 56.conf:", stdout.read().decode().strip(), stderr.read().decode().strip()) + +# 3. Reload nginx +cmd = 'docker exec $(docker ps -q -f name=nginx-proxy-manager_app) nginx -s reload' +stdin, stdout, stderr = client.exec_command(cmd) +print("Nginx reload:", stdout.read().decode().strip(), stderr.read().decode().strip()) + +client.close() diff --git a/scripts/deploy_vaultconfigurator.py b/scripts/deploy_vaultconfigurator.py new file mode 100644 index 0000000..a5d5e81 --- /dev/null +++ b/scripts/deploy_vaultconfigurator.py @@ -0,0 +1,50 @@ +import paramiko + +COMPOSE = """version: '3.8' + +services: + app: + image: vaultconfigurator:latest + ports: + - target: 8080 + published: 8085 + protocol: tcp + mode: ingress + volumes: + - /mnt/nfs_shares/vaultconfigurator/data:/data:ro + - /mnt/nfs_shares/vaultconfigurator/cache:/cache + - /mnt/nfs_shares/vaultconfigurator/bins:/bins + deploy: + replicas: 1 + placement: + constraints: + - node.hostname == snshpd4lnxswm01 + restart_policy: + condition: any + delay: 5s + max_attempts: 3 + resources: + limits: + cpus: '1.0' + memory: 256M + reservations: + cpus: '0.05' + memory: 32M +""" + +client = paramiko.SSHClient() +client.set_missing_host_key_policy(paramiko.AutoAddPolicy()) +client.connect("140.44.4.71", username="root", key_filename=r"C:\Users\Snarf\.ssh\id_ed25519_snarfnet", timeout=10) + +# Write compose file +cmd = f"cat << 'EOF' > /opt/vaultconfigurator/docker-compose.yml\n{COMPOSE}\nEOF" +stdin, stdout, stderr = client.exec_command(cmd) +print("Write compose:", stdout.read().decode(), stderr.read().decode()) + +# Deploy stack +cmd = "docker stack deploy -c /opt/vaultconfigurator/docker-compose.yml vaultconfigurator" +stdin, stdout, stderr = client.exec_command(cmd) +print("Deploy stack:", stdout.read().decode(), stderr.read().decode()) + +client.close() + diff --git a/scripts/insert_vault_db.py b/scripts/insert_vault_db.py new file mode 100644 index 0000000..526b967 --- /dev/null +++ b/scripts/insert_vault_db.py @@ -0,0 +1,79 @@ +import sqlite3 +import datetime + +conn = sqlite3.connect('/var/lib/docker/volumes/nginx-proxy-manager_nginx-data/_data/database.sqlite') +now = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S') + +conn.execute('DELETE FROM proxy_host WHERE id=56;') + +adv = """# Buffer tuning for Authentik headers +proxy_buffers 8 16k; +proxy_buffer_size 32k; + +# Outpost verification route - plain HTTP to internal VIP +location /outpost.goauthentik.io { + auth_request off; + proxy_pass http://140.44.4.29:9000/outpost.goauthentik.io; + proxy_set_header Host authentik.snarfnet.net; + proxy_set_header X-Original-URL $scheme://$http_host$request_uri; + add_header Set-Cookie $auth_cookie; + auth_request_set $auth_cookie $upstream_http_set_cookie; + proxy_pass_request_body off; + proxy_set_header Content-Length ""; +} + +# Main reverse proxy protected by Forward Auth +location / { + proxy_pass http://140.44.4.70:8085; + proxy_set_header Host $http_host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + + # Websocket support + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection $http_connection; + + # Forward Authentication against Authentik + auth_request /outpost.goauthentik.io/auth/nginx; + error_page 401 = @goauthentik_proxy_signin; + auth_request_set $auth_cookie $upstream_http_set_cookie; + add_header Set-Cookie $auth_cookie; + + # Inject Authentik user headers + auth_request_set $authentik_username $upstream_http_x_authentik_username; + auth_request_set $authentik_groups $upstream_http_x_authentik_groups; + auth_request_set $authentik_email $upstream_http_x_authentik_email; + proxy_set_header X-authentik-username $authentik_username; + proxy_set_header X-authentik-groups $authentik_groups; + proxy_set_header X-authentik-email $authentik_email; +} + +# Redirect to Authentik when unauthenticated +location @goauthentik_proxy_signin { + internal; + auth_request off; + add_header Set-Cookie $auth_cookie; + return 302 https://authentik.snarfnet.net/outpost.goauthentik.io/start?rd=$scheme://$http_host$request_uri; +} +""" + +conn.execute(''' +INSERT INTO proxy_host ( + id, created_on, modified_on, owner_user_id, is_deleted, + domain_names, forward_host, forward_port, access_list_id, certificate_id, + ssl_forced, caching_enabled, block_exploits, advanced_config, meta, + allow_websocket_upgrade, http2_support, forward_scheme, enabled, locations, + hsts_enabled, hsts_subdomains, trust_forwarded_proto +) VALUES ( + 56, ?, ?, 1, 0, + ?, '140.44.4.70', 8085, 0, 1, + 1, 1, 1, ?, '{"letsencrypt_agree":false,"dns_challenge":false,"nginx_online":true,"nginx_err":null}', + 1, 0, 'http', 1, '[]', + 1, 1, 0 +) +''', (now, now, '["vault.snarfnet.net"]', adv)) +conn.commit() +print("Successfully inserted proxy_host 56") +conn.close() diff --git a/scripts/run_insert_vault.py b/scripts/run_insert_vault.py new file mode 100644 index 0000000..b720ac7 --- /dev/null +++ b/scripts/run_insert_vault.py @@ -0,0 +1,19 @@ +import paramiko + +client = paramiko.SSHClient() +client.set_missing_host_key_policy(paramiko.AutoAddPolicy()) +client.connect("140.44.4.71", username="root", key_filename=r"C:\Users\Snarf\.ssh\id_ed25519_snarfnet", timeout=10) + +with open(r"c:\Users\Snarf\OneDrive\Desktop\code\SnarfCode\scripts\insert_vault_db.py", "r") as f: + script_text = f.read() + +cmd = f"cat << 'EOF' > /tmp/insert_vault.py\n{script_text}\nEOF\npython3 /tmp/insert_vault.py" +stdin, stdout, stderr = client.exec_command(cmd) +print("DB script output:", stdout.read().decode().strip(), stderr.read().decode().strip()) + +# Reload both NPM containers +cmd = "for id in $(docker ps -q -f name=nginx-proxy-manager_app); do docker exec $id nginx -s reload; done" +stdin, stdout, stderr = client.exec_command(cmd) +print("Reload output:", stdout.read().decode().strip()) + +client.close()