diff options
| author | Xe Iaso <me@xeiaso.net> | 2025-01-18 23:29:06 -0500 |
|---|---|---|
| committer | Xe Iaso <me@xeiaso.net> | 2025-01-18 23:29:10 -0500 |
| commit | 10a208640deac5474ceb15c553cda1c17776fcfd (patch) | |
| tree | 0f70edc91495cf723983274e12ff68f5cbb06ba9 /cmd/anubis | |
| parent | 84b152afc083ff5421c76e1eb0b7eac9e0f20569 (diff) | |
| download | x-10a208640deac5474ceb15c553cda1c17776fcfd.tar.xz x-10a208640deac5474ceb15c553cda1c17776fcfd.zip | |
cmd/anubis: embed diagrams in README
Signed-off-by: Xe Iaso <me@xeiaso.net>
Diffstat (limited to 'cmd/anubis')
| -rw-r--r-- | cmd/anubis/README.md | 75 | ||||
| -rw-r--r-- | cmd/anubis/main.go | 13 |
2 files changed, 83 insertions, 5 deletions
diff --git a/cmd/anubis/README.md b/cmd/anubis/README.md index 2de4847..bbfe795 100644 --- a/cmd/anubis/README.md +++ b/cmd/anubis/README.md @@ -30,6 +30,39 @@ For live chat, please join the [Patreon](https://patreon.com/cadey) and ask in t Anubis uses a proof-of-work challenge to ensure that clients are using a modern browser and are able to calculate SHA-256 checksums. Anubis has a customizable difficulty for this proof-of-work challenge, but defaults to 5 leading zeroes. +```mermaid +--- +title: Challenge generation and validation +--- + +flowchart TD + Backend("Backend") + Fail("Fail") + + style PresentChallenge color:#FFFFFF, fill:#AA00FF, stroke:#AA00FF + style ValidateChallenge color:#FFFFFF, fill:#AA00FF, stroke:#AA00FF + style Backend color:#FFFFFF, stroke:#00C853, fill:#00C853 + style Fail color:#FFFFFF, stroke:#FF2962, fill:#FF2962 + + subgraph Server + PresentChallenge("Present Challenge") + ValidateChallenge("Validate Challenge") + end + + subgraph Client + Main("main.mjs") + Worker("Worker") + end + + Main -- Request challenge --> PresentChallenge + PresentChallenge -- Return challenge & difficulty --> Main + Main -- Spawn worker --> Worker + Worker -- Successful challenge --> Main + Main -- Validate challenge --> ValidateChallenge + ValidateChallenge -- Return cookie --> Backend + ValidateChallenge -- If anything is wrong --> Fail +``` + ### Challenge presentation Anubis decides to present a challenge using this logic: @@ -40,6 +73,48 @@ Anubis decides to present a challenge using this logic: This should ensure that git clients, RSS readers, and other low-harm clients can get through without issue, but high-risk clients such as browsers and AI scraper bots will get blocked. +```mermaid +--- +title: Challenge presentation logic +--- + +flowchart LR + Request("Request") + Backend("Backend") + %%Fail("Fail") + PresentChallenge("Present +challenge") + HasMozilla{"Is browser +or scraper?"} + HasCookie{"Has cookie?"} + HasExpired{"Cookie expired?"} + HasSignature{"Has valid +signature?"} + RandomJitter{"Secondary +screening?"} + POWPass{"Proof of +work valid?"} + + style PresentChallenge color:#FFFFFF, fill:#AA00FF, stroke:#AA00FF + style Backend color:#FFFFFF, stroke:#00C853, fill:#00C853 + %%style Fail color:#FFFFFF, stroke:#FF2962, fill:#FF2962 + + Request --> HasMozilla + HasMozilla -- Yes --> HasCookie + HasMozilla -- No --> Backend + HasCookie -- Yes --> HasExpired + HasCookie -- No --> PresentChallenge + HasExpired -- Yes --> PresentChallenge + HasExpired -- No --> HasSignature + HasSignature -- Yes --> RandomJitter + HasSignature -- No --> PresentChallenge + RandomJitter -- Yes --> POWPass + RandomJitter -- No --> Backend + POWPass -- Yes --> Backend + PowPass -- No --> PresentChallenge + PresentChallenge -- Back again for another cycle --> Request +``` + ### Proof of passing challenges When a client passes a challenge, Anubis sets an HTTP cookie named `"within.website-x-cmd-anubis-auth"` containing a signed [JWT](https://jwt.io/) (JSON Web Token). This JWT contains the following claims: diff --git a/cmd/anubis/main.go b/cmd/anubis/main.go index 2cef409..f3fa06b 100644 --- a/cmd/anubis/main.go +++ b/cmd/anubis/main.go @@ -202,6 +202,13 @@ func (s *Server) maybeReverseProxy(w http.ResponseWriter, r *http.Request) { return } + if ckie.Expires.Before(time.Now()) { + slog.Debug("cookie expired", "path", r.URL.Path) + clearCookie(w) + s.renderIndex(w, r) + return + } + token, err := jwt.ParseWithClaims(ckie.Value, jwt.MapClaims{}, func(token *jwt.Token) (interface{}, error) { return s.pub, nil }) @@ -244,11 +251,7 @@ func (s *Server) maybeReverseProxy(w http.ResponseWriter, r *http.Request) { if subtle.ConstantTimeCompare([]byte(claims["response"].(string)), []byte(calculated)) != 1 { slog.Debug("invalid response", "path", r.URL.Path) failedValidations.Inc() - http.SetCookie(w, &http.Cookie{ - Name: cookieName, - Value: "", - Expires: time.Now().Add(-1 * time.Hour), - }) + clearCookie(w) s.renderIndex(w, r) return } |
