Rename discriminator to waf everywhere (closes #312)
check / check (push) Successful in 5m7s

The command is now `sww waf`, and the package is `internal/waf`. Types,
functions, files, tests, comments, docs, the Dockerfile, the runit
script, `script/`, compose examples, the log field `waf_line` and the
`X-From-Waf` test header follow. No behaviour changes, and no setting
changes name: rproxy already finds the waf through `SWRP_WAF_BACKEND`.

Done with one `sed -i` (Discriminator and DISCRIMINATOR to WAF,
discriminator to waf) over the files `git grep -i discriminator`
listed, as sneak allowed for this rename, then `git mv` for the
package and files.

Model: opus-5-5
This commit is contained in:
sneak committed 2026-11-11 00:41:32 +00:00
1 parent ad36dd1775
commit 3fc63a1e40
89 files changed
+1454 -1489

No files matched your search

+10 -10
View File
@@ -11,7 +11,7 @@ import (
"time"
"sneak.berlin/go/smallwebwaf/internal/config"
"sneak.berlin/go/smallwebwaf/internal/discriminator"
"sneak.berlin/go/smallwebwaf/internal/waf"
)
// healthCheckTimeout bounds the whole health check.
@@ -19,8 +19,8 @@ const healthCheckTimeout = 5 * time.Second
var errHealthEndpoint = errors.New("the health endpoint answered")
// HealthCheck is the container's health check. In discriminator's container, where
// SWW_COMMAND is discriminator, it returns 0 while discriminator answers its health
// HealthCheck is the container's health check. In waf's container, where
// SWW_COMMAND is waf, it returns 0 while waf answers its health
// endpoint on 127.0.0.1, at the port in SWW_LISTEN_ADDR. In rproxy's container,
// where SWW_COMMAND is rproxy, unset or empty, it returns 0 while rproxy
// answers its health endpoint on 127.0.0.1, at the port in SWRP_LISTEN_ADDR, and
@@ -49,8 +49,8 @@ func HealthCheck(
switch {
case err != nil:
err = fmt.Errorf("invalid setting: %w", err)
case command == "discriminator":
err = checkDiscriminator(ctx, lookupEnv)
case command == "waf":
err = checkWAF(ctx, lookupEnv)
default:
err = checkRProxyAndApp(ctx, lookupEnv)
}
@@ -64,18 +64,18 @@ func HealthCheck(
return 0
}
// checkDiscriminator asks discriminator's health endpoint.
func checkDiscriminator(
// checkWAF asks waf's health endpoint.
func checkWAF(
ctx context.Context, lookupEnv func(string) (string, bool),
) error {
listenAddr, err := config.DiscriminatorListenAddr(lookupEnv)
listenAddr, err := config.WAFListenAddr(lookupEnv)
if err != nil {
return fmt.Errorf("invalid setting: %w", err)
}
err = askHealthEndpoint(ctx, listenAddr)
if err != nil {
return fmt.Errorf("ask discriminator: %w", err)
return fmt.Errorf("ask waf: %w", err)
}
return nil
@@ -109,7 +109,7 @@ func checkRProxyAndApp(
// listenAddr, which the settings have checked has one.
func askHealthEndpoint(ctx context.Context, listenAddr string) error {
_, port, _ := net.SplitHostPort(listenAddr)
health := "http://" + net.JoinHostPort("127.0.0.1", port) + discriminator.HealthPath
health := "http://" + net.JoinHostPort("127.0.0.1", port) + waf.HealthPath
req, err := http.NewRequestWithContext(ctx, http.MethodGet, health, http.NoBody)
if err != nil {
+11 -11
View File
@@ -19,11 +19,11 @@ const (
// commandSetting says which command a container runs, and which the health
// check checks.
commandSetting = "SWW_COMMAND"
// discriminatorCommand is the command that runs discriminator.
discriminatorCommand = "discriminator"
// wafCommand is the command that runs waf.
wafCommand = "waf"
)
func TestHealthCheckInDiscriminatorsContainer(t *testing.T) {
func TestHealthCheckInWAFsContainer(t *testing.T) {
t.Parallel()
ctx, stop := context.WithCancel(t.Context())
@@ -47,7 +47,7 @@ func TestHealthCheckInDiscriminatorsContainer(t *testing.T) {
// The container's settings: an address to listen on with an empty
// host part, which the health check asks at 127.0.0.1.
wantHealthCheck(t, map[string]string{
commandSetting: discriminatorCommand, listenAddr: ":" + port,
commandSetting: wafCommand, listenAddr: ":" + port,
}, 0, "")
// The health check reads that setting alone, here given as a file: a removed
@@ -59,7 +59,7 @@ func TestHealthCheckInDiscriminatorsContainer(t *testing.T) {
{"SWW_MODE", "neither"},
} {
wantHealthCheck(t, map[string]string{
commandSetting: discriminatorCommand,
commandSetting: wafCommand,
listenAddr + "_FILE": writeFile(t, ":"+port+"\n"),
other.name: other.value,
}, 0, "")
@@ -68,10 +68,10 @@ func TestHealthCheckInDiscriminatorsContainer(t *testing.T) {
// Listening on port 0 takes any free port, so nothing listens on port 0
// itself.
wantHealthCheck(t, map[string]string{
commandSetting: discriminatorCommand, listenAddr: ":0",
}, 1, "unhealthy: ask discriminator: ")
commandSetting: wafCommand, listenAddr: ":0",
}, 1, "unhealthy: ask waf: ")
wantHealthCheck(t, map[string]string{
commandSetting: discriminatorCommand, listenAddr: "8080",
commandSetting: wafCommand, listenAddr: "8080",
}, 1, "unhealthy: invalid setting: SWW_LISTEN_ADDR: ")
stop()
@@ -90,7 +90,7 @@ func TestHealthCheckInRProxysContainer(t *testing.T) {
defer app.Close()
// Nothing listens at the backend. The health check asks rproxy and the app,
// whether or not discriminator answers.
// whether or not waf answers.
site, _ := startRProxy(t, "http://127.0.0.1:9", app.URL)
_, port, _ := net.SplitHostPort(strings.TrimPrefix(site, "http://"))
settings := func(more ...string) map[string]string {
@@ -107,7 +107,7 @@ func TestHealthCheckInRProxysContainer(t *testing.T) {
wantHealthCheck(t, settings(commandSetting, "rproxy"), 0, "")
// SWW_COMMAND cannot be given as a file.
wantHealthCheck(t, settings(commandSetting+"_FILE", writeFile(t, "discriminator\n")),
wantHealthCheck(t, settings(commandSetting+"_FILE", writeFile(t, "waf\n")),
0, "")
// Another setting does not fail it, even an invalid one, and neither does
@@ -137,7 +137,7 @@ func TestHealthCheckFailsWithAnyOtherCommand(t *testing.T) {
wantHealthCheck(t, map[string]string{commandSetting: "healthcheck"}, 1,
"unhealthy: invalid setting: SWW_COMMAND: "+
"\"healthcheck\" is not rproxy or discriminator\n")
"\"healthcheck\" is not rproxy or waf\n")
}
func TestHealthCheckRefusesAnArgument(t *testing.T) {
+53 -53
View File
@@ -19,18 +19,18 @@ import (
"testing"
"time"
"sneak.berlin/go/smallwebwaf/internal/discriminator"
"sneak.berlin/go/smallwebwaf/internal/sww"
"sneak.berlin/go/smallwebwaf/internal/waf"
)
const (
// wafBackend is the setting that names discriminator to rproxy.
// wafBackend is the setting that names waf to rproxy.
wafBackend = "SWRP_WAF_BACKEND"
// rproxyListenAddr is the setting that says where rproxy listens.
rproxyListenAddr = "SWRP_LISTEN_ADDR"
// rproxyUpstreamURL is the setting that says where the app is.
rproxyUpstreamURL = "SWRP_UPSTREAM_URL"
// rproxyTimeout is the setting that says how long rproxy waits for discriminator.
// rproxyTimeout is the setting that says how long rproxy waits for waf.
rproxyTimeout = "SWRP_TIMEOUT_MS"
// bannedProbe is a path the default rule file bans a client for.
bannedProbe = "/.env"
@@ -52,7 +52,7 @@ func runRProxy(ctx context.Context, env map[string]string, out *output) int {
})
}
// startRProxy runs rproxy with discriminator at backend and the app at appURL, and
// startRProxy runs rproxy with waf at backend and the app at appURL, and
// returns its URL and what it writes. It believes X-Forwarded-For from the
// tests. It stops as the test ends, and that it stopped in order is checked.
func startRProxy(t *testing.T, backend, appURL string) (string, *output) {
@@ -91,10 +91,10 @@ func startRProxy(t *testing.T, backend, appURL string) (string, *output) {
return "http://" + addr, out
}
// startDiscriminator runs discriminator with the settings in env, and returns
// startWAF runs waf with the settings in env, and returns
// its address and URL, what it writes, and what stops it, which checks that it
// stopped in order.
func startDiscriminator(t *testing.T, env map[string]string) (string, *output, func()) {
func startWAF(t *testing.T, env map[string]string) (string, *output, func()) {
t.Helper()
ctx, stop := context.WithCancel(t.Context())
@@ -113,17 +113,17 @@ func startDiscriminator(t *testing.T, env map[string]string) (string, *output, f
select {
case status := <-exited:
if status != 0 {
t.Fatalf("discriminator's exit status %d, want 0; output:\n%s", status, out.text())
t.Fatalf("waf's exit status %d, want 0; output:\n%s", status, out.text())
}
case <-time.After(waitLimit):
t.Fatal("discriminator still running after being told to stop")
t.Fatal("waf still running after being told to stop")
}
}
}
// discriminatorSettings are discriminator's settings with the default rule
// wafSettings are waf's settings with the default rule
// file, to listen at any free port.
func discriminatorSettings(t *testing.T) map[string]string {
func wafSettings(t *testing.T) map[string]string {
t.Helper()
return map[string]string{
@@ -136,7 +136,7 @@ func discriminatorSettings(t *testing.T) map[string]string {
func TestABanThroughOneSiteRefusesTheClientOnTheOther(t *testing.T) {
t.Parallel()
addr, discriminatorOut, stop := startDiscriminator(t, discriminatorSettings(t))
addr, wafOut, stop := startWAF(t, wafSettings(t))
defer stop()
backend := "http://" + addr
@@ -151,32 +151,32 @@ func TestABanThroughOneSiteRefusesTheClientOnTheOther(t *testing.T) {
wantStatus(t, second+"/", placed, http.StatusForbidden)
wantStatus(t, second+"/", otherClient, http.StatusOK)
// One request's three lines carry one id, rproxy's with discriminator's answer.
// One request's three lines carry one id, rproxy's with waf's answer.
line := firstOut.line(t, "path", bannedProbe)
if line["disposition"] != discriminator.DispositionDeny ||
if line["disposition"] != waf.DispositionDeny ||
line["client_ip"] != placed {
t.Errorf("rproxy's line is %v", line)
}
id, _ := line["request_id"].(string)
decision := discriminatorLine(t, discriminatorOut, id, "decision")
report := discriminatorLine(t, discriminatorOut, id, "report")
decision := wafLine(t, wafOut, id, "decision")
report := wafLine(t, wafOut, id, "report")
if decision["host"] != line["host"] || decision["client_ip"] != placed ||
report["status"] != float64(http.StatusForbidden) {
t.Errorf("discriminator's lines are %v and %v", decision, report)
t.Errorf("waf's lines are %v and %v", decision, report)
}
}
// discriminatorLine waits for discriminator's line with request id and
// discriminator_line kind.
func discriminatorLine(t *testing.T, out *output, id, kind string) map[string]any {
// wafLine waits for waf's line with request id and
// waf_line kind.
func wafLine(t *testing.T, out *output, id, kind string) map[string]any {
t.Helper()
deadline := time.Now().Add(waitLimit)
for time.Now().Before(deadline) {
line := out.findLine(t, func(fields map[string]any) bool {
return fields["request_id"] == id && fields["discriminator_line"] == kind
return fields["request_id"] == id && fields["waf_line"] == kind
})
if line != nil {
return line
@@ -190,14 +190,14 @@ func discriminatorLine(t *testing.T, out *output, id, kind string) map[string]an
return nil
}
func TestTheSiteAnswersWhileDiscriminatorIsStoppedAndStartedAgain(t *testing.T) {
func TestTheSiteAnswersWhileWAFIsStoppedAndStartedAgain(t *testing.T) {
t.Parallel()
env := discriminatorSettings(t)
addr, _, stop := startDiscriminator(t, env)
env := wafSettings(t)
addr, _, stop := startWAF(t, env)
// rproxy asks discriminator through an address the test holds throughout,
// so no other test can take it while discriminator is stopped.
// rproxy asks waf through an address the test holds throughout,
// so no other test can take it while waf is stopped.
backend, forwardTo := startForwarder(t)
forwardTo(addr)
@@ -228,22 +228,22 @@ func TestTheSiteAnswersWhileDiscriminatorIsStoppedAndStartedAgain(t *testing.T)
wantStatus(t, site+bannedProbe, placed, http.StatusForbidden)
wantStatus(t, site+"/", placed, http.StatusForbidden)
// rproxy's connections are closed at once from here, before discriminator
// rproxy's connections are closed at once from here, before waf
// stops and frees its port.
forwardTo("")
stop()
// With discriminator down, the site answers, and rproxy says the default did it.
// With waf down, the site answers, and rproxy says the default did it.
wantStatus(t, site+"/after-stop", placed, http.StatusOK)
line := out.line(t, "path", "/after-stop")
if line["disposition"] != discriminator.DispositionAllow ||
if line["disposition"] != waf.DispositionAllow ||
line["default_disposition"] != true {
t.Errorf("rproxy's line is %v", line)
}
// Started again with its state files, it applies the ban again.
addr, _, stop = startDiscriminator(t, env)
addr, _, stop = startWAF(t, env)
forwardTo(addr)
defer func() {
@@ -397,10 +397,10 @@ func sendRaw(t *testing.T, site, request string) int {
return res.StatusCode
}
func TestAChunkedBodyStaysABodyAtDiscriminator(t *testing.T) {
func TestAChunkedBodyStaysABodyAtWAF(t *testing.T) {
t.Parallel()
addr, discriminatorOut, stop := startDiscriminator(t, discriminatorSettings(t))
addr, wafOut, stop := startWAF(t, wafSettings(t))
defer stop()
site, _ := startRProxy(t, "http://"+addr, startApp(t))
@@ -409,7 +409,7 @@ func TestAChunkedBodyStaysABodyAtDiscriminator(t *testing.T) {
const smuggledID = "smuggled-request"
smuggled := "GET " + bannedProbe + " HTTP/1.1\r\nHost: site.example\r\n" +
discriminator.ClientHeader + ": " + otherClient + "\r\n" +
waf.ClientHeader + ": " + otherClient + "\r\n" +
"X-Request-ID: " + smuggledID + "\r\n\r\n"
status := sendRaw(t, site, "POST /upload HTTP/1.1\r\nHost: site.example\r\n"+
@@ -425,18 +425,18 @@ func TestAChunkedBodyStaysABodyAtDiscriminator(t *testing.T) {
time.Sleep(pollInterval)
}
line := discriminatorOut.findLine(t, func(fields map[string]any) bool {
line := wafOut.findLine(t, func(fields map[string]any) bool {
return fields["request_id"] == smuggledID
})
if line != nil {
t.Errorf("discriminator read the body as a request: %v", line)
t.Errorf("waf read the body as a request: %v", line)
}
}
func TestATargetThatCannotGoToDiscriminatorIsRefusedForABannedClient(t *testing.T) {
func TestATargetThatCannotGoToWAFIsRefusedForABannedClient(t *testing.T) {
t.Parallel()
addr, _, stop := startDiscriminator(t, discriminatorSettings(t))
addr, _, stop := startWAF(t, wafSettings(t))
defer stop()
site, out := startRProxy(t, "http://"+addr, startApp(t))
@@ -460,7 +460,7 @@ func TestAClientWithAZoneIsTheClientWithout(t *testing.T) {
const zoned = "2001:db8::1%eth0"
addr, _, stop := startDiscriminator(t, discriminatorSettings(t))
addr, _, stop := startWAF(t, wafSettings(t))
defer stop()
site, out := startRProxy(t, "http://"+addr, startApp(t))
@@ -475,7 +475,7 @@ func TestAClientWithAZoneIsTheClientWithout(t *testing.T) {
}
}
func TestAStatusDiscriminatorDoesNotTakeIsReportedAs599(t *testing.T) {
func TestAStatusWAFDoesNotTakeIsReportedAs599(t *testing.T) {
t.Parallel()
const status = 799
@@ -486,7 +486,7 @@ func TestAStatusDiscriminatorDoesNotTakeIsReportedAs599(t *testing.T) {
}))
t.Cleanup(app.Close)
addr, discriminatorOut, stop := startDiscriminator(t, discriminatorSettings(t))
addr, wafOut, stop := startWAF(t, wafSettings(t))
defer stop()
site, out := startRProxy(t, "http://"+addr, app.URL)
@@ -500,9 +500,9 @@ func TestAStatusDiscriminatorDoesNotTakeIsReportedAs599(t *testing.T) {
id, _ := line["request_id"].(string)
report := discriminatorLine(t, discriminatorOut, id, "report")
report := wafLine(t, wafOut, id, "report")
if report["status"] != 599.0 {
t.Errorf("discriminator's line is %v, want status 599", report)
t.Errorf("waf's line is %v, want status 599", report)
}
}
@@ -544,16 +544,16 @@ func startRProxyProcess(t *testing.T, env ...string) (string, *exec.Cmd, *output
return addr, cmd, out
}
func TestRProxyAsksADiscriminatorThatSpeaksHTTP2OverHTTP1(t *testing.T) {
func TestRProxyAsksAWAFThatSpeaksHTTP2OverHTTP1(t *testing.T) {
t.Parallel()
protocols := make(chan string, 10)
backend := httptest.NewUnstartedServer(http.HandlerFunc(
func(w http.ResponseWriter, r *http.Request) {
if r.Header.Get(discriminator.ClientHeader) != "" {
if r.Header.Get(waf.ClientHeader) != "" {
protocols <- r.Proto
w.Header().Set(discriminator.DispositionHeader, discriminator.DispositionDeny)
w.Header().Set(waf.DispositionHeader, waf.DispositionDeny)
w.WriteHeader(http.StatusForbidden)
}
}))
@@ -563,7 +563,7 @@ func TestRProxyAsksADiscriminatorThatSpeaksHTTP2OverHTTP1(t *testing.T) {
// rproxy has no setting for the certificates it trusts, so it is told of this
// one as Go's client is told of any, in a process of its own.
certificates := filepath.Join(t.TempDir(), "discriminator.pem")
certificates := filepath.Join(t.TempDir(), "waf.pem")
err := os.WriteFile(certificates, pem.EncodeToMemory(&pem.Block{
Type: "CERTIFICATE", Bytes: backend.Certificate().Raw,
@@ -585,7 +585,7 @@ func TestRProxyAsksADiscriminatorThatSpeaksHTTP2OverHTTP1(t *testing.T) {
} {
status := sendRaw(t, addr, "GET / HTTP/1.1\r\nHost: site.example\r\n"+headers+"\r\n")
if status != http.StatusForbidden {
t.Errorf("with %s: status %d, want discriminator's 403", name, status)
t.Errorf("with %s: status %d, want waf's 403", name, status)
}
select {
@@ -594,7 +594,7 @@ func TestRProxyAsksADiscriminatorThatSpeaksHTTP2OverHTTP1(t *testing.T) {
t.Errorf("with %s: asked over %s, want HTTP/1.1", name, protocol)
}
case <-time.After(waitLimit):
t.Errorf("with %s: discriminator was not asked", name)
t.Errorf("with %s: waf was not asked", name)
}
}
}
@@ -623,8 +623,8 @@ func TestRProxyStoppingWaitsForAReportBeingSent(t *testing.T) {
release := make(chan struct{})
backend := httptest.NewServer(http.HandlerFunc(
func(w http.ResponseWriter, r *http.Request) {
if r.Header.Get(discriminator.ClientHeader) != "" {
w.Header().Set(discriminator.DispositionHeader, discriminator.DispositionAllow)
if r.Header.Get(waf.ClientHeader) != "" {
w.Header().Set(waf.DispositionHeader, waf.DispositionAllow)
return
}
@@ -731,7 +731,7 @@ func TestRProxyStoppingCutsOffARequestAfterFiveSeconds(t *testing.T) {
backend := httptest.NewServer(http.HandlerFunc(
func(w http.ResponseWriter, _ *http.Request) {
w.Header().Set(discriminator.DispositionHeader, discriminator.DispositionAllow)
w.Header().Set(waf.DispositionHeader, waf.DispositionAllow)
}))
t.Cleanup(backend.Close)
@@ -794,7 +794,7 @@ func TestRProxyInvalidSettingStopsTheStart(t *testing.T) {
{"no backend", wafBackend, nil},
{
"another invalid", rproxyTimeout,
map[string]string{wafBackend: "http://discriminator:8080", rproxyTimeout: "soon"},
map[string]string{wafBackend: "http://waf:8080", rproxyTimeout: "soon"},
},
} {
t.Run(tc.name, func(t *testing.T) {
@@ -831,7 +831,7 @@ func TestRProxyAddressInUseStopsTheStart(t *testing.T) {
out := &output{}
status := runRProxy(t.Context(), map[string]string{
wafBackend: "http://discriminator:8080",
wafBackend: "http://waf:8080",
rproxyListenAddr: taken.Addr().String(),
}, out)
if status != 1 {
@@ -848,7 +848,7 @@ func TestMainRunsRProxy(t *testing.T) {
addr, cmd, out := startRProxyProcess(t, wafBackend+"=http://127.0.0.1:9")
req, err := http.NewRequestWithContext(t.Context(), http.MethodGet,
"http://"+addr+discriminator.HealthPath, http.NoBody)
"http://"+addr+waf.HealthPath, http.NoBody)
if err != nil {
t.Fatalf("new request: %v", err)
}
+18 -18
View File
@@ -1,9 +1,9 @@
// Package sww runs the sww process, as the command it is given.
// `sww discriminator` reads the settings, the rule files, the lookup database
// `sww waf` reads the settings, the rule files, the lookup database
// and the state files, answers rproxy's questions until it is told to stop, and
// then stops in an orderly way, writing the state files. `sww rproxy`
// reads rproxy's settings alone, and runs the reverse proxy in front of one app
// that asks discriminator about each request. `sww healthcheck` is the
// that asks waf about each request. `sww healthcheck` is the
// container's health check.
package sww
@@ -23,13 +23,13 @@ import (
"sneak.berlin/go/smallwebwaf/internal/alerts"
"sneak.berlin/go/smallwebwaf/internal/config"
"sneak.berlin/go/smallwebwaf/internal/coreruleset"
"sneak.berlin/go/smallwebwaf/internal/discriminator"
"sneak.berlin/go/smallwebwaf/internal/lookup"
"sneak.berlin/go/smallwebwaf/internal/remotelog"
"sneak.berlin/go/smallwebwaf/internal/reputation"
"sneak.berlin/go/smallwebwaf/internal/requestlog"
"sneak.berlin/go/smallwebwaf/internal/rules"
"sneak.berlin/go/smallwebwaf/internal/state"
"sneak.berlin/go/smallwebwaf/internal/waf"
)
// shutdownTimeout is how long requests in progress may take to finish
@@ -37,7 +37,7 @@ import (
// runit and docker wait a little longer before they kill the process.
const shutdownTimeout = 5 * time.Second
// remoteLogStopTimeout is how long, as discriminator stops, the log lines still
// remoteLogStopTimeout is how long, as waf stops, the log lines still
// waiting are sent to SWW_LOG_REMOTE_URL before they are given up. stdout has
// carried them.
const remoteLogStopTimeout = 2 * time.Second
@@ -51,11 +51,11 @@ const usage = `usage: sww <command>
commands:
rproxy the reverse proxy in front of one app
discriminator decides for rproxy, and keeps bans, counters and history
waf decides for rproxy, and keeps bans, counters and history
healthcheck the container's health check
`
// Params are what RunDiscriminator and RunRProxy need from the process.
// Params are what RunWAF and RunRProxy need from the process.
type Params struct {
// Version is the version of the binary, set when it is built.
Version string
@@ -66,7 +66,7 @@ type Params struct {
}
// Main runs the command os.Args names, and returns the process's exit status.
// `sww rproxy` and `sww discriminator` run until SIGTERM or SIGINT.
// `sww rproxy` and `sww waf` run until SIGTERM or SIGINT.
// With no command, or one it does not know, it prints the commands and returns
// usageStatus.
func Main(version string) int {
@@ -84,8 +84,8 @@ func Main(version string) int {
switch args[0] {
case "rproxy":
return RunRProxy(ctx, params)
case "discriminator":
return RunDiscriminator(ctx, params)
case "waf":
return RunWAF(ctx, params)
case "healthcheck":
return HealthCheck(ctx, args[1:], os.LookupEnv, os.Stderr)
default:
@@ -100,10 +100,10 @@ func printUsage(w io.Writer) int {
return usageStatus
}
// RunDiscriminator is `sww discriminator`: it reads the settings, the rule files,
// RunWAF is `sww waf`: it reads the settings, the rule files,
// the lookup database and the state files, then serves requests until ctx is
// done. It returns the process's exit status, 1 when discriminator cannot start.
func RunDiscriminator(ctx context.Context, params Params) int {
// done. It returns the process's exit status, 1 when waf cannot start.
func RunWAF(ctx context.Context, params Params) int {
// Until the settings are read, the one message is an invalid setting's
// error, which every SWW_LOG_LEVEL lets through.
processLog := requestlog.NewProcessLogger(params.Stdout,
@@ -151,7 +151,7 @@ func RunDiscriminator(ctx context.Context, params Params) int {
return 1
}
crs, err := discriminator.NewCoreRuleSet(cfg)
crs, err := waf.NewCoreRuleSet(cfg)
if err != nil {
processLog.Error("cannot use the Core Rule Set", "error", err.Error())
@@ -191,14 +191,14 @@ func RunDiscriminator(ctx context.Context, params Params) int {
return serve(ctx, server, listener, files, ruleFiles, alertQueue, processLog)
}
// newServer returns the server discriminator runs, with the metrics of the alerts,
// newServer returns the server waf runs, with the metrics of the alerts,
// after reading the lookup database while SWW_LOOKUP_SOURCE is file. A lookup
// database that cannot be read is an error.
func newServer(
cfg *config.Config, stdout io.Writer, processLog *slog.Logger,
now func() time.Time, ruleFiles *rules.Files, alertQueue *alerts.Queue,
crs *coreruleset.CoreRuleSet,
) (*discriminator.Server, error) {
) (*waf.Server, error) {
var lookupFile *lookup.File
if cfg.LookupSource == "file" {
@@ -215,7 +215,7 @@ func newServer(
}
}
server := discriminator.New(discriminator.Params{
server := waf.New(waf.Params{
Config: cfg,
RequestLog: stdout,
ProcessLog: processLog,
@@ -239,7 +239,7 @@ func newServer(
// loadStateFiles reads the state files into the parts of server and into
// alertQueue, as state.Load does.
func loadStateFiles(
cfg *config.Config, server *discriminator.Server, alertQueue *alerts.Queue,
cfg *config.Config, server *waf.Server, alertQueue *alerts.Queue,
now func() time.Time, processLog *slog.Logger,
) (*state.Files, error) {
return state.Load(state.Params{
@@ -325,7 +325,7 @@ func startSending(
// shutdownTimeout to finish, and writes every state file, alerts.json with
// the alerts still waiting.
func serve(
ctx context.Context, server *discriminator.Server, listener net.Listener,
ctx context.Context, server *waf.Server, listener net.Listener,
files *state.Files, ruleFiles *rules.Files, alertQueue *alerts.Queue,
processLog *slog.Logger,
) int {
+9 -9
View File
@@ -20,9 +20,9 @@ import (
"time"
"sneak.berlin/go/smallwebwaf/internal/config"
"sneak.berlin/go/smallwebwaf/internal/discriminator"
"sneak.berlin/go/smallwebwaf/internal/lookup/lookuptest"
"sneak.berlin/go/smallwebwaf/internal/sww"
"sneak.berlin/go/smallwebwaf/internal/waf"
)
const (
@@ -123,11 +123,11 @@ func (o *output) text() string {
return o.buf.String()
}
// run runs `sww discriminator` with the settings in env until ctx is done,
// run runs `sww waf` with the settings in env until ctx is done,
// and returns its exit status. SWW_LOOKUP_SOURCE is off unless env sets it,
// so that no test sends GeoJS its clients' addresses.
func run(ctx context.Context, env map[string]string, out *output) int {
return sww.RunDiscriminator(ctx, sww.Params{
return sww.RunWAF(ctx, sww.Params{
Version: testVersion,
LookupEnv: func(name string) (string, bool) {
value, ok := env[name]
@@ -1245,7 +1245,7 @@ func startApp(t *testing.T) string {
return app.URL
}
// runUntilStopped runs `sww discriminator` with the settings in env, has use
// runUntilStopped runs `sww waf` with the settings in env, has use
// send it requests at url, then stops it as SIGTERM does, checks that it
// stopped in order, and returns its output.
func runUntilStopped(
@@ -1319,7 +1319,7 @@ func wantStartingLine(t *testing.T, line map[string]any, dir string) {
}
}
// wantAllowed checks that discriminator at url allows a request from localhost.
// wantAllowed checks that waf at url allows a request from localhost.
func wantAllowed(t *testing.T, url string) {
t.Helper()
@@ -1504,7 +1504,7 @@ func onlyBan(t *testing.T, dir string) map[string]any {
return file.Bans[0]
}
// wantRefused checks that discriminator at url refuses a request from localhost with
// wantRefused checks that waf at url refuses a request from localhost with
// 403, the default SWW_BAN_RESPONSE.
func wantRefused(t *testing.T, url string) {
t.Helper()
@@ -1681,8 +1681,8 @@ func (w *webhook) waitFor(t *testing.T, event string, answered bool) webhookPost
}
// statusFrom returns the status a request to url from the client at from is
// answered with. The request serves rproxy and discriminator alike. rproxy reads the
// client from X-Forwarded-For, and replaces the other two headers. To discriminator,
// answered with. The request serves rproxy and waf alike. rproxy reads the
// client from X-Forwarded-For, and replaces the other two headers. To waf,
// they make it a question about that client.
func statusFrom(t *testing.T, url, from string) int {
t.Helper()
@@ -1694,7 +1694,7 @@ func statusFrom(t *testing.T, url, from string) int {
}
req.Header.Set("X-Forwarded-For", from)
req.Header.Set(discriminator.ClientHeader, from)
req.Header.Set(waf.ClientHeader, from)
req.Header.Set("X-Request-ID", rand.Text())
transport := &http.Transport{}
@@ -14,8 +14,8 @@ import (
"testing"
"time"
"sneak.berlin/go/smallwebwaf/internal/discriminator"
"sneak.berlin/go/smallwebwaf/internal/sww"
"sneak.berlin/go/smallwebwaf/internal/waf"
)
// commandForMain, while set in the environment, has this test binary run Main
@@ -34,7 +34,7 @@ func TestMain(m *testing.M) {
os.Exit(sww.Main(testVersion))
}
func TestMainRunsDiscriminator(t *testing.T) {
func TestMainRunsWAF(t *testing.T) {
t.Parallel()
binary, err := os.Executable()
@@ -50,7 +50,7 @@ func TestMainRunsDiscriminator(t *testing.T) {
cmd.Stdout = out
cmd.Env = append(os.Environ(),
commandForMain+"="+discriminatorCommand,
commandForMain+"="+wafCommand,
listenAddr+"="+localhost+":0",
stateDir+"="+t.TempDir(),
rulesDir+"="+t.TempDir(),
@@ -67,7 +67,7 @@ func TestMainRunsDiscriminator(t *testing.T) {
})
addr, _ := out.line(t, "msg", "starting")["address"].(string)
ask(t, addr, "/", http.StatusOK, discriminator.DispositionAllow)
ask(t, addr, "/", http.StatusOK, waf.DispositionAllow)
err = cmd.Process.Signal(syscall.SIGTERM)
if err != nil {
@@ -108,7 +108,7 @@ func TestMainWithoutACommandItKnowsPrintsTheCommands(t *testing.T) {
t.Errorf("exit status %d (%v), want 2", code, err)
}
for _, name := range []string{"rproxy", discriminatorCommand, "healthcheck"} {
for _, name := range []string{"rproxy", wafCommand, "healthcheck"} {
if !strings.Contains(stderr.String(), name) {
t.Errorf("stderr %q does not name %s", stderr.String(), name)
}
@@ -121,7 +121,7 @@ func TestMainWithoutACommandItKnowsPrintsTheCommands(t *testing.T) {
}
}
// ask sends discriminator at addr rproxy's question about a request for path from
// ask sends waf at addr rproxy's question about a request for path from
// placed, checks the answer's status and disposition, and closes it.
func ask(t *testing.T, addr, path string, status int, disposition string) {
t.Helper()
@@ -132,7 +132,7 @@ func ask(t *testing.T, addr, path string, status int, disposition string) {
t.Fatalf("new request: %v", err)
}
req.Header.Set(discriminator.ClientHeader, placed)
req.Header.Set(waf.ClientHeader, placed)
req.Header.Set("X-Request-ID", rand.Text())
transport := &http.Transport{}
@@ -147,16 +147,16 @@ func ask(t *testing.T, addr, path string, status int, disposition string) {
_ = res.Body.Close()
if res.StatusCode != status ||
res.Header.Get(discriminator.DispositionHeader) != disposition {
res.Header.Get(waf.DispositionHeader) != disposition {
t.Errorf("answered %d with disposition %q, want %d with %q", res.StatusCode,
res.Header.Get(discriminator.DispositionHeader), status, disposition)
res.Header.Get(waf.DispositionHeader), status, disposition)
}
}
// runDiscriminator runs `sww discriminator` with the settings in env, has use
// runWAF runs `sww waf` with the settings in env, has use
// send it requests at the address it listens on, stops it as SIGTERM does, and
// checks that it stopped in order.
func runDiscriminator(t *testing.T, env map[string]string, use func(addr string)) {
func runWAF(t *testing.T, env map[string]string, use func(addr string)) {
t.Helper()
ctx, stop := context.WithCancel(t.Context())
@@ -183,7 +183,7 @@ func runDiscriminator(t *testing.T, env map[string]string, use func(addr string)
out.line(t, "msg", "stopped")
}
func TestDiscriminatorKeepsItsBansAcrossARestartAndNeedsNoApp(t *testing.T) {
func TestWAFKeepsItsBansAcrossARestartAndNeedsNoApp(t *testing.T) {
t.Parallel()
env := map[string]string{
@@ -192,13 +192,13 @@ func TestDiscriminatorKeepsItsBansAcrossARestartAndNeedsNoApp(t *testing.T) {
rulesDir: filepath.Join("..", "..", "share", "rules.d"),
}
runDiscriminator(t, env, func(addr string) {
ask(t, addr, "/", http.StatusOK, discriminator.DispositionAllow)
runWAF(t, env, func(addr string) {
ask(t, addr, "/", http.StatusOK, waf.DispositionAllow)
// The default rule file bans a probe for this.
ask(t, addr, "/.env", http.StatusForbidden, discriminator.DispositionDeny)
ask(t, addr, "/.env", http.StatusForbidden, waf.DispositionDeny)
})
runDiscriminator(t, env, func(addr string) {
ask(t, addr, "/", http.StatusForbidden, discriminator.DispositionDeny)
runWAF(t, env, func(addr string) {
ask(t, addr, "/", http.StatusForbidden, waf.DispositionDeny)
})
}