# Claude AI IP Filter for Tailscale Funnel
#
# Routes MCP transport to Claude AI IPs only, while allowing
# OAuth/auth endpoints from any IP (needed for user login flow).
#
# Pattern: homelab-argocd/atlantis/templates/nginx-webhook-config.yaml

worker_processes auto;
pid /var/run/nginx.pid;

events {
    worker_connections 1024;
}

http {
    include /etc/nginx/mime.types;
    default_type application/octet-stream;

    log_format main '$remote_addr - $remote_user [$time_local] "$request" '
                    '$status $body_bytes_sent "$http_referer" '
                    '"$http_user_agent" "$http_x_forwarded_for"';

    access_log /var/log/nginx/access.log main;
    error_log /var/log/nginx/error.log warn;

    sendfile on;
    keepalive_timeout 65;

    # Map Claude AI outbound IPs (must be at http level)
    geo $remote_addr $is_claude_ai {
        default 0;

        # Anthropic Claude AI Outbound IP Range
        # Source: https://docs.claude.com/en/api/ip-addresses
        # Last updated: 2026-03-29

        # IPv4 range
        160.79.104.0/21  1;  # Claude AI
    }

    server {
        listen 8080;
        server_name _;

        # Trust Tailscale proxy for real IP extraction
        real_ip_header X-Forwarded-For;
        set_real_ip_from 100.64.0.0/10;  # Tailscale CGNAT range
        set_real_ip_from 10.0.0.0/8;     # Docker internal networks
        set_real_ip_from 172.16.0.0/12;  # Docker bridge networks
        real_ip_recursive on;

        # OAuth/auth endpoints + favicon - allow ALL IPs (user browser needs access for login flow)
        location ~ ^/(oauth|\.well-known|app|favicon\.ico)(/|$) {
            proxy_pass http://mcp-login-flow:8004;
            proxy_set_header Host $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 https;
        }

        # MCP transport endpoint - Claude AI IPs ONLY
        # login-flow uses streamable-http transport (no /sse needed)
        location /mcp {
            if ($is_claude_ai = 0) {
                return 403 '{"error": "Access denied - IP not in Claude AI range", "source_ip": "$remote_addr"}\n';
            }

            proxy_pass http://mcp-login-flow:8004;
            proxy_set_header Host $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 https;

            # Streamable HTTP support (long-lived connections)
            proxy_buffering off;
            proxy_cache off;
            proxy_read_timeout 86400s;
            proxy_send_timeout 86400s;
            proxy_http_version 1.1;
            proxy_set_header Connection '';
        }

        # Default - deny everything else
        location / {
            return 404 '{"error": "Not found"}\n';
        }
    }
}
