zoraxy-waf/proxy.go

83 lines
2.1 KiB
Go

package main
import (
"fmt"
"io"
"net/http"
"net/url"
"time"
)
// proxyThroughWAF forwards the request to the WAF, returns the response.
// The WAF is expected to either block (403) or forward to the backend (2xx).
func proxyThroughWAF(w http.ResponseWriter, r *http.Request) (*http.Response, error) {
cfg := getConfig()
target, err := url.Parse(cfg.WAFURL)
if err != nil {
return nil, fmt.Errorf("invalid WAF URL: %w", err)
}
// Clone the request for the WAF.
proxyReq, err := http.NewRequestWithContext(r.Context(), r.Method, target.String()+r.URL.Path, r.Body)
if err != nil {
return nil, fmt.Errorf("create proxy request: %w", err)
}
// Copy headers.
for key, vals := range r.Header {
for _, v := range vals {
proxyReq.Header.Add(key, v)
}
}
proxyReq.Header.Set("X-Forwarded-For", r.RemoteAddr)
proxyReq.Header.Set("X-Real-IP", r.RemoteAddr)
client := &http.Client{Timeout: time.Duration(cfg.TimeoutMs) * time.Millisecond}
resp, err := client.Do(proxyReq)
if err != nil {
return nil, fmt.Errorf("WAF unreachable: %w", err)
}
return resp, nil
}
// bypassToBackend forwards the request directly to the backend (fail-open path).
func bypassToBackend(w http.ResponseWriter, r *http.Request) {
cfg := getConfig()
target, err := url.Parse(cfg.BackendURL)
if err != nil {
http.Error(w, "backend unavailable", http.StatusServiceUnavailable)
return
}
proxyReq, err := http.NewRequestWithContext(r.Context(), r.Method, target.String()+r.URL.Path, r.Body)
if err != nil {
http.Error(w, "proxy error", http.StatusInternalServerError)
return
}
for key, vals := range r.Header {
for _, v := range vals {
proxyReq.Header.Add(key, v)
}
}
client := &http.Client{Timeout: time.Duration(cfg.TimeoutMs) * time.Millisecond}
resp, err := client.Do(proxyReq)
if err != nil {
http.Error(w, "backend unreachable", http.StatusBadGateway)
return
}
defer resp.Body.Close()
// Copy response back.
for key, vals := range resp.Header {
for _, v := range vals {
w.Header().Add(key, v)
}
}
w.WriteHeader(resp.StatusCode)
io.Copy(w, resp.Body)
}