Rename discriminator to waf everywhere (closes #312)
check / check (push) Successful in 5m7s
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:
1 parent
ad36dd1775
commit
3fc63a1e40
89 files changed
+1454
-1489
No files matched your search
+10
-10
@@ -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 {
|
||||
|
||||
@@ -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
@@ -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
@@ -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 {
|
||||
|
||||
@@ -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)
|
||||
})
|
||||
}
|
||||
Reference in new issue
Block a user