summaryrefslogtreecommitdiff
path: root/internal/httpapi/proxy.go
diff options
context:
space:
mode:
authorChia <Chia@93.nz>2026-08-05 22:01:29 +1200
committerChia <Chia@93.nz>2026-08-05 22:07:50 +1200
commiteadb2ffe85c43cf6fc741c9823cd28eedb4a844c (patch)
tree1aba2536d57360da403aa35c9ced58b615c7064e /internal/httpapi/proxy.go
parentcd0dd91ab93653631904f2ea0e574ccde6d60339 (diff)
feat: harden prepaid billing and commercial operations
Diffstat (limited to 'internal/httpapi/proxy.go')
-rw-r--r--internal/httpapi/proxy.go44
1 files changed, 44 insertions, 0 deletions
diff --git a/internal/httpapi/proxy.go b/internal/httpapi/proxy.go
new file mode 100644
index 0000000..3cd2797
--- /dev/null
+++ b/internal/httpapi/proxy.go
@@ -0,0 +1,44 @@
+package httpapi
+
+import (
+ "net"
+ "net/http"
+ "strings"
+)
+
+// TrustProxyHeaders accepts forwarding metadata only from explicitly trusted
+// CIDRs. This prevents a direct client from forging HTTPS or audit IP state.
+func TrustProxyHeaders(next http.Handler, trustedCIDRs []string, requireHTTPS bool) (http.Handler, error) {
+ trusted := make([]*net.IPNet, 0, len(trustedCIDRs))
+ for _, value := range trustedCIDRs {
+ _, network, err := net.ParseCIDR(value)
+ if err != nil {
+ return nil, err
+ }
+ trusted = append(trusted, network)
+ }
+ return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ host, _, _ := net.SplitHostPort(r.RemoteAddr)
+ remote := net.ParseIP(host)
+ trustedPeer := false
+ for _, network := range trusted {
+ if remote != nil && network.Contains(remote) {
+ trustedPeer = true
+ break
+ }
+ }
+ if !trustedPeer {
+ for _, header := range []string{"Forwarded", "X-Forwarded-For", "X-Forwarded-Host", "X-Forwarded-Port", "X-Forwarded-Proto", "X-Real-IP"} {
+ r.Header.Del(header)
+ }
+ } else if forwarded := strings.TrimSpace(strings.Split(r.Header.Get("X-Forwarded-For"), ",")[0]); net.ParseIP(forwarded) != nil {
+ r.RemoteAddr = net.JoinHostPort(forwarded, "0")
+ }
+ secure := r.TLS != nil || (trustedPeer && strings.EqualFold(strings.TrimSpace(r.Header.Get("X-Forwarded-Proto")), "https"))
+ if requireHTTPS && !secure {
+ http.Error(w, "HTTPS is required", http.StatusUpgradeRequired)
+ return
+ }
+ next.ServeHTTP(w, r)
+ }), nil
+}