mirror of
https://github.com/TecharoHQ/anubis.git
synced 2025-08-03 01:38:14 -04:00
fix: Dynamic cookie domain not working (#731)
* Fix cookieDynamicDomain option not being set in Options struct * Fix using wrong cookie name when using dynamic cookie domains * Adjust testcases for new cookie option structs * Add known words to expect.txt and change typo in Zombocom * Cleanup expect.txt * Add changes to changelog * Bump versions of grpc and apimachinery * Fix testcases and add additional condition for dynamic cookie domain
This commit is contained in:
parent
b1edf84a7c
commit
6aa17532da
7
.github/actions/spelling/expect.txt
vendored
7
.github/actions/spelling/expect.txt
vendored
@ -42,7 +42,6 @@ cgr
|
|||||||
chainguard
|
chainguard
|
||||||
chall
|
chall
|
||||||
challengemozilla
|
challengemozilla
|
||||||
Chargement
|
|
||||||
checkpath
|
checkpath
|
||||||
checkresult
|
checkresult
|
||||||
chibi
|
chibi
|
||||||
@ -54,6 +53,7 @@ containerbuild
|
|||||||
coreutils
|
coreutils
|
||||||
Cotoyogi
|
Cotoyogi
|
||||||
CRDs
|
CRDs
|
||||||
|
Cromite
|
||||||
crt
|
crt
|
||||||
Cscript
|
Cscript
|
||||||
daemonizing
|
daemonizing
|
||||||
@ -75,6 +75,7 @@ droneblresponse
|
|||||||
duckduckbot
|
duckduckbot
|
||||||
eerror
|
eerror
|
||||||
ellenjoe
|
ellenjoe
|
||||||
|
emacs
|
||||||
enbyware
|
enbyware
|
||||||
etld
|
etld
|
||||||
everyones
|
everyones
|
||||||
@ -166,7 +167,6 @@ Linting
|
|||||||
linuxbrew
|
linuxbrew
|
||||||
LLU
|
LLU
|
||||||
loadbalancer
|
loadbalancer
|
||||||
locahost
|
|
||||||
lol
|
lol
|
||||||
LOMINSA
|
LOMINSA
|
||||||
maintainership
|
maintainership
|
||||||
@ -283,6 +283,7 @@ unparseable
|
|||||||
uuidgen
|
uuidgen
|
||||||
uvx
|
uvx
|
||||||
UXP
|
UXP
|
||||||
|
Valkey
|
||||||
Varis
|
Varis
|
||||||
Velen
|
Velen
|
||||||
vendored
|
vendored
|
||||||
@ -320,5 +321,5 @@ yourdomain
|
|||||||
yoursite
|
yoursite
|
||||||
Zenos
|
Zenos
|
||||||
zizmor
|
zizmor
|
||||||
Zonbocom
|
zombocom
|
||||||
zos
|
zos
|
||||||
|
@ -13,9 +13,6 @@ var Version = "devel"
|
|||||||
// access.
|
// access.
|
||||||
const CookieName = "techaro.lol-anubis-auth"
|
const CookieName = "techaro.lol-anubis-auth"
|
||||||
|
|
||||||
// WithDomainCookieName is the name that is prepended to the per-domain cookie used when COOKIE_DOMAIN is set.
|
|
||||||
const WithDomainCookieName = "techaro.lol-anubis-auth-for-"
|
|
||||||
|
|
||||||
const TestCookieName = "techaro.lol-anubis-cookie-test-if-you-block-this-anubis-wont-work"
|
const TestCookieName = "techaro.lol-anubis-cookie-test-if-you-block-this-anubis-wont-work"
|
||||||
|
|
||||||
// CookieDefaultExpirationTime is the amount of time before the cookie/JWT expires.
|
// CookieDefaultExpirationTime is the amount of time before the cookie/JWT expires.
|
||||||
|
@ -384,20 +384,21 @@ func main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
s, err := libanubis.New(libanubis.Options{
|
s, err := libanubis.New(libanubis.Options{
|
||||||
BasePrefix: *basePrefix,
|
BasePrefix: *basePrefix,
|
||||||
StripBasePrefix: *stripBasePrefix,
|
StripBasePrefix: *stripBasePrefix,
|
||||||
Next: rp,
|
Next: rp,
|
||||||
Policy: policy,
|
Policy: policy,
|
||||||
ServeRobotsTXT: *robotsTxt,
|
ServeRobotsTXT: *robotsTxt,
|
||||||
ED25519PrivateKey: ed25519Priv,
|
ED25519PrivateKey: ed25519Priv,
|
||||||
HS512Secret: []byte(*hs512Secret),
|
HS512Secret: []byte(*hs512Secret),
|
||||||
CookieDomain: *cookieDomain,
|
CookieDomain: *cookieDomain,
|
||||||
CookieExpiration: *cookieExpiration,
|
CookieDynamicDomain: *cookieDynamicDomain,
|
||||||
CookiePartitioned: *cookiePartitioned,
|
CookieExpiration: *cookieExpiration,
|
||||||
RedirectDomains: redirectDomainsList,
|
CookiePartitioned: *cookiePartitioned,
|
||||||
Target: *target,
|
RedirectDomains: redirectDomainsList,
|
||||||
WebmasterEmail: *webmasterEmail,
|
Target: *target,
|
||||||
OpenGraph: policy.OpenGraph,
|
WebmasterEmail: *webmasterEmail,
|
||||||
|
OpenGraph: policy.OpenGraph,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("can't construct libanubis.Server: %v", err)
|
log.Fatalf("can't construct libanubis.Server: %v", err)
|
||||||
|
@ -88,7 +88,7 @@ dnsbl: false
|
|||||||
# impressum:
|
# impressum:
|
||||||
# # Displayed at the bottom of every page rendered by Anubis.
|
# # Displayed at the bottom of every page rendered by Anubis.
|
||||||
# footer: >-
|
# footer: >-
|
||||||
# This website is hosted by Zonbocom. If you have any complaints or notes
|
# This website is hosted by Zombocom. If you have any complaints or notes
|
||||||
# about the service, please contact
|
# about the service, please contact
|
||||||
# <a href="mailto:contact@domainhere.example">contact@domainhere.example</a>
|
# <a href="mailto:contact@domainhere.example">contact@domainhere.example</a>
|
||||||
# and we will assist you as soon as possible.
|
# and we will assist you as soon as possible.
|
||||||
|
@ -13,6 +13,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
|
|
||||||
- Determine the `BIND_NETWORK`/`--bind-network` value from the bind address ([#677](https://github.com/TecharoHQ/anubis/issues/677))
|
- Determine the `BIND_NETWORK`/`--bind-network` value from the bind address ([#677](https://github.com/TecharoHQ/anubis/issues/677))
|
||||||
- Implement localization system. Find locale files in lib/localization/locales/.
|
- Implement localization system. Find locale files in lib/localization/locales/.
|
||||||
|
- Fix dynamic cookie domains functionality ([#731](https://github.com/TecharoHQ/anubis/pull/731))
|
||||||
|
|
||||||
## v1.20.0: Thancred Waters
|
## v1.20.0: Thancred Waters
|
||||||
|
|
||||||
|
@ -69,7 +69,6 @@ type Server struct {
|
|||||||
policy *policy.ParsedConfig
|
policy *policy.ParsedConfig
|
||||||
DNSBLCache *decaymap.Impl[string, dnsbl.DroneBLResponse]
|
DNSBLCache *decaymap.Impl[string, dnsbl.DroneBLResponse]
|
||||||
OGTags *ogtags.OGTagCache
|
OGTags *ogtags.OGTagCache
|
||||||
cookieName string
|
|
||||||
ed25519Priv ed25519.PrivateKey
|
ed25519Priv ed25519.PrivateKey
|
||||||
hs512Secret []byte
|
hs512Secret []byte
|
||||||
opts Options
|
opts Options
|
||||||
@ -88,8 +87,6 @@ func (s *Server) getTokenKeyfunc() jwt.Keyfunc {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
func (s *Server) challengeFor(r *http.Request, difficulty int) string {
|
func (s *Server) challengeFor(r *http.Request, difficulty int) string {
|
||||||
var fp [32]byte
|
var fp [32]byte
|
||||||
if len(s.hs512Secret) == 0 {
|
if len(s.hs512Secret) == 0 {
|
||||||
@ -149,24 +146,24 @@ func (s *Server) maybeReverseProxy(w http.ResponseWriter, r *http.Request, httpS
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
ckie, err := r.Cookie(s.cookieName)
|
ckie, err := r.Cookie(anubis.CookieName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
lg.Debug("cookie not found", "path", r.URL.Path)
|
lg.Debug("cookie not found", "path", r.URL.Path)
|
||||||
s.ClearCookie(w, s.cookieName, cookiePath, r.Host)
|
s.ClearCookie(w, CookieOpts{Path: cookiePath, Host: r.Host})
|
||||||
s.RenderIndex(w, r, rule, httpStatusOnly)
|
s.RenderIndex(w, r, rule, httpStatusOnly)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := ckie.Valid(); err != nil {
|
if err := ckie.Valid(); err != nil {
|
||||||
lg.Debug("cookie is invalid", "err", err)
|
lg.Debug("cookie is invalid", "err", err)
|
||||||
s.ClearCookie(w, s.cookieName, cookiePath, r.Host)
|
s.ClearCookie(w, CookieOpts{Path: cookiePath, Host: r.Host})
|
||||||
s.RenderIndex(w, r, rule, httpStatusOnly)
|
s.RenderIndex(w, r, rule, httpStatusOnly)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if time.Now().After(ckie.Expires) && !ckie.Expires.IsZero() {
|
if time.Now().After(ckie.Expires) && !ckie.Expires.IsZero() {
|
||||||
lg.Debug("cookie expired", "path", r.URL.Path)
|
lg.Debug("cookie expired", "path", r.URL.Path)
|
||||||
s.ClearCookie(w, s.cookieName, cookiePath, r.Host)
|
s.ClearCookie(w, CookieOpts{Path: cookiePath, Host: r.Host})
|
||||||
s.RenderIndex(w, r, rule, httpStatusOnly)
|
s.RenderIndex(w, r, rule, httpStatusOnly)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -175,7 +172,7 @@ func (s *Server) maybeReverseProxy(w http.ResponseWriter, r *http.Request, httpS
|
|||||||
|
|
||||||
if err != nil || !token.Valid {
|
if err != nil || !token.Valid {
|
||||||
lg.Debug("invalid token", "path", r.URL.Path, "err", err)
|
lg.Debug("invalid token", "path", r.URL.Path, "err", err)
|
||||||
s.ClearCookie(w, s.cookieName, cookiePath, r.Host)
|
s.ClearCookie(w, CookieOpts{Path: cookiePath, Host: r.Host})
|
||||||
s.RenderIndex(w, r, rule, httpStatusOnly)
|
s.RenderIndex(w, r, rule, httpStatusOnly)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -183,7 +180,7 @@ func (s *Server) maybeReverseProxy(w http.ResponseWriter, r *http.Request, httpS
|
|||||||
claims, ok := token.Claims.(jwt.MapClaims)
|
claims, ok := token.Claims.(jwt.MapClaims)
|
||||||
if !ok {
|
if !ok {
|
||||||
lg.Debug("invalid token claims type", "path", r.URL.Path)
|
lg.Debug("invalid token claims type", "path", r.URL.Path)
|
||||||
s.ClearCookie(w, s.cookieName, cookiePath, r.Host)
|
s.ClearCookie(w, CookieOpts{Path: cookiePath, Host: r.Host})
|
||||||
s.RenderIndex(w, r, rule, httpStatusOnly)
|
s.RenderIndex(w, r, rule, httpStatusOnly)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -191,14 +188,14 @@ func (s *Server) maybeReverseProxy(w http.ResponseWriter, r *http.Request, httpS
|
|||||||
policyRule, ok := claims["policyRule"].(string)
|
policyRule, ok := claims["policyRule"].(string)
|
||||||
if !ok {
|
if !ok {
|
||||||
lg.Debug("policyRule claim is not a string")
|
lg.Debug("policyRule claim is not a string")
|
||||||
s.ClearCookie(w, s.cookieName, cookiePath, r.Host)
|
s.ClearCookie(w, CookieOpts{Path: cookiePath, Host: r.Host})
|
||||||
s.RenderIndex(w, r, rule, httpStatusOnly)
|
s.RenderIndex(w, r, rule, httpStatusOnly)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if policyRule != rule.Hash() {
|
if policyRule != rule.Hash() {
|
||||||
lg.Debug("user originally passed with a different rule, issuing new challenge", "old", policyRule, "new", rule.Name)
|
lg.Debug("user originally passed with a different rule, issuing new challenge", "old", policyRule, "new", rule.Name)
|
||||||
s.ClearCookie(w, s.cookieName, cookiePath, r.Host)
|
s.ClearCookie(w, CookieOpts{Path: cookiePath, Host: r.Host})
|
||||||
s.RenderIndex(w, r, rule, httpStatusOnly)
|
s.RenderIndex(w, r, rule, httpStatusOnly)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -222,7 +219,7 @@ func (s *Server) checkRules(w http.ResponseWriter, r *http.Request, cr policy.Ch
|
|||||||
s.ServeHTTPNext(w, r)
|
s.ServeHTTPNext(w, r)
|
||||||
return true
|
return true
|
||||||
case config.RuleDeny:
|
case config.RuleDeny:
|
||||||
s.ClearCookie(w, s.cookieName, cookiePath, r.Host)
|
s.ClearCookie(w, CookieOpts{Path: cookiePath, Host: r.Host})
|
||||||
lg.Info("explicit deny")
|
lg.Info("explicit deny")
|
||||||
if rule == nil {
|
if rule == nil {
|
||||||
lg.Error("rule is nil, cannot calculate checksum")
|
lg.Error("rule is nil, cannot calculate checksum")
|
||||||
@ -241,7 +238,7 @@ func (s *Server) checkRules(w http.ResponseWriter, r *http.Request, cr policy.Ch
|
|||||||
s.RenderBench(w, r)
|
s.RenderBench(w, r)
|
||||||
return true
|
return true
|
||||||
default:
|
default:
|
||||||
s.ClearCookie(w, s.cookieName, cookiePath, r.Host)
|
s.ClearCookie(w, CookieOpts{Path: cookiePath, Host: r.Host})
|
||||||
slog.Error("CONFIG ERROR: unknown rule", "rule", cr.Rule)
|
slog.Error("CONFIG ERROR: unknown rule", "rule", cr.Rule)
|
||||||
s.respondWithError(w, r, fmt.Sprintf("%s \"maybeReverseProxy.Rules\"", localizer.T("internal_server_error")))
|
s.respondWithError(w, r, fmt.Sprintf("%s \"maybeReverseProxy.Rules\"", localizer.T("internal_server_error")))
|
||||||
return true
|
return true
|
||||||
@ -265,10 +262,10 @@ func (s *Server) handleDNSBL(w http.ResponseWriter, r *http.Request, ip string,
|
|||||||
if resp != dnsbl.AllGood {
|
if resp != dnsbl.AllGood {
|
||||||
lg.Info("DNSBL hit", "status", resp.String())
|
lg.Info("DNSBL hit", "status", resp.String())
|
||||||
localizer := localization.GetLocalizer(r)
|
localizer := localization.GetLocalizer(r)
|
||||||
s.respondWithStatus(w, r, fmt.Sprintf("%s: %s, %s https://dronebl.org/lookup?ip=%s",
|
s.respondWithStatus(w, r, fmt.Sprintf("%s: %s, %s https://dronebl.org/lookup?ip=%s",
|
||||||
localizer.T("dronebl_entry"),
|
localizer.T("dronebl_entry"),
|
||||||
resp.String(),
|
resp.String(),
|
||||||
localizer.T("see_dronebl_lookup"),
|
localizer.T("see_dronebl_lookup"),
|
||||||
ip), s.policy.StatusCodes.Deny)
|
ip), s.policy.StatusCodes.Deny)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
@ -314,7 +311,7 @@ func (s *Server) MakeChallenge(w http.ResponseWriter, r *http.Request) {
|
|||||||
lg = lg.With("check_result", cr)
|
lg = lg.With("check_result", cr)
|
||||||
chal := s.challengeFor(r, rule.Challenge.Difficulty)
|
chal := s.challengeFor(r, rule.Challenge.Difficulty)
|
||||||
|
|
||||||
s.SetCookie(w, anubis.TestCookieName, chal, "/", r.Host)
|
s.SetCookie(w, CookieOpts{Host: r.Host, Name: anubis.TestCookieName, Value: chal})
|
||||||
|
|
||||||
err = encoder.Encode(struct {
|
err = encoder.Encode(struct {
|
||||||
Rules *config.ChallengeRules `json:"rules"`
|
Rules *config.ChallengeRules `json:"rules"`
|
||||||
@ -343,14 +340,14 @@ func (s *Server) PassChallenge(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if _, err := r.Cookie(anubis.TestCookieName); errors.Is(err, http.ErrNoCookie) {
|
if _, err := r.Cookie(anubis.TestCookieName); errors.Is(err, http.ErrNoCookie) {
|
||||||
s.ClearCookie(w, s.cookieName, cookiePath, r.Host)
|
s.ClearCookie(w, CookieOpts{Path: cookiePath, Host: r.Host})
|
||||||
s.ClearCookie(w, anubis.TestCookieName, "/", r.Host)
|
s.ClearCookie(w, CookieOpts{Name: anubis.TestCookieName, Host: r.Host})
|
||||||
lg.Warn("user has cookies disabled, this is not an anubis bug")
|
lg.Warn("user has cookies disabled, this is not an anubis bug")
|
||||||
s.respondWithError(w, r, localizer.T("cookies_disabled"))
|
s.respondWithError(w, r, localizer.T("cookies_disabled"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
s.ClearCookie(w, anubis.TestCookieName, "/", r.Host)
|
s.ClearCookie(w, CookieOpts{Name: anubis.TestCookieName, Host: r.Host})
|
||||||
|
|
||||||
redir := r.FormValue("redir")
|
redir := r.FormValue("redir")
|
||||||
redirURL, err := url.ParseRequestURI(redir)
|
redirURL, err := url.ParseRequestURI(redir)
|
||||||
@ -392,7 +389,7 @@ func (s *Server) PassChallenge(w http.ResponseWriter, r *http.Request) {
|
|||||||
if err := impl.Validate(r, lg, rule, challengeStr); err != nil {
|
if err := impl.Validate(r, lg, rule, challengeStr); err != nil {
|
||||||
failedValidations.WithLabelValues(rule.Challenge.Algorithm).Inc()
|
failedValidations.WithLabelValues(rule.Challenge.Algorithm).Inc()
|
||||||
var cerr *challenge.Error
|
var cerr *challenge.Error
|
||||||
s.ClearCookie(w, s.cookieName, cookiePath, r.Host)
|
s.ClearCookie(w, CookieOpts{Path: cookiePath, Host: r.Host})
|
||||||
lg.Debug("challenge validate call failed", "err", err)
|
lg.Debug("challenge validate call failed", "err", err)
|
||||||
|
|
||||||
switch {
|
switch {
|
||||||
@ -415,12 +412,12 @@ func (s *Server) PassChallenge(w http.ResponseWriter, r *http.Request) {
|
|||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
lg.Error("failed to sign JWT", "err", err)
|
lg.Error("failed to sign JWT", "err", err)
|
||||||
s.ClearCookie(w, s.cookieName, cookiePath, r.Host)
|
s.ClearCookie(w, CookieOpts{Path: cookiePath, Host: r.Host})
|
||||||
s.respondWithError(w, r, localizer.T("failed_to_sign_jwt"))
|
s.respondWithError(w, r, localizer.T("failed_to_sign_jwt"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
s.SetCookie(w, s.cookieName, tokenString, cookiePath, r.Host)
|
s.SetCookie(w, CookieOpts{Path: cookiePath, Host: r.Host, Value: tokenString})
|
||||||
|
|
||||||
challengesValidated.WithLabelValues(rule.Challenge.Algorithm).Inc()
|
challengesValidated.WithLabelValues(rule.Challenge.Algorithm).Inc()
|
||||||
lg.Debug("challenge passed, redirecting to app")
|
lg.Debug("challenge passed, redirecting to app")
|
||||||
|
@ -189,8 +189,6 @@ func TestCVE2025_24369(t *testing.T) {
|
|||||||
srv := spawnAnubis(t, Options{
|
srv := spawnAnubis(t, Options{
|
||||||
Next: http.NewServeMux(),
|
Next: http.NewServeMux(),
|
||||||
Policy: pol,
|
Policy: pol,
|
||||||
|
|
||||||
CookieName: t.Name(),
|
|
||||||
})
|
})
|
||||||
|
|
||||||
ts := httptest.NewServer(internal.RemoteXRealIP(true, "tcp", srv))
|
ts := httptest.NewServer(internal.RemoteXRealIP(true, "tcp", srv))
|
||||||
@ -235,13 +233,13 @@ func TestCookieCustomExpiration(t *testing.T) {
|
|||||||
var ckie *http.Cookie
|
var ckie *http.Cookie
|
||||||
for _, cookie := range resp.Cookies() {
|
for _, cookie := range resp.Cookies() {
|
||||||
t.Logf("%#v", cookie)
|
t.Logf("%#v", cookie)
|
||||||
if cookie.Name == srv.cookieName {
|
if cookie.Name == anubis.CookieName {
|
||||||
ckie = cookie
|
ckie = cookie
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ckie == nil {
|
if ckie == nil {
|
||||||
t.Errorf("Cookie %q not found", srv.cookieName)
|
t.Errorf("Cookie %q not found", anubis.CookieName)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -264,7 +262,6 @@ func TestCookieSettings(t *testing.T) {
|
|||||||
|
|
||||||
CookieDomain: "127.0.0.1",
|
CookieDomain: "127.0.0.1",
|
||||||
CookiePartitioned: true,
|
CookiePartitioned: true,
|
||||||
CookieName: t.Name(),
|
|
||||||
CookieExpiration: anubis.CookieDefaultExpirationTime,
|
CookieExpiration: anubis.CookieDefaultExpirationTime,
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -286,13 +283,13 @@ func TestCookieSettings(t *testing.T) {
|
|||||||
var ckie *http.Cookie
|
var ckie *http.Cookie
|
||||||
for _, cookie := range resp.Cookies() {
|
for _, cookie := range resp.Cookies() {
|
||||||
t.Logf("%#v", cookie)
|
t.Logf("%#v", cookie)
|
||||||
if cookie.Name == srv.cookieName {
|
if cookie.Name == anubis.CookieName {
|
||||||
ckie = cookie
|
ckie = cookie
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ckie == nil {
|
if ckie == nil {
|
||||||
t.Errorf("Cookie %q not found", srv.cookieName)
|
t.Errorf("Cookie %q not found", anubis.CookieName)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -619,7 +616,6 @@ func TestRuleChange(t *testing.T) {
|
|||||||
Policy: pol,
|
Policy: pol,
|
||||||
|
|
||||||
CookieDomain: "127.0.0.1",
|
CookieDomain: "127.0.0.1",
|
||||||
CookieName: t.Name(),
|
|
||||||
CookieExpiration: ckieExpiration,
|
CookieExpiration: ckieExpiration,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -35,7 +35,6 @@ type Options struct {
|
|||||||
CookieDynamicDomain bool
|
CookieDynamicDomain bool
|
||||||
CookieDomain string
|
CookieDomain string
|
||||||
CookieExpiration time.Duration
|
CookieExpiration time.Duration
|
||||||
CookieName string
|
|
||||||
CookiePartitioned bool
|
CookiePartitioned bool
|
||||||
BasePrefix string
|
BasePrefix string
|
||||||
WebmasterEmail string
|
WebmasterEmail string
|
||||||
@ -102,12 +101,6 @@ func New(opts Options) (*Server, error) {
|
|||||||
|
|
||||||
anubis.BasePrefix = opts.BasePrefix
|
anubis.BasePrefix = opts.BasePrefix
|
||||||
|
|
||||||
cookieName := anubis.CookieName
|
|
||||||
|
|
||||||
if opts.CookieDomain != "" {
|
|
||||||
cookieName = anubis.WithDomainCookieName + opts.CookieDomain
|
|
||||||
}
|
|
||||||
|
|
||||||
result := &Server{
|
result := &Server{
|
||||||
next: opts.Next,
|
next: opts.Next,
|
||||||
ed25519Priv: opts.ED25519PrivateKey,
|
ed25519Priv: opts.ED25519PrivateKey,
|
||||||
@ -116,7 +109,6 @@ func New(opts Options) (*Server, error) {
|
|||||||
opts: opts,
|
opts: opts,
|
||||||
DNSBLCache: decaymap.New[string, dnsbl.DroneBLResponse](),
|
DNSBLCache: decaymap.New[string, dnsbl.DroneBLResponse](),
|
||||||
OGTags: ogtags.NewOGTagCache(opts.Target, opts.Policy.OpenGraph),
|
OGTags: ogtags.NewOGTagCache(opts.Target, opts.Policy.OpenGraph),
|
||||||
cookieName: cookieName,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
mux := http.NewServeMux()
|
mux := http.NewServeMux()
|
||||||
|
39
lib/http.go
39
lib/http.go
@ -22,18 +22,32 @@ import (
|
|||||||
|
|
||||||
var domainMatchRegexp = regexp.MustCompile(`^((xn--)?[a-z0-9]+(-[a-z0-9]+)*\.)+[a-z]{2,}$`)
|
var domainMatchRegexp = regexp.MustCompile(`^((xn--)?[a-z0-9]+(-[a-z0-9]+)*\.)+[a-z]{2,}$`)
|
||||||
|
|
||||||
func (s *Server) SetCookie(w http.ResponseWriter, name, value, path, host string) {
|
type CookieOpts struct {
|
||||||
|
Value string
|
||||||
|
Host string
|
||||||
|
Path string
|
||||||
|
Name string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) SetCookie(w http.ResponseWriter, cookieOpts CookieOpts) {
|
||||||
var domain = s.opts.CookieDomain
|
var domain = s.opts.CookieDomain
|
||||||
if s.opts.CookieDynamicDomain && domainMatchRegexp.MatchString(host) {
|
var name = anubis.CookieName
|
||||||
if etld, err := publicsuffix.EffectiveTLDPlusOne(host); err == nil {
|
var path = "/"
|
||||||
|
if cookieOpts.Name != "" {
|
||||||
|
name = cookieOpts.Name
|
||||||
|
}
|
||||||
|
if cookieOpts.Path != "" {
|
||||||
|
path = cookieOpts.Path
|
||||||
|
}
|
||||||
|
if s.opts.CookieDynamicDomain && domainMatchRegexp.MatchString(cookieOpts.Host) {
|
||||||
|
if etld, err := publicsuffix.EffectiveTLDPlusOne(cookieOpts.Host); err == nil {
|
||||||
domain = etld
|
domain = etld
|
||||||
name = anubis.WithDomainCookieName + etld
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
http.SetCookie(w, &http.Cookie{
|
http.SetCookie(w, &http.Cookie{
|
||||||
Name: name,
|
Name: name,
|
||||||
Value: value,
|
Value: cookieOpts.Value,
|
||||||
Expires: time.Now().Add(s.opts.CookieExpiration),
|
Expires: time.Now().Add(s.opts.CookieExpiration),
|
||||||
SameSite: http.SameSiteLaxMode,
|
SameSite: http.SameSiteLaxMode,
|
||||||
Domain: domain,
|
Domain: domain,
|
||||||
@ -42,12 +56,19 @@ func (s *Server) SetCookie(w http.ResponseWriter, name, value, path, host string
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) ClearCookie(w http.ResponseWriter, name, path, host string) {
|
func (s *Server) ClearCookie(w http.ResponseWriter, cookieOpts CookieOpts) {
|
||||||
var domain = s.opts.CookieDomain
|
var domain = s.opts.CookieDomain
|
||||||
if s.opts.CookieDynamicDomain && domainMatchRegexp.MatchString(host) {
|
var name = anubis.CookieName
|
||||||
if etld, err := publicsuffix.EffectiveTLDPlusOne(host); err == nil {
|
var path = "/"
|
||||||
|
if cookieOpts.Name != "" {
|
||||||
|
name = cookieOpts.Name
|
||||||
|
}
|
||||||
|
if cookieOpts.Path != "" {
|
||||||
|
path = cookieOpts.Path
|
||||||
|
}
|
||||||
|
if s.opts.CookieDynamicDomain && domainMatchRegexp.MatchString(cookieOpts.Host) {
|
||||||
|
if etld, err := publicsuffix.EffectiveTLDPlusOne(cookieOpts.Host); err == nil {
|
||||||
domain = etld
|
domain = etld
|
||||||
name = anubis.WithDomainCookieName + etld
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -24,20 +24,20 @@ func TestSetCookie(t *testing.T) {
|
|||||||
name: "domain techaro.lol",
|
name: "domain techaro.lol",
|
||||||
options: Options{CookieDomain: "techaro.lol"},
|
options: Options{CookieDomain: "techaro.lol"},
|
||||||
host: "",
|
host: "",
|
||||||
cookieName: anubis.WithDomainCookieName + "techaro.lol",
|
cookieName: anubis.CookieName,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "dynamic cookie domain",
|
name: "dynamic cookie domain",
|
||||||
options: Options{CookieDynamicDomain: true},
|
options: Options{CookieDynamicDomain: true},
|
||||||
host: "techaro.lol",
|
host: "techaro.lol",
|
||||||
cookieName: anubis.WithDomainCookieName + "techaro.lol",
|
cookieName: anubis.CookieName,
|
||||||
},
|
},
|
||||||
} {
|
} {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
srv := spawnAnubis(t, tt.options)
|
srv := spawnAnubis(t, tt.options)
|
||||||
rw := httptest.NewRecorder()
|
rw := httptest.NewRecorder()
|
||||||
|
|
||||||
srv.SetCookie(rw, srv.cookieName, "test", "/", tt.host)
|
srv.SetCookie(rw, CookieOpts{Value: "test", Host: tt.host})
|
||||||
|
|
||||||
resp := rw.Result()
|
resp := rw.Result()
|
||||||
cookies := resp.Cookies()
|
cookies := resp.Cookies()
|
||||||
@ -55,7 +55,7 @@ func TestClearCookie(t *testing.T) {
|
|||||||
srv := spawnAnubis(t, Options{})
|
srv := spawnAnubis(t, Options{})
|
||||||
rw := httptest.NewRecorder()
|
rw := httptest.NewRecorder()
|
||||||
|
|
||||||
srv.ClearCookie(rw, srv.cookieName, "/", "localhost")
|
srv.ClearCookie(rw, CookieOpts{Host: "localhost"})
|
||||||
|
|
||||||
resp := rw.Result()
|
resp := rw.Result()
|
||||||
|
|
||||||
@ -80,7 +80,7 @@ func TestClearCookieWithDomain(t *testing.T) {
|
|||||||
srv := spawnAnubis(t, Options{CookieDomain: "techaro.lol"})
|
srv := spawnAnubis(t, Options{CookieDomain: "techaro.lol"})
|
||||||
rw := httptest.NewRecorder()
|
rw := httptest.NewRecorder()
|
||||||
|
|
||||||
srv.ClearCookie(rw, srv.cookieName, "/", "locahost")
|
srv.ClearCookie(rw, CookieOpts{Host: "localhost"})
|
||||||
|
|
||||||
resp := rw.Result()
|
resp := rw.Result()
|
||||||
|
|
||||||
@ -92,8 +92,8 @@ func TestClearCookieWithDomain(t *testing.T) {
|
|||||||
|
|
||||||
ckie := cookies[0]
|
ckie := cookies[0]
|
||||||
|
|
||||||
if ckie.Name != srv.cookieName {
|
if ckie.Name != anubis.CookieName {
|
||||||
t.Errorf("wanted cookie named %q, got cookie named %q", srv.cookieName, ckie.Name)
|
t.Errorf("wanted cookie named %q, got cookie named %q", anubis.CookieName, ckie.Name)
|
||||||
}
|
}
|
||||||
|
|
||||||
if ckie.MaxAge != -1 {
|
if ckie.MaxAge != -1 {
|
||||||
@ -105,7 +105,7 @@ func TestClearCookieWithDynamicDomain(t *testing.T) {
|
|||||||
srv := spawnAnubis(t, Options{CookieDynamicDomain: true})
|
srv := spawnAnubis(t, Options{CookieDynamicDomain: true})
|
||||||
rw := httptest.NewRecorder()
|
rw := httptest.NewRecorder()
|
||||||
|
|
||||||
srv.ClearCookie(rw, srv.cookieName, "/", "xeiaso.net")
|
srv.ClearCookie(rw, CookieOpts{Host: "subdomain.xeiaso.net"})
|
||||||
|
|
||||||
resp := rw.Result()
|
resp := rw.Result()
|
||||||
|
|
||||||
@ -117,8 +117,12 @@ func TestClearCookieWithDynamicDomain(t *testing.T) {
|
|||||||
|
|
||||||
ckie := cookies[0]
|
ckie := cookies[0]
|
||||||
|
|
||||||
if ckie.Name != anubis.WithDomainCookieName+"xeiaso.net" {
|
if ckie.Name != anubis.CookieName {
|
||||||
t.Errorf("wanted cookie named %q, got cookie named %q", srv.cookieName, ckie.Name)
|
t.Errorf("wanted cookie named %q, got cookie named %q", anubis.CookieName, ckie.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
if ckie.Domain != "xeiaso.net" {
|
||||||
|
t.Errorf("wanted cookie domain %q, got cookie domain %q", "xeiaso.net", ckie.Domain)
|
||||||
}
|
}
|
||||||
|
|
||||||
if ckie.MaxAge != -1 {
|
if ckie.MaxAge != -1 {
|
||||||
|
@ -24,8 +24,8 @@ require (
|
|||||||
github.com/gaissmai/bart v0.20.4 // indirect
|
github.com/gaissmai/bart v0.20.4 // indirect
|
||||||
github.com/golang-jwt/jwt/v5 v5.2.2 // indirect
|
github.com/golang-jwt/jwt/v5 v5.2.2 // indirect
|
||||||
github.com/google/cel-go v0.25.0 // indirect
|
github.com/google/cel-go v0.25.0 // indirect
|
||||||
github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.1 // indirect
|
github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.1.0 // indirect
|
||||||
github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.1.0 // indirect
|
github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.2 // indirect
|
||||||
github.com/joho/godotenv v1.5.1 // indirect
|
github.com/joho/godotenv v1.5.1 // indirect
|
||||||
github.com/jsha/minica v1.1.0 // indirect
|
github.com/jsha/minica v1.1.0 // indirect
|
||||||
github.com/kr/text v0.2.0 // indirect
|
github.com/kr/text v0.2.0 // indirect
|
||||||
@ -43,9 +43,9 @@ require (
|
|||||||
golang.org/x/text v0.26.0 // indirect
|
golang.org/x/text v0.26.0 // indirect
|
||||||
google.golang.org/genproto/googleapis/api v0.0.0-20250519155744-55703ea1f237 // indirect
|
google.golang.org/genproto/googleapis/api v0.0.0-20250519155744-55703ea1f237 // indirect
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250519155744-55703ea1f237 // indirect
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20250519155744-55703ea1f237 // indirect
|
||||||
google.golang.org/grpc v1.72.2 // indirect
|
google.golang.org/grpc v1.73.0 // indirect
|
||||||
google.golang.org/protobuf v1.36.6 // indirect
|
google.golang.org/protobuf v1.36.6 // indirect
|
||||||
k8s.io/apimachinery v0.33.1 // indirect
|
k8s.io/apimachinery v0.33.2 // indirect
|
||||||
sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 // indirect
|
sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 // indirect
|
||||||
sigs.k8s.io/yaml v1.4.0 // indirect
|
sigs.k8s.io/yaml v1.4.0 // indirect
|
||||||
)
|
)
|
||||||
|
@ -44,8 +44,10 @@ github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
|||||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.1 h1:qnpSQwGEnkcRpTqNOIR6bJbR0gAorgP9CSALpRcKoAA=
|
github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.1 h1:qnpSQwGEnkcRpTqNOIR6bJbR0gAorgP9CSALpRcKoAA=
|
||||||
github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.1/go.mod h1:lXGCsh6c22WGtjr+qGHj1otzZpV/1kwTMAqkwZsnWRU=
|
github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.1/go.mod h1:lXGCsh6c22WGtjr+qGHj1otzZpV/1kwTMAqkwZsnWRU=
|
||||||
|
github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.1.0/go.mod h1:hM2alZsMUni80N33RBe6J0e423LB+odMj7d3EMP9l20=
|
||||||
github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.1.0 h1:pRhl55Yx1eC7BZ1N+BBWwnKaMyD8uC+34TLdndZMAKk=
|
github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.1.0 h1:pRhl55Yx1eC7BZ1N+BBWwnKaMyD8uC+34TLdndZMAKk=
|
||||||
github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.1.0/go.mod h1:XKMd7iuf/RGPSMJ/U4HP0zS2Z9Fh8Ps9a+6X26m/tmI=
|
github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.1.0/go.mod h1:XKMd7iuf/RGPSMJ/U4HP0zS2Z9Fh8Ps9a+6X26m/tmI=
|
||||||
|
github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.2/go.mod h1:wd1YpapPLivG6nQgbf7ZkG1hhSOXDhhn4MLTknx2aAc=
|
||||||
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
|
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
|
||||||
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
|
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
|
||||||
github.com/jsha/minica v1.1.0 h1:O2ZbzAN75w4RTB+5+HfjIEvY5nxRqDlwj3ZlLVG5JD8=
|
github.com/jsha/minica v1.1.0 h1:O2ZbzAN75w4RTB+5+HfjIEvY5nxRqDlwj3ZlLVG5JD8=
|
||||||
@ -113,6 +115,7 @@ google.golang.org/genproto/googleapis/rpc v0.0.0-20250519155744-55703ea1f237 h1:
|
|||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250519155744-55703ea1f237/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A=
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20250519155744-55703ea1f237/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A=
|
||||||
google.golang.org/grpc v1.72.2 h1:TdbGzwb82ty4OusHWepvFWGLgIbNo1/SUynEN0ssqv8=
|
google.golang.org/grpc v1.72.2 h1:TdbGzwb82ty4OusHWepvFWGLgIbNo1/SUynEN0ssqv8=
|
||||||
google.golang.org/grpc v1.72.2/go.mod h1:wH5Aktxcg25y1I3w7H69nHfXdOG3UiadoBtjh3izSDM=
|
google.golang.org/grpc v1.72.2/go.mod h1:wH5Aktxcg25y1I3w7H69nHfXdOG3UiadoBtjh3izSDM=
|
||||||
|
google.golang.org/grpc v1.73.0/go.mod h1:50sbHOUqWoCQGI8V2HQLJM0B+LMlIUjNSZmow7EVBQc=
|
||||||
google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY=
|
google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY=
|
||||||
google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=
|
google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
@ -124,6 +127,7 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
|||||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
k8s.io/apimachinery v0.33.1 h1:mzqXWV8tW9Rw4VeW9rEkqvnxj59k1ezDUl20tFK/oM4=
|
k8s.io/apimachinery v0.33.1 h1:mzqXWV8tW9Rw4VeW9rEkqvnxj59k1ezDUl20tFK/oM4=
|
||||||
k8s.io/apimachinery v0.33.1/go.mod h1:BHW0YOu7n22fFv/JkYOEfkUYNRN0fj0BlvMFWA7b+SM=
|
k8s.io/apimachinery v0.33.1/go.mod h1:BHW0YOu7n22fFv/JkYOEfkUYNRN0fj0BlvMFWA7b+SM=
|
||||||
|
k8s.io/apimachinery v0.33.2/go.mod h1:BHW0YOu7n22fFv/JkYOEfkUYNRN0fj0BlvMFWA7b+SM=
|
||||||
sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 h1:gBQPwqORJ8d8/YNZWEjoZs7npUVDpVXUUOFfW6CgAqE=
|
sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 h1:gBQPwqORJ8d8/YNZWEjoZs7npUVDpVXUUOFfW6CgAqE=
|
||||||
sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8/go.mod h1:mdzfpAEoE6DHQEN0uh9ZbOCuHbLK5wOm7dK4ctXE9Tg=
|
sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8/go.mod h1:mdzfpAEoE6DHQEN0uh9ZbOCuHbLK5wOm7dK4ctXE9Tg=
|
||||||
sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E=
|
sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E=
|
||||||
|
Loading…
x
Reference in New Issue
Block a user