feat: add HTTP port 80 support with Cloudflare-only firewall rules

This commit is contained in:
NetworkCats
2026-03-08 05:52:42 +08:00
parent 801ed1f45e
commit 5565f4a0a5
4 changed files with 57 additions and 3 deletions
@@ -3,6 +3,7 @@ services:
image: haproxy:3.3.4-alpine3.23
container_name: rustyip-haproxy
ports:
- "80:80"
- "443:443"
volumes:
- ./haproxy/haproxy.cfg:/usr/local/etc/haproxy/haproxy.cfg:ro
@@ -26,14 +26,14 @@ defaults
timeout http-keep-alive 60s
timeout queue 30s
frontend https_in
bind *:443 ssl crt /etc/haproxy/certs/origin.pem alpn h2,http/1.1 strict-sni
frontend http_in
bind *:80
# Gzip compression
compression algo gzip
compression type text/html text/plain application/json text/css
# Access to port 443 is already restricted to Cloudflare IPs at the firewall level
# Access to port 80 is restricted to Cloudflare IPs at the firewall level
# All requests reaching HAProxy come from Cloudflare; no additional ACL validation is required.
# HAProxy's own forwardfor is not enabled; real IP is set explicitly below via CF-Connecting-IP
@@ -62,6 +62,40 @@ frontend https_in
default_backend app
frontend https_in
bind *:443 ssl crt /etc/haproxy/certs/origin.pem alpn h2,http/1.1 strict-sni
# Gzip compression
compression algo gzip
compression type text/html text/plain application/json text/css
# Access to port 443 is restricted to Cloudflare IPs at the firewall level
# All requests reaching HAProxy come from Cloudflare; no additional ACL validation is required.
# HAProxy's own forwardfor is not enabled; real IP is set explicitly below via CF-Connecting-IP
# Overwrite standard headers directly with the real IP forcibly added by Cloudflare
http-request set-header X-Forwarded-For %[req.hdr(CF-Connecting-IP)]
http-request set-header X-Real-IP %[req.hdr(CF-Connecting-IP)]
# Rate limiting uses the shared stick-table from http_in frontend
http-request track-sc0 req.hdr(CF-Connecting-IP) table http_in
# Track global request rate (per second)
http-request track-sc1 str(global) table global_rate_tracking
# Tier 3: global >= 200 RPS -> limit each IP to 5 RPM
http-request deny deny_status 429 if { sc_http_req_rate(1,global_rate_tracking) ge 200 } { sc_http_req_rate(0,http_in) gt 5 }
# Tier 2: global >= 100 RPS -> limit each IP to 10 RPM
http-request deny deny_status 429 if { sc_http_req_rate(1,global_rate_tracking) ge 100 } { sc_http_req_rate(0,http_in) gt 10 }
# Tier 1: default -> limit each IP to 30 RPM
http-request deny deny_status 429 if { sc_http_req_rate(0,http_in) gt 30 }
# Block /health from external access
acl is_health path /health
http-request deny deny_status 403 if is_health
default_backend app
backend global_rate_tracking
stick-table type string len 6 size 1 expire 10m store http_req_rate(1s)
+7
View File
@@ -45,6 +45,13 @@
proto: tcp
comment: "SSH"
- name: Allow HTTP (port 80) through UFW
community.general.ufw:
rule: allow
port: "80"
proto: tcp
comment: "HTTP"
- name: Allow HTTPS (port 443) through UFW
community.general.ufw:
rule: allow
+12
View File
@@ -13,6 +13,18 @@ resource "vultr_firewall_rule" "ssh_v4" {
notes = "Allow SSH from anywhere"
}
# Allow HTTP from Cloudflare only (IPv4)
resource "vultr_firewall_rule" "http_cloudflare_v4" {
firewall_group_id = vultr_firewall_group.rustyip.id
protocol = "tcp"
ip_type = "v4"
subnet = "0.0.0.0"
subnet_size = 0
port = "80"
source = "cloudflare"
notes = "Allow HTTP from Cloudflare IPv4"
}
# Allow HTTPS from Cloudflare only (IPv4)
resource "vultr_firewall_rule" "https_cloudflare_v4" {
firewall_group_id = vultr_firewall_group.rustyip.id