83 lines
2.1 KiB
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)
|
|
}
|