feat: add return port listener for WAF async callbacks/alerts

This commit is contained in:
Claus Lohmar 2026-08-02 06:57:37 +00:00
parent 32d60a3583
commit 6c8815749f
4 changed files with 39 additions and 0 deletions

View file

@ -12,6 +12,7 @@ import (
type Config struct {
Enabled bool `json:"enabled"`
WAFURL string `json:"waf_url"`
ReturnPort int `json:"return_port"` // port for WAF async callbacks
HealthInterval int `json:"health_interval"`
TimeoutMs int `json:"timeout_ms"`
}

30
main.go
View file

@ -41,6 +41,20 @@ func main() {
// Start background health checker.
startHealthCheck()
// Return port listener for WAF async callbacks.
cfg := getConfig()
if cfg.ReturnPort > 0 {
go func() {
rmux := http.NewServeMux()
rmux.HandleFunc("/verdict", handleReturnVerdict)
addr := fmt.Sprintf(":%d", cfg.ReturnPort)
log.Printf("Firewall: return listener on %s", addr)
if err := http.ListenAndServe(addr, rmux); err != nil {
log.Printf("Firewall: return listener error: %v", err)
}
}()
}
mux := http.NewServeMux()
// Static capture handler — all proxied traffic hits this.
@ -85,6 +99,9 @@ func handleSaveConfig(w http.ResponseWriter, r *http.Request) {
if u := r.FormValue("waf_url"); u != "" {
cfg.WAFURL = u
}
if v := r.FormValue("return_port"); v != "" {
fmt.Sscanf(v, "%d", &cfg.ReturnPort)
}
if v := r.FormValue("health_interval"); v != "" {
fmt.Sscanf(v, "%d", &cfg.HealthInterval)
}
@ -105,6 +122,19 @@ func handleGetStats(w http.ResponseWriter, r *http.Request) {
writeJSON(w, http.StatusOK, metricsSnapshot())
}
func handleReturnVerdict(w http.ResponseWriter, r *http.Request) {
var v struct {
Verdict string `json:"verdict"`
Reason string `json:"reason"`
}
if err := json.NewDecoder(r.Body).Decode(&v); err != nil {
writeJSON(w, http.StatusBadRequest, map[string]string{"error": "bad payload"})
return
}
log.Printf("Firewall: async verdict %s (reason=%s)", v.Verdict, v.Reason)
writeJSON(w, http.StatusOK, map[string]string{"status": "ok"})
}
// --- Helpers ---
func writeJSON(w http.ResponseWriter, status int, v interface{}) {

View file

@ -5,6 +5,7 @@ function loadConfig() {
$('#fw-enabled').prop('checked', data.enabled);
$('#enabled-label').text(data.enabled ? 'Firewall Enabled' : 'Firewall Disabled');
$('#waf-url').val(data.waf_url);
$('#return-port').val(data.return_port);
$('#health-interval').val(data.health_interval);
$('#timeout-ms').val(data.timeout_ms);
});
@ -31,6 +32,7 @@ function saveConfig() {
data: {
enabled: $('#fw-enabled').is(':checked'),
waf_url: $('#waf-url').val().trim(),
return_port: $('#return-port').val(),
health_interval: $('#health-interval').val(),
timeout_ms: $('#timeout-ms').val()
},

View file

@ -31,6 +31,12 @@
<small>The firewall service that inspects each request and returns a verdict.</small>
</div>
<div class="field">
<label for="return-port">Return Port (async callbacks)</label>
<input type="number" id="return-port" placeholder="9090" min="1" max="65535">
<small>WAF sends async verdicts/alerts to this port.</small>
</div>
<div class="field">
<label for="health-interval">Health Check Interval (s)</label>
<input type="number" id="health-interval" placeholder="5" min="1" max="60">