diff --git a/README.md b/README.md index f0266eb..b8562fd 100644 --- a/README.md +++ b/README.md @@ -409,9 +409,6 @@ services: GOAWAY_CHALLENGE_TEMPLATE: forgejo GOAWAY_CHALLENGE_TEMPLATE_THEME: forgejo-dark - # specify a DNSBL for usage in conditions. Defaults to DroneBL - # GOAWAY_DNSBL: "dnsbl.dronebl.org" - # Backend to match. Can be subdomain or full wildcards, "*.example.com" or "*" GOAWAY_BACKEND: "git.example.com=http://forgejo:3000" @@ -426,9 +423,12 @@ services: ## Other Similar Projects -* [Anubis](https://anubis.techaro.lol/): Proxy that uses JavaScript proof of work to weight request based on rules [[source]](https://github.com/TecharoHQ/anubis) -* [powxy](https://sr.ht/~runxiyu/powxy/): Powxy is a reverse proxy that protects your upstream service by challenging clients with SHA-256 proof-of-work. [[source](https://git.sr.ht/~runxiyu/powxy)] -* [anticrawl](https://flak.tedunangst.com/post/anticrawl): Go http handler / proxy for regex based rules [[source]](https://humungus.tedunangst.com/r/anticrawl) + +| Project | Forge | Description | +|:-------------------------------------------------------:|:-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------:|:----------------------------------------------------------------------------------------------------------------| +| [Anubis](https://anubis.techaro.lol/) | [![GitHub](https://img.shields.io/badge/GitHub-TecharoHQ/anubis-blue?style=flat&logo=github&labelColor=fff&logoColor=24292f)](https://github.com/TecharoHQ/anubis)
Go / [MIT](https://github.com/TecharoHQ/anubis/blob/main/LICENSE) | Proxy that uses JavaScript proof of work to weight request based on simple match rules | +| [powxy](https://sr.ht/~runxiyu/powxy/) | [![sourcehut](https://img.shields.io/badge/sourcehut-~runxiyu/powxy-blue?style=flat&logo=sourcehut&labelColor=fff&logoColor=000)](https://git.sr.ht/~runxiyu/powxy)
Go / [BSD 2-Clause](https://git.sr.ht/~runxiyu/powxy/tree/master/item/LICENSE) | Powxy is a reverse proxy that protects your upstream service by challenging clients with SHA-256 proof-of-work. | +| [anticrawl](https://flak.tedunangst.com/post/anticrawl) | [[source]](https://humungus.tedunangst.com/r/anticrawl)
Go / None | Go http handler / proxy for regex based rules | ## Development diff --git a/lib/challenge/cookie/cookie.go b/lib/challenge/cookie/cookie.go index 16f0ab0..daa5933 100644 --- a/lib/challenge/cookie/cookie.go +++ b/lib/challenge/cookie/cookie.go @@ -23,7 +23,7 @@ func FillRegistration(state challenge.StateInterface, reg *challenge.Registratio return challenge.VerifyResultFail } - utils.SetCookie(utils.CookiePrefix+reg.Name, token, expiry, w, r) + utils.SetCookie(challenge.RequestDataFromContext(r.Context()).CookiePrefix+reg.Name, token, expiry, w, r) uri, err := challenge.RedirectUrl(r, reg) if err != nil { diff --git a/lib/challenge/data.go b/lib/challenge/data.go index a7a0c59..ef1ac3a 100644 --- a/lib/challenge/data.go +++ b/lib/challenge/data.go @@ -3,6 +3,7 @@ package challenge import ( "context" "crypto/rand" + "crypto/sha256" "encoding/hex" "errors" "fmt" @@ -37,6 +38,7 @@ type RequestData struct { ChallengeState map[Id]VerifyState RemoteAddress net.IP State StateInterface + CookiePrefix string r *http.Request @@ -75,6 +77,13 @@ func CreateRequestData(r *http.Request, state StateInterface) (*http.Request, *R data.query = condition.NewValuesMap(r.URL.Query()) data.header = condition.NewMIMEMap(textproto.MIMEHeader(r.Header)) + sum := sha256.New() + sum.Write([]byte(r.Host)) + sum.Write([]byte{0}) + sum.Write(state.PublicKey()) + sum.Write([]byte{0}) + data.CookiePrefix = utils.CookiePrefix + hex.EncodeToString(sum.Sum(nil)[:4]) + "-" + r = r.WithContext(context.WithValue(r.Context(), requestDataContextKey{}, &data)) return r, &data @@ -118,7 +127,7 @@ func (d *RequestData) EvaluateChallenges(w http.ResponseWriter, r *http.Request) verifyResult, verifyState, err := reg.VerifyChallengeToken(d.State.PublicKey(), key, r) if err != nil && !errors.Is(err, http.ErrNoCookie) { // clear invalid cookie - utils.ClearCookie(utils.CookiePrefix+reg.Name, w, r) + utils.ClearCookie(d.CookiePrefix+reg.Name, w, r) } // prevent evaluating the challenge if not solved diff --git a/lib/challenge/dnsbl/dnsbl.go b/lib/challenge/dnsbl/dnsbl.go index df6c80c..634214b 100644 --- a/lib/challenge/dnsbl/dnsbl.go +++ b/lib/challenge/dnsbl/dnsbl.go @@ -133,14 +133,14 @@ func FillRegistration(state challenge.StateInterface, reg *challenge.Registratio if err != nil { return challenge.VerifyResultFail } - utils.SetCookie(utils.CookiePrefix+reg.Name, token, expiry, w, r) + utils.SetCookie(data.CookiePrefix+reg.Name, token, expiry, w, r) return challenge.VerifyResultNotOK } else { token, err := reg.IssueChallengeToken(state.PrivateKey(), key, nil, expiry, true) if err != nil { return challenge.VerifyResultFail } - utils.SetCookie(utils.CookiePrefix+reg.Name, token, expiry, w, r) + utils.SetCookie(data.CookiePrefix+reg.Name, token, expiry, w, r) return challenge.VerifyResultOK } } diff --git a/lib/challenge/helper.go b/lib/challenge/helper.go index 5239a9a..cc66164 100644 --- a/lib/challenge/helper.go +++ b/lib/challenge/helper.go @@ -138,7 +138,7 @@ func VerifyHandlerFunc(state StateInterface, reg *Registration, verify VerifyFun if err != nil { return err } else if !verifyResult.Ok() { - utils.ClearCookie(utils.CookiePrefix+reg.Name, w, r) + utils.ClearCookie(data.CookiePrefix+reg.Name, w, r) state.ChallengeFailed(r, reg, nil, redirect, nil) responseFunc(state, data, w, r, verifyResult, nil, redirect) return nil @@ -146,9 +146,9 @@ func VerifyHandlerFunc(state StateInterface, reg *Registration, verify VerifyFun challengeToken, err := reg.IssueChallengeToken(state.PrivateKey(), key, []byte(token), expiration, true) if err != nil { - utils.ClearCookie(utils.CookiePrefix+reg.Name, w, r) + utils.ClearCookie(data.CookiePrefix+reg.Name, w, r) } else { - utils.SetCookie(utils.CookiePrefix+reg.Name, challengeToken, expiration, w, r) + utils.SetCookie(data.CookiePrefix+reg.Name, challengeToken, expiration, w, r) } data.ChallengeVerify[reg.id] = verifyResult state.ChallengePassed(r, reg, redirect, nil) @@ -157,7 +157,7 @@ func VerifyHandlerFunc(state StateInterface, reg *Registration, verify VerifyFun return nil }() if err != nil { - utils.ClearCookie(utils.CookiePrefix+reg.Name, w, r) + utils.ClearCookie(data.CookiePrefix+reg.Name, w, r) state.ChallengeFailed(r, reg, err, redirect, nil) responseFunc(state, data, w, r, VerifyResultFail, fmt.Errorf("access denied: error in challenge %s: %w", reg.Name, err), redirect) return diff --git a/lib/challenge/http/http.go b/lib/challenge/http/http.go index 78472ca..b54a4b6 100644 --- a/lib/challenge/http/http.go +++ b/lib/challenge/http/http.go @@ -137,19 +137,21 @@ func FillRegistration(state challenge.StateInterface, reg *challenge.Registratio defer response.Body.Close() defer io.Copy(io.Discard, response.Body) + data := challenge.RequestDataFromContext(r.Context()) + if response.StatusCode != params.HttpCode { token, err := reg.IssueChallengeToken(state.PrivateKey(), key, sum, expiry, false) if err != nil { return challenge.VerifyResultFail } - utils.SetCookie(utils.CookiePrefix+reg.Name, token, expiry, w, r) + utils.SetCookie(data.CookiePrefix+reg.Name, token, expiry, w, r) return challenge.VerifyResultNotOK } else { token, err := reg.IssueChallengeToken(state.PrivateKey(), key, sum, expiry, true) if err != nil { return challenge.VerifyResultFail } - utils.SetCookie(utils.CookiePrefix+reg.Name, token, expiry, w, r) + utils.SetCookie(data.CookiePrefix+reg.Name, token, expiry, w, r) return challenge.VerifyResultOK } } diff --git a/lib/challenge/register.go b/lib/challenge/register.go index 9aebece..054248a 100644 --- a/lib/challenge/register.go +++ b/lib/challenge/register.go @@ -7,7 +7,6 @@ import ( "fmt" "git.gammaspectra.live/git/go-away/lib/condition" "git.gammaspectra.live/git/go-away/lib/policy" - "git.gammaspectra.live/git/go-away/utils" "github.com/go-jose/go-jose/v4" "github.com/go-jose/go-jose/v4/jwt" "github.com/goccy/go-yaml/ast" @@ -193,7 +192,7 @@ var ErrVerifyVerifyMismatch = errors.New("verify: verification mismatch") var ErrTokenExpired = errors.New("token: expired") func (reg Registration) VerifyChallengeToken(publicKey ed25519.PublicKey, expectedKey Key, r *http.Request) (VerifyResult, VerifyState, error) { - cookie, err := r.Cookie(utils.CookiePrefix + reg.Name) + cookie, err := r.Cookie(RequestDataFromContext(r.Context()).CookiePrefix + reg.Name) if err != nil { return VerifyResultNone, VerifyStateNone, err }