import paramiko import sys sys.stdout.reconfigure(encoding='utf-8') key_path = r"C:\Users\Snarf\.ssh\id_ed25519_snarfnet" c = paramiko.SSHClient() c.set_missing_host_key_policy(paramiko.AutoAddPolicy()) c.connect("140.44.4.71", username="root", key_filename=key_path, timeout=10) print("1. Creating safe backups of database and config files...") backup_cmd = """ cp /var/lib/docker/volumes/nginx-proxy-manager_nginx-data/_data/database.sqlite /var/lib/docker/volumes/nginx-proxy-manager_nginx-data/_data/database.sqlite.bak.dns cp -r /var/lib/docker/volumes/nginx-proxy-manager_nginx-data/_data/nginx/proxy_host /var/lib/docker/volumes/nginx-proxy-manager_nginx-data/_data/nginx/proxy_host_bak_dns """ stdin, stdout, stderr = c.exec_command(backup_cmd) stdout.channel.recv_exit_status() print("Backups created successfully.") print("2. Updating database.sqlite...") py_script = """ import sqlite3, json, datetime conn = sqlite3.connect("/var/lib/docker/volumes/nginx-proxy-manager_nginx-data/_data/database.sqlite") cur = conn.cursor() now = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S") updates = [ (3, json.dumps(["pihole.snarfnet.net"]), "140.44.4.34", 443, "https"), (43, json.dumps(["pihole2.snarfnet.net"]), "140.44.4.62", 443, "https"), (51, json.dumps(["pihole3.snarfnet.net"]), "140.44.4.62", 443, "https"), (52, json.dumps(["dns1.snarfnet.net"]), "140.44.4.34", 443, "https"), (53, json.dumps(["dns2.snarfnet.net"]), "140.44.4.62", 443, "https"), ] for pid, dnames, host, port, scheme in updates: cur.execute('''UPDATE proxy_host SET domain_names = ?, forward_host = ?, forward_port = ?, forward_scheme = ?, certificate_id = 1, ssl_forced = 1, allow_websocket_upgrade = 1, http2_support = 1, hsts_enabled = 1, hsts_subdomains = 1, advanced_config = "", modified_on = ? WHERE id = ?''', (dnames, host, port, scheme, now, pid)) print(f"Updated proxy host {pid} -> {host}:{port} ({scheme})") cur.execute("SELECT id FROM proxy_host WHERE id = 54") if not cur.fetchone(): cur.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 ( 54, ?, ?, 1, 0, ?, "140.44.4.34", 443, 0, 1, 1, 0, 1, "", "{\\"nginx_online\\":true,\\"nginx_err\\":null}", 1, 1, "https", 1, "[]", 1, 1, 0 )''', (now, now, json.dumps(["technitium.snarfnet.net"]))) print("Inserted proxy host 54: technitium.snarfnet.net -> 140.44.4.34:443") else: cur.execute('''UPDATE proxy_host SET domain_names = ?, forward_host = "140.44.4.34", forward_port = 443, forward_scheme = "https", certificate_id = 1, ssl_forced = 1, allow_websocket_upgrade = 1, http2_support = 1, hsts_enabled = 1, hsts_subdomains = 1, modified_on = ? WHERE id = 54''', (json.dumps(["technitium.snarfnet.net"]), now)) print("Updated proxy host 54: technitium.snarfnet.net") cur.execute("SELECT id FROM proxy_host WHERE id = 55") if not cur.fetchone(): cur.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 ( 55, ?, ?, 1, 0, ?, "140.44.4.34", 443, 0, 1, 1, 0, 1, "", "{\\"nginx_online\\":true,\\"nginx_err\\":null}", 1, 1, "https", 1, "[]", 1, 1, 0 )''', (now, now, json.dumps(["pihole1.snarfnet.net"]))) print("Inserted proxy host 55: pihole1.snarfnet.net -> 140.44.4.34:443") else: cur.execute('''UPDATE proxy_host SET domain_names = ?, forward_host = "140.44.4.34", forward_port = 443, forward_scheme = "https", certificate_id = 1, ssl_forced = 1, allow_websocket_upgrade = 1, http2_support = 1, hsts_enabled = 1, hsts_subdomains = 1, modified_on = ? WHERE id = 55''', (json.dumps(["pihole1.snarfnet.net"]), now)) print("Updated proxy host 55: pihole1.snarfnet.net") conn.commit() conn.close() """ stdin, stdout, stderr = c.exec_command(f"python3 -c '{py_script}'") print(stdout.read().decode('utf-8')) err = stderr.read().decode('utf-8') if err: print("DB Update Err:", err) print("3. Generating Nginx configuration files in proxy_host/...") conf_template = """# ------------------------------------------------------------ # {domains} # ------------------------------------------------------------ map $scheme $hsts_header {{ https "max-age=63072000;includeSubDomains; preload"; }} server {{ set $forward_scheme {scheme}; set $server "{host}"; set $port {port}; listen 80; listen [::]:80; listen 443 ssl; listen [::]:443 ssl; server_name {domains}; http2 on; # 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 (ngx_http_headers_module is required) (63072000 seconds = 2 years) 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-{pid}_access.log proxy; error_log /data/logs/proxy-host-{pid}_error.log warn; location / {{ # HSTS (ngx_http_headers_module is required) (63072000 seconds = 2 years) add_header Strict-Transport-Security $hsts_header always; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection $http_connection; proxy_http_version 1.1; # Proxy! include conf.d/include/proxy.conf; }} # Custom include /data/nginx/custom/server_proxy[.]conf; }} """ configs = [ (3, "pihole.snarfnet.net", "140.44.4.34", 443, "https"), (43, "pihole2.snarfnet.net", "140.44.4.62", 443, "https"), (51, "pihole3.snarfnet.net", "140.44.4.62", 443, "https"), (52, "dns1.snarfnet.net", "140.44.4.34", 443, "https"), (53, "dns2.snarfnet.net", "140.44.4.62", 443, "https"), (54, "technitium.snarfnet.net", "140.44.4.34", 443, "https"), (55, "pihole1.snarfnet.net", "140.44.4.34", 443, "https"), ] for pid, dom, host, port, scheme in configs: cfg = conf_template.format(pid=pid, domains=dom, host=host, port=port, scheme=scheme) remote_path = f"/var/lib/docker/volumes/nginx-proxy-manager_nginx-data/_data/nginx/proxy_host/{pid}.conf" cmd_write = f"cat << 'EOF' > {remote_path}\n{cfg}\nEOF\n" stdin, stdout, stderr = c.exec_command(cmd_write) stdout.channel.recv_exit_status() print(f"Wrote {remote_path} ({dom} -> {host}:{port})") print("\n4. Testing Nginx configuration syntax inside container...") test_cmd = "docker exec $(docker ps -q -f name=nginx-proxy-manager_app | head -n1) nginx -t" stdin, stdout, stderr = c.exec_command(test_cmd) test_out = stdout.read().decode('utf-8') test_err = stderr.read().decode('utf-8') print("nginx -t stdout:", test_out) print("nginx -t stderr:", test_err) if "successful" in test_err or "successful" in test_out: print("\n5. Reloading Nginx across Swarm service...") reload_cmd = "docker service update --force nginx-proxy-manager_app" stdin, stdout, stderr = c.exec_command(reload_cmd) print(stdout.read().decode('utf-8')) print("NPM service reloaded successfully!") else: print("\nWARNING: Nginx test failed! Please check configuration.") c.close()