aboutsummaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/anubis.go51
-rw-r--r--lib/anubis_test.go99
-rw-r--r--lib/http.go16
3 files changed, 41 insertions, 125 deletions
diff --git a/lib/anubis.go b/lib/anubis.go
index 6b5a1e3..6e40f95 100644
--- a/lib/anubis.go
+++ b/lib/anubis.go
@@ -67,10 +67,6 @@ type Options struct {
Policy *policy.ParsedConfig
ServeRobotsTXT bool
PrivateKey ed25519.PrivateKey
-
- CookieDomain string
- CookieName string
- CookiePartitioned bool
}
func LoadPoliciesOrDefault(fname string, defaultDifficulty int) (*policy.ParsedConfig, error) {
@@ -112,7 +108,6 @@ func New(opts Options) (*Server, error) {
priv: opts.PrivateKey,
pub: opts.PrivateKey.Public().(ed25519.PublicKey),
policy: opts.Policy,
- opts: opts,
DNSBLCache: decaymap.New[string, dnsbl.DroneBLResponse](),
}
@@ -150,7 +145,6 @@ type Server struct {
priv ed25519.PrivateKey
pub ed25519.PublicKey
policy *policy.ParsedConfig
- opts Options
DNSBLCache *decaymap.Impl[string, dnsbl.DroneBLResponse]
ChallengeDifficulty int
}
@@ -223,7 +217,7 @@ func (s *Server) MaybeReverseProxy(w http.ResponseWriter, r *http.Request) {
s.next.ServeHTTP(w, r)
return
case config.RuleDeny:
- s.ClearCookie(w)
+ ClearCookie(w)
lg.Info("explicit deny")
if rule == nil {
lg.Error("rule is nil, cannot calculate checksum")
@@ -242,29 +236,29 @@ func (s *Server) MaybeReverseProxy(w http.ResponseWriter, r *http.Request) {
case config.RuleChallenge:
lg.Debug("challenge requested")
default:
- s.ClearCookie(w)
+ ClearCookie(w)
templ.Handler(web.Base("Oh noes!", web.ErrorPage("Other internal server error (contact the admin)")), templ.WithStatus(http.StatusInternalServerError)).ServeHTTP(w, r)
return
}
- ckie, err := r.Cookie(s.opts.CookieName)
+ ckie, err := r.Cookie(anubis.CookieName)
if err != nil {
lg.Debug("cookie not found", "path", r.URL.Path)
- s.ClearCookie(w)
+ ClearCookie(w)
s.RenderIndex(w, r)
return
}
if err := ckie.Valid(); err != nil {
lg.Debug("cookie is invalid", "err", err)
- s.ClearCookie(w)
+ ClearCookie(w)
s.RenderIndex(w, r)
return
}
if time.Now().After(ckie.Expires) && !ckie.Expires.IsZero() {
lg.Debug("cookie expired", "path", r.URL.Path)
- s.ClearCookie(w)
+ ClearCookie(w)
s.RenderIndex(w, r)
return
}
@@ -275,7 +269,7 @@ func (s *Server) MaybeReverseProxy(w http.ResponseWriter, r *http.Request) {
if err != nil || !token.Valid {
lg.Debug("invalid token", "path", r.URL.Path, "err", err)
- s.ClearCookie(w)
+ ClearCookie(w)
s.RenderIndex(w, r)
return
}
@@ -290,7 +284,7 @@ func (s *Server) MaybeReverseProxy(w http.ResponseWriter, r *http.Request) {
claims, ok := token.Claims.(jwt.MapClaims)
if !ok {
lg.Debug("invalid token claims type", "path", r.URL.Path)
- s.ClearCookie(w)
+ ClearCookie(w)
s.RenderIndex(w, r)
return
}
@@ -298,7 +292,7 @@ func (s *Server) MaybeReverseProxy(w http.ResponseWriter, r *http.Request) {
if claims["challenge"] != challenge {
lg.Debug("invalid challenge", "path", r.URL.Path)
- s.ClearCookie(w)
+ ClearCookie(w)
s.RenderIndex(w, r)
return
}
@@ -315,7 +309,7 @@ func (s *Server) MaybeReverseProxy(w http.ResponseWriter, r *http.Request) {
if subtle.ConstantTimeCompare([]byte(claims["response"].(string)), []byte(calculated)) != 1 {
lg.Debug("invalid response", "path", r.URL.Path)
failedValidations.Inc()
- s.ClearCookie(w)
+ ClearCookie(w)
s.RenderIndex(w, r)
return
}
@@ -378,7 +372,7 @@ func (s *Server) PassChallenge(w http.ResponseWriter, r *http.Request) {
nonceStr := r.FormValue("nonce")
if nonceStr == "" {
- s.ClearCookie(w)
+ ClearCookie(w)
lg.Debug("no nonce")
templ.Handler(web.Base("Oh noes!", web.ErrorPage("missing nonce")), templ.WithStatus(http.StatusInternalServerError)).ServeHTTP(w, r)
return
@@ -386,7 +380,7 @@ func (s *Server) PassChallenge(w http.ResponseWriter, r *http.Request) {
elapsedTimeStr := r.FormValue("elapsedTime")
if elapsedTimeStr == "" {
- s.ClearCookie(w)
+ ClearCookie(w)
lg.Debug("no elapsedTime")
templ.Handler(web.Base("Oh noes!", web.ErrorPage("missing elapsedTime")), templ.WithStatus(http.StatusInternalServerError)).ServeHTTP(w, r)
return
@@ -394,7 +388,7 @@ func (s *Server) PassChallenge(w http.ResponseWriter, r *http.Request) {
elapsedTime, err := strconv.ParseFloat(elapsedTimeStr, 64)
if err != nil {
- s.ClearCookie(w)
+ ClearCookie(w)
lg.Debug("elapsedTime doesn't parse", "err", err)
templ.Handler(web.Base("Oh noes!", web.ErrorPage("invalid elapsedTime")), templ.WithStatus(http.StatusInternalServerError)).ServeHTTP(w, r)
return
@@ -410,7 +404,7 @@ func (s *Server) PassChallenge(w http.ResponseWriter, r *http.Request) {
nonce, err := strconv.Atoi(nonceStr)
if err != nil {
- s.ClearCookie(w)
+ ClearCookie(w)
lg.Debug("nonce doesn't parse", "err", err)
templ.Handler(web.Base("Oh noes!", web.ErrorPage("invalid nonce")), templ.WithStatus(http.StatusInternalServerError)).ServeHTTP(w, r)
return
@@ -420,7 +414,7 @@ func (s *Server) PassChallenge(w http.ResponseWriter, r *http.Request) {
calculated := internal.SHA256sum(calcString)
if subtle.ConstantTimeCompare([]byte(response), []byte(calculated)) != 1 {
- s.ClearCookie(w)
+ ClearCookie(w)
lg.Debug("hash does not match", "got", response, "want", calculated)
templ.Handler(web.Base("Oh noes!", web.ErrorPage("invalid response")), templ.WithStatus(http.StatusForbidden)).ServeHTTP(w, r)
failedValidations.Inc()
@@ -429,7 +423,7 @@ func (s *Server) PassChallenge(w http.ResponseWriter, r *http.Request) {
// compare the leading zeroes
if !strings.HasPrefix(response, strings.Repeat("0", s.ChallengeDifficulty)) {
- s.ClearCookie(w)
+ ClearCookie(w)
lg.Debug("difficulty check failed", "response", response, "difficulty", s.ChallengeDifficulty)
templ.Handler(web.Base("Oh noes!", web.ErrorPage("invalid response")), templ.WithStatus(http.StatusForbidden)).ServeHTTP(w, r)
failedValidations.Inc()
@@ -448,18 +442,17 @@ func (s *Server) PassChallenge(w http.ResponseWriter, r *http.Request) {
tokenString, err := token.SignedString(s.priv)
if err != nil {
lg.Error("failed to sign JWT", "err", err)
- s.ClearCookie(w)
+ ClearCookie(w)
templ.Handler(web.Base("Oh noes!", web.ErrorPage("failed to sign JWT")), templ.WithStatus(http.StatusInternalServerError)).ServeHTTP(w, r)
return
}
http.SetCookie(w, &http.Cookie{
- Name: s.opts.CookieName,
- Value: tokenString,
- Expires: time.Now().Add(24 * 7 * time.Hour),
- Domain: s.opts.CookieDomain,
- Partitioned: s.opts.CookiePartitioned,
- SameSite: http.SameSiteLaxMode,
+ Name: anubis.CookieName,
+ Value: tokenString,
+ Expires: time.Now().Add(24 * 7 * time.Hour),
+ SameSite: http.SameSiteLaxMode,
+ Path: "/",
})
challengesValidated.Inc()
diff --git a/lib/anubis_test.go b/lib/anubis_test.go
index 7369b5d..0498c13 100644
--- a/lib/anubis_test.go
+++ b/lib/anubis_test.go
@@ -1,18 +1,15 @@
package lib
import (
- "encoding/json"
"fmt"
"net/http"
"net/http/httptest"
"testing"
"github.com/TecharoHQ/anubis"
- "github.com/TecharoHQ/anubis/internal"
- "github.com/TecharoHQ/anubis/lib/policy"
)
-func loadPolicies(t *testing.T, fname string) *policy.ParsedConfig {
+func spawnAnubis(t *testing.T, h http.Handler) string {
t.Helper()
policy, err := LoadPoliciesOrDefault("", anubis.DefaultDifficulty)
@@ -20,97 +17,23 @@ func loadPolicies(t *testing.T, fname string) *policy.ParsedConfig {
t.Fatal(err)
}
- return policy
-}
-
-func spawnAnubis(t *testing.T, opts Options) *Server {
- t.Helper()
-
- s, err := New(opts)
+ s, err := New(Options{
+ Next: h,
+ Policy: policy,
+ ServeRobotsTXT: true,
+ })
if err != nil {
t.Fatalf("can't construct libanubis.Server: %v", err)
}
- return s
-}
-
-func TestCookieSettings(t *testing.T) {
- pol := loadPolicies(t, "")
- pol.DefaultDifficulty = 0
-
- srv := spawnAnubis(t, Options{
- Next: http.NewServeMux(),
- Policy: pol,
+ ts := httptest.NewServer(s)
+ t.Log(ts.URL)
- CookieDomain: "local.cetacean.club",
- CookiePartitioned: true,
- CookieName: t.Name(),
+ t.Cleanup(func() {
+ ts.Close()
})
- ts := httptest.NewServer(internal.DefaultXRealIP("127.0.0.1", srv))
- defer ts.Close()
-
- cli := &http.Client{
- CheckRedirect: func(req *http.Request, via []*http.Request) error {
- return http.ErrUseLastResponse
- },
- }
-
- resp, err := cli.Post(ts.URL+"/.within.website/x/cmd/anubis/api/make-challenge", "", nil)
- if err != nil {
- t.Fatalf("can't request challenge: %v", err)
- }
- defer resp.Body.Close()
-
- var chall = struct {
- Challenge string `json:"challenge"`
- }{}
- if err := json.NewDecoder(resp.Body).Decode(&chall); err != nil {
- t.Fatalf("can't read challenge response body: %v", err)
- }
-
- nonce := 0
- elapsedTime := 420
- redir := "/"
- calcString := fmt.Sprintf("%s%d", chall.Challenge, nonce)
- calculated := internal.SHA256sum(calcString)
-
- req, err := http.NewRequest(http.MethodGet, ts.URL+"/.within.website/x/cmd/anubis/api/pass-challenge", nil)
- if err != nil {
- t.Fatalf("can't make request: %v", err)
- }
-
- q := req.URL.Query()
- q.Set("response", calculated)
- q.Set("nonce", fmt.Sprint(nonce))
- q.Set("redir", redir)
- q.Set("elapsedTime", fmt.Sprint(elapsedTime))
- req.URL.RawQuery = q.Encode()
-
- resp, err = cli.Do(req)
- if err != nil {
- t.Fatalf("can't do challenge passing")
- }
-
- if resp.StatusCode != http.StatusFound {
- t.Errorf("wanted %d, got: %d", http.StatusFound, resp.StatusCode)
- }
-
- found := false
- for _, cookie := range resp.Cookies() {
- t.Logf("%#v", cookie)
- if cookie.Name == t.Name() {
- found = true
- }
-
- if found && cookie.Domain != "local.cetacean.club" {
- t.Errorf("cookie domain is wrong, wanted local.cetacean.club, got: %s", cookie.Domain)
- }
- }
-
- if !found {
- t.Errorf("Cookie %q not found", t.Name())
- }
+ return ts.URL
}
func TestCheckDefaultDifficultyMatchesPolicy(t *testing.T) {
diff --git a/lib/http.go b/lib/http.go
index d7f678f..1284523 100644
--- a/lib/http.go
+++ b/lib/http.go
@@ -3,17 +3,17 @@ package lib
import (
"net/http"
"time"
+
+ "github.com/TecharoHQ/anubis"
)
-func (s *Server) ClearCookie(w http.ResponseWriter) {
+func ClearCookie(w http.ResponseWriter) {
http.SetCookie(w, &http.Cookie{
- Name: s.opts.CookieName,
- Value: "",
- Expires: time.Now().Add(-1 * time.Hour),
- MaxAge: -1,
- Domain: s.opts.CookieDomain,
- Partitioned: s.opts.CookiePartitioned,
- SameSite: http.SameSiteLaxMode,
+ Name: anubis.CookieName,
+ Value: "",
+ Expires: time.Now().Add(-1 * time.Hour),
+ MaxAge: -1,
+ SameSite: http.SameSiteLaxMode,
})
}