aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorXe Iaso <me@xeiaso.net>2025-03-17 01:25:18 -0400
committerGitHub <noreply@github.com>2025-03-17 01:25:18 -0400
commit2859f037cdf0cfd7314768152a2a5ad0ec8cd638 (patch)
tree7031b50ed6509b9ce22ff8de7628d2771da33237
parentc3f5f1f5463bb8c48dc341e31957a54527f299e9 (diff)
downloadx-2859f037cdf0cfd7314768152a2a5ad0ec8cd638.tar.xz
x-2859f037cdf0cfd7314768152a2a5ad0ec8cd638.zip
cmd/anubis: add rule hashes for admin-configured denials (#696)
* cmd/anubis: add rule hashes for admin-configured denials Closes #695 Signed-off-by: Xe Iaso <me@xeiaso.net> * cmd/anubis: remove theoretical nil pointer deference panic This won't actually happen in real life, but the code paths might change so we should be somewhat defensive. Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --------- Signed-off-by: Xe Iaso <me@xeiaso.net> Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
-rw-r--r--cmd/anubis/index_templ.go2
-rw-r--r--cmd/anubis/main.go31
-rw-r--r--cmd/anubis/policy.go21
-rw-r--r--cmd/anubis/static/js/main.mjs.gzbin985 -> 985 bytes
-rw-r--r--cmd/anubis/testdata/everything_blocked.json10
5 files changed, 57 insertions, 7 deletions
diff --git a/cmd/anubis/index_templ.go b/cmd/anubis/index_templ.go
index 50d65ef..4b400d1 100644
--- a/cmd/anubis/index_templ.go
+++ b/cmd/anubis/index_templ.go
@@ -1,6 +1,6 @@
// Code generated by templ - DO NOT EDIT.
-// templ: version: v0.3.819
+// templ: version: v0.3.833
package main
//lint:file-ignore SA4006 This context is only used if a nested component is present.
diff --git a/cmd/anubis/main.go b/cmd/anubis/main.go
index 69be826..de0a592 100644
--- a/cmd/anubis/main.go
+++ b/cmd/anubis/main.go
@@ -114,6 +114,21 @@ func main() {
log.Fatal(err)
}
+ fmt.Println("Rule error IDs:")
+ for _, rule := range s.policy.Bots {
+ if rule.Action != config.RuleDeny {
+ continue
+ }
+
+ hash, err := rule.Hash()
+ if err != nil {
+ log.Fatalf("can't calculate checksum of rule %s: %v", rule.Name, err)
+ }
+
+ fmt.Printf("* %s: %s\n", rule.Name, hash)
+ }
+ fmt.Println()
+
mux := http.NewServeMux()
xess.Mount(mux)
@@ -229,7 +244,7 @@ type Server struct {
}
func (s *Server) maybeReverseProxy(w http.ResponseWriter, r *http.Request) {
- cr := s.check(r)
+ cr, rule := s.check(r)
r.Header.Add("X-Anubis-Rule", cr.Name)
r.Header.Add("X-Anubis-Action", string(cr.Rule))
lg := slog.With(
@@ -272,7 +287,19 @@ func (s *Server) maybeReverseProxy(w http.ResponseWriter, r *http.Request) {
case config.RuleDeny:
clearCookie(w)
lg.Info("explicit deny")
- templ.Handler(base("Oh noes!", errorPage("Access Denied")), templ.WithStatus(http.StatusOK)).ServeHTTP(w, r)
+ if rule == nil {
+ lg.Error("rule is nil, cannot calculate checksum")
+ templ.Handler(base("Oh noes!", errorPage("Other internal server error (contact the admin)")), templ.WithStatus(http.StatusInternalServerError)).ServeHTTP(w, r)
+ return
+ }
+ hash, err := rule.Hash()
+ if err != nil {
+ lg.Error("can't calculate checksum of rule", "err", err)
+ templ.Handler(base("Oh noes!", errorPage("Other internal server error (contact the admin)")), templ.WithStatus(http.StatusInternalServerError)).ServeHTTP(w, r)
+ return
+ }
+ lg.Debug("rule hash", "hash", hash)
+ templ.Handler(base("Oh noes!", errorPage(fmt.Sprintf("Access Denied: error code %s", hash))), templ.WithStatus(http.StatusOK)).ServeHTTP(w, r)
return
case config.RuleChallenge:
lg.Debug("challenge requested")
diff --git a/cmd/anubis/policy.go b/cmd/anubis/policy.go
index 6e2241e..421bfff 100644
--- a/cmd/anubis/policy.go
+++ b/cmd/anubis/policy.go
@@ -35,6 +35,19 @@ type Bot struct {
Action config.Rule `json:"action"`
}
+func (b Bot) Hash() (string, error) {
+ var pathRex string
+ if b.Path != nil {
+ pathRex = b.Path.String()
+ }
+ var userAgentRex string
+ if b.UserAgent != nil {
+ userAgentRex = b.UserAgent.String()
+ }
+
+ return sha256sum(fmt.Sprintf("%s::%s::%s", b.Name, pathRex, userAgentRex))
+}
+
func parseConfig(fin io.Reader, fname string) (*ParsedConfig, error) {
var c config.Config
if err := json.NewDecoder(fin).Decode(&c); err != nil {
@@ -110,20 +123,20 @@ func cr(name string, rule config.Rule) CheckResult {
}
// Check evaluates the list of rules, and returns the result
-func (s *Server) check(r *http.Request) CheckResult {
+func (s *Server) check(r *http.Request) (CheckResult, *Bot) {
for _, b := range s.policy.Bots {
if b.UserAgent != nil {
if b.UserAgent.MatchString(r.UserAgent()) {
- return cr("bot/"+b.Name, b.Action)
+ return cr("bot/"+b.Name, b.Action), &b
}
}
if b.Path != nil {
if b.Path.MatchString(r.URL.Path) {
- return cr("bot/"+b.Name, b.Action)
+ return cr("bot/"+b.Name, b.Action), &b
}
}
}
- return cr("default/allow", config.RuleAllow)
+ return cr("default/allow", config.RuleAllow), nil
}
diff --git a/cmd/anubis/static/js/main.mjs.gz b/cmd/anubis/static/js/main.mjs.gz
index 868c622..c104f77 100644
--- a/cmd/anubis/static/js/main.mjs.gz
+++ b/cmd/anubis/static/js/main.mjs.gz
Binary files differ
diff --git a/cmd/anubis/testdata/everything_blocked.json b/cmd/anubis/testdata/everything_blocked.json
new file mode 100644
index 0000000..e1763e4
--- /dev/null
+++ b/cmd/anubis/testdata/everything_blocked.json
@@ -0,0 +1,10 @@
+{
+ "bots": [
+ {
+ "name": "everything",
+ "user_agent_regex": ".*",
+ "action": "DENY"
+ }
+ ],
+ "dnsbl": false
+} \ No newline at end of file