gabriel / musehub public
nginx-cf.conf
141 lines 5.9 KB
Raw
sha256:9590cee1e0ccd6c76528f005b95d634d80f5019f0dcb7c371e149adc31d1fb65 refactor: enforce gRPC framing on all MWP wire traffic Sonnet 4.6 minor ⚠ breaking 153 days ago
1 # /etc/nginx/sites-available/musehub
2 #
3 # Cloudflare Origin Certificate configuration.
4 # Cloudflare terminates SSL at the edge. This nginx instance accepts HTTPS
5 # connections using a Cloudflare Origin Certificate.
6 #
7 # SSL mode in Cloudflare dashboard MUST be set to "Full (Strict)".
8 # No Certbot or Let's Encrypt required — the Origin Certificate is valid for 15 years.
9 #
10 # IP restriction is enforced at the EC2 security group level (inbound port 443
11 # restricted to Cloudflare IP ranges). Do not duplicate that logic here —
12 # the real_ip module replaces $remote_addr with the true client IP before
13 # allow/deny runs, which would incorrectly block legitimate Cloudflare traffic.
14 #
15 # To generate the Origin Certificate:
16 # Cloudflare Dashboard → <domain> → SSL/TLS → Origin Server → Create Certificate
17 # Choose "Generate private key and CSR with Cloudflare" → RSA (2048) → 15 years
18 # Save certificate → /etc/ssl/cloudflare/origin.pem
19 # Save private key → /etc/ssl/cloudflare/origin.key
20 # chmod 640 /etc/ssl/cloudflare/origin.key
21
22 # Restore the real client IP from the Cloudflare connecting-IP header.
23 # Without this, every request appears to come from a Cloudflare edge node.
24 # Cloudflare IP ranges: https://www.cloudflare.com/ips/
25 real_ip_header CF-Connecting-IP;
26 real_ip_recursive on;
27
28 # Cloudflare IPv4 ranges
29 set_real_ip_from 173.245.48.0/20;
30 set_real_ip_from 103.21.244.0/22;
31 set_real_ip_from 103.22.200.0/22;
32 set_real_ip_from 103.31.4.0/22;
33 set_real_ip_from 141.101.64.0/18;
34 set_real_ip_from 108.162.192.0/18;
35 set_real_ip_from 190.93.240.0/20;
36 set_real_ip_from 188.114.96.0/20;
37 set_real_ip_from 197.234.240.0/22;
38 set_real_ip_from 198.41.128.0/17;
39 set_real_ip_from 162.158.0.0/15;
40 set_real_ip_from 104.16.0.0/13;
41 set_real_ip_from 104.24.0.0/14;
42 set_real_ip_from 172.64.0.0/13;
43 set_real_ip_from 131.0.72.0/22;
44
45 # Cloudflare IPv6 ranges
46 set_real_ip_from 2400:cb00::/32;
47 set_real_ip_from 2606:4700::/32;
48 set_real_ip_from 2803:f800::/32;
49 set_real_ip_from 2405:b500::/32;
50 set_real_ip_from 2405:8100::/32;
51 set_real_ip_from 2a06:98c0::/29;
52 set_real_ip_from 2c0f:f248::/32;
53
54 # Blue-green upstream: deploy.sh rewrites /etc/nginx/musehub-active-port
55 # and runs `nginx -s reload` to switch slots atomically.
56 upstream musehub_backend {
57 include /etc/nginx/musehub-active-port;
58 }
59
60 # Redirect all plain-HTTP traffic to HTTPS — 301 (permanent, cacheable).
61 # In prod, Cloudflare enforces HTTPS at the edge, so this only fires for
62 # traffic that bypasses Cloudflare (e.g., direct-to-origin access during
63 # ops or monitoring). Belt-and-suspenders.
64 server {
65 listen 80;
66 listen [::]:80;
67 server_name DOMAIN_PLACEHOLDER;
68 return 301 https://$host$request_uri;
69 }
70
71 server {
72 listen 443 ssl http2;
73 listen [::]:443 ssl http2;
74 server_name DOMAIN_PLACEHOLDER;
75
76 ssl_certificate /etc/ssl/cloudflare/origin.pem;
77 ssl_certificate_key /etc/ssl/cloudflare/origin.key;
78
79 # Enforce TLS 1.2 minimum — disable TLS 1.0 and TLS 1.1 (both deprecated).
80 # TLS 1.3 is preferred; 1.2 retained for compatibility with older clients.
81 ssl_protocols TLSv1.2 TLSv1.3;
82 ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256;
83 ssl_prefer_server_ciphers off; # TLS 1.3 ignores this; keep off for 1.2 forward secrecy
84
85 client_max_body_size 500m;
86
87 # Push stream — full HTTP/2 path end-to-end.
88 # CF gRPC proxy → nginx grpc_pass (H2 h2c) → hypercorn (H2 h2c, ASGI).
89 # Content-Type: application/grpc+muse triggers CF gRPC proxy (full-duplex H2).
90 location ~ ^/[^/]+/[^/]+/push/stream$ {
91 grpc_pass grpc://musehub_backend;
92 grpc_set_header Host $host;
93 grpc_set_header X-Real-IP $remote_addr;
94 grpc_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
95 grpc_set_header X-Forwarded-Proto $scheme;
96 grpc_read_timeout 300s;
97 grpc_send_timeout 300s;
98 grpc_buffer_size 64k;
99 }
100
101 # Non-streaming push endpoints — standard HTTP/1.1 proxy.
102 # /push/objects is Phase 1 (presigned URL requests, small JSON).
103 # /push (no suffix) is the legacy Phase 2 path.
104 location ~ ^/[^/]+/[^/]+/push(/objects)?$ {
105 proxy_pass http://musehub_backend;
106 proxy_http_version 1.1;
107 proxy_set_header Host $host;
108 proxy_set_header X-Real-IP $remote_addr;
109 proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
110 proxy_set_header X-Forwarded-Proto $scheme;
111 proxy_read_timeout 300s;
112 proxy_request_buffering off;
113 proxy_buffering off;
114 }
115
116 # Fetch endpoints — standard HTTP/1.1 proxy.
117 # /fetch is Phase 1 (commits + snapshots metadata).
118 # /fetch/objects is Phase 2 (raw object bytes for checkout).
119 location ~ ^/[^/]+/[^/]+/fetch(/objects)?$ {
120 proxy_pass http://musehub_backend;
121 proxy_http_version 1.1;
122 proxy_set_header Host $host;
123 proxy_set_header X-Real-IP $remote_addr;
124 proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
125 proxy_set_header X-Forwarded-Proto $scheme;
126 proxy_read_timeout 300s;
127 }
128
129 # Proxy all other traffic to the MuseHub hypercorn container
130 location / {
131 proxy_pass http://musehub_backend;
132 proxy_http_version 1.1;
133 proxy_set_header Upgrade $http_upgrade;
134 proxy_set_header Connection "upgrade";
135 proxy_set_header Host $host;
136 proxy_set_header X-Real-IP $remote_addr;
137 proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
138 proxy_set_header X-Forwarded-Proto $scheme;
139 proxy_read_timeout 60s;
140 }
141 }
File History 1 commit
sha256:9590cee1e0ccd6c76528f005b95d634d80f5019f0dcb7c371e149adc31d1fb65 refactor: enforce gRPC framing on all MWP wire traffic Sonnet 4.6 minor 153 days ago