This reverts commit e7cbd349f3
.
This commit is contained in:
parent
e7cbd349f3
commit
ecc6b47f90
6 changed files with 53 additions and 144 deletions
|
@ -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()
|
||||
|
|
|
@ -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
|
||||
}
|
||||
ts := httptest.NewServer(s)
|
||||
t.Log(ts.URL)
|
||||
|
||||
func TestCookieSettings(t *testing.T) {
|
||||
pol := loadPolicies(t, "")
|
||||
pol.DefaultDifficulty = 0
|
||||
|
||||
srv := spawnAnubis(t, Options{
|
||||
Next: http.NewServeMux(),
|
||||
Policy: pol,
|
||||
|
||||
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) {
|
||||
|
|
16
lib/http.go
16
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,
|
||||
})
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue