#!/usr/bin/env bash # Fix JWT validation by using a simple HTTP service instead of FastCGI set -euo pipefail PROXMOX_HOST="${PROXMOX_HOST:-192.168.11.10}" VMID=2501 # Create a simple HTTP validation service ssh -o ConnectTimeout=5 -o StrictHostKeyChecking=no root@${PROXMOX_HOST} \ "pct exec $VMID -- bash" <<'FIX_EOF' # Create systemd service for JWT validation cat > /etc/systemd/system/jwt-validator.service <<'SERVICE_EOF' [Unit] Description=JWT Validation Service After=network.target [Service] Type=simple User=www-data ExecStart=/usr/bin/python3 /usr/local/bin/jwt-validator-http.py Restart=always RestartSec=5 [Install] WantedBy=multi-user.target SERVICE_EOF # Create HTTP-based JWT validator cat > /usr/local/bin/jwt-validator-http.py <<'PYTHON_EOF' #!/usr/bin/env python3 import http.server import socketserver import sys import os import hmac import hashlib import base64 import json import time import urllib.parse def base64url_decode(data): padding = 4 - len(data) % 4 if padding != 4: data += '=' * padding return base64.urlsafe_b64decode(data) def verify_jwt(token, secret): try: parts = token.split('.') if len(parts) != 3: return False, "Invalid token format" header_data = base64url_decode(parts[0]) payload_data = base64url_decode(parts[1]) signature = parts[2] message = f"{parts[0]}.{parts[1]}" expected_sig = hmac.new( secret.encode('utf-8'), message.encode('utf-8'), hashlib.sha256 ).digest() expected_sig_b64 = base64.urlsafe_b64encode(expected_sig).decode('utf-8').rstrip('=') if signature != expected_sig_b64: return False, "Invalid signature" payload = json.loads(payload_data) if 'exp' in payload: if time.time() > payload['exp']: return False, "Token expired" return True, payload except Exception as e: return False, str(e) class JWTValidatorHandler(http.server.BaseHTTPRequestHandler): def do_GET(self): auth_header = self.headers.get('Authorization', '') if not auth_header.startswith('Bearer '): self.send_response(401) self.send_header('Content-Type', 'application/json') self.end_headers() self.wfile.write(b'{"error": "Missing or invalid Authorization header"}') return token = auth_header[7:] # Read secret try: with open('/etc/nginx/jwt_secret', 'r') as f: secret = f.read().strip() except Exception as e: self.send_response(500) self.send_header('Content-Type', 'application/json') self.end_headers() self.wfile.write(f'{{"error": "Server error: {str(e)}"}}'.encode()) return valid, result = verify_jwt(token, secret) if valid: self.send_response(200) self.send_header('Content-Type', 'application/json') self.end_headers() self.wfile.write(b'{"valid": true}') else: self.send_response(401) self.send_header('Content-Type', 'application/json') self.end_headers() self.wfile.write(f'{{"error": "Invalid token", "reason": "{result}"}}'.encode()) def log_message(self, format, *args): # Suppress default logging pass if __name__ == '__main__': PORT = 8888 with socketserver.TCPServer(("127.0.0.1", PORT), JWTValidatorHandler) as httpd: httpd.serve_forever() PYTHON_EOF chmod +x /usr/local/bin/jwt-validator-http.py # Update nginx config to use HTTP instead of FastCGI sed -i 's|proxy_pass http://127.0.0.1:8888/validate;|proxy_pass http://127.0.0.1:8888/;|' /etc/nginx/sites-available/rpc-perm sed -i 's|fastcgi_pass unix:/var/run/fcgiwrap.socket;|# Removed FastCGI|' /etc/nginx/sites-available/rpc-perm sed -i 's|include fastcgi_params;|# Removed FastCGI|' /etc/nginx/sites-available/rpc-perm sed -i 's|fastcgi_param SCRIPT_FILENAME /usr/local/bin/jwt-validate.py;|# Removed FastCGI|' /etc/nginx/sites-available/rpc-perm sed -i 's|fastcgi_param HTTP_AUTHORIZATION \$http_authorization;|# Removed FastCGI|' /etc/nginx/sites-available/rpc-perm # Remove the internal FastCGI server block sed -i '/# Internal server for JWT validation/,/^}$/d' /etc/nginx/sites-available/rpc-perm # Enable and start the service systemctl daemon-reload systemctl enable jwt-validator.service systemctl restart jwt-validator.service # Test nginx config nginx -t # Reload nginx systemctl restart nginx FIX_EOF echo "JWT validation service updated!"