From c5ad9cdf03421dffeabf05b7ada74a0209c89b5b Mon Sep 17 00:00:00 2001 From: WeebDataHoarder Date: Sun, 27 Apr 2025 17:20:57 +0200 Subject: [PATCH] context: add CONTEXT action to apply options on current request --- README.md | 2 ++ lib/action/context.go | 50 +++++++++++++++++++++++++++++ lib/action/{backend.go => proxy.go} | 0 lib/challenge/data.go | 15 +++++++++ lib/challenge/helper.go | 2 +- lib/policy/rule.go | 3 ++ 6 files changed, 71 insertions(+), 1 deletion(-) create mode 100644 lib/action/context.go rename lib/action/{backend.go => proxy.go} (100%) diff --git a/README.md b/README.md index 327bb80..de30c95 100644 --- a/README.md +++ b/README.md @@ -88,6 +88,7 @@ In addition to the common PASS / CHALLENGE / DENY rules, go-away offers more act | Action | Behavior | Terminating | |:---------:|:------------------------------------------------------------------------|:-----------:| +| NONE | Do nothing, continue. Useful for specifying on checks or challenges. | No | | PASS | Passes the request to the backend immediately | Yes | | DENY | Denies the request with a descriptive page | Yes | | BLOCK | Denies the request with a response code | Yes | @@ -95,6 +96,7 @@ In addition to the common PASS / CHALLENGE / DENY rules, go-away offers more act | CHALLENGE | Issues a challenge that when passed, acts like PASS | Yes | | CHECK | Issues a challenge that when passed, continues executing rules | No | | PROXY | Proxies request to a different backend, with optional path replacements | Yes | +| CONTEXT | Modify the request context and apply different options | No | CHECK allows the client to be challenged but continue matching rules after these, for example, chaining a list of challenges that must be passed. diff --git a/lib/action/context.go b/lib/action/context.go new file mode 100644 index 0000000..d3dea07 --- /dev/null +++ b/lib/action/context.go @@ -0,0 +1,50 @@ +package action + +import ( + "git.gammaspectra.live/git/go-away/lib/challenge" + "git.gammaspectra.live/git/go-away/lib/policy" + "github.com/goccy/go-yaml" + "github.com/goccy/go-yaml/ast" + "log/slog" + "net/http" +) + +func init() { + Register[policy.RuleActionCONTEXT] = func(state challenge.StateInterface, ruleName, ruleHash string, settings ast.Node) (Handler, error) { + params := ContextDefaultSettings + + if settings != nil { + ymlData, err := settings.MarshalYAML() + if err != nil { + return nil, err + } + err = yaml.Unmarshal(ymlData, ¶ms) + if err != nil { + return nil, err + } + } + + return Context{ + opts: params, + }, nil + } +} + +var ContextDefaultSettings = ContextSettings{} + +type ContextSettings struct { + Settings map[string]string `yaml:"context-set"` +} + +type Context struct { + opts ContextSettings +} + +func (a Context) Handle(logger *slog.Logger, w http.ResponseWriter, r *http.Request, done func() (backend http.Handler)) (next bool, err error) { + data := challenge.RequestDataFromContext(r.Context()) + for k, v := range a.opts.Settings { + data.SetOpt(k, v) + } + + return false, nil +} diff --git a/lib/action/backend.go b/lib/action/proxy.go similarity index 100% rename from lib/action/backend.go rename to lib/action/proxy.go diff --git a/lib/challenge/data.go b/lib/challenge/data.go index 407f721..7adac9e 100644 --- a/lib/challenge/data.go +++ b/lib/challenge/data.go @@ -46,6 +46,8 @@ type RequestData struct { fp map[string]string header traits.Mapper query traits.Mapper + + opts map[string]string } func CreateRequestData(r *http.Request, state StateInterface) (*http.Request, *RequestData) { @@ -84,6 +86,7 @@ func CreateRequestData(r *http.Request, state StateInterface) (*http.Request, *R data.query = http_cel.NewValuesMap(q) data.header = http_cel.NewMIMEMap(textproto.MIMEHeader(r.Header)) + data.opts = make(map[string]string) sum := sha256.New() sum.Write([]byte(r.Host)) @@ -126,6 +129,18 @@ func (d *RequestData) Parent() cel.Activation { return nil } +func (d *RequestData) SetOpt(n, v string) { + d.opts[n] = v +} + +func (d *RequestData) GetOpt(n, def string) string { + v, ok := d.opts[n] + if !ok { + return def + } + return v +} + func (d *RequestData) EvaluateChallenges(w http.ResponseWriter, r *http.Request) { q := r.URL.Query() var issuedChallenge string diff --git a/lib/challenge/helper.go b/lib/challenge/helper.go index acf56d8..6e733fe 100644 --- a/lib/challenge/helper.go +++ b/lib/challenge/helper.go @@ -19,7 +19,7 @@ func NewKeyVerifier() (verify VerifyFunc, issue func(key Key) string) { if subtle.ConstantTimeCompare(key[:], expectedKey) == 1 { return VerifyResultOK, nil } - return VerifyResultFail, errors.New("invalid token") + return VerifyResultFail, errors.New("mismatched token") }, func(key Key) string { return hex.EncodeToString(key[:]) } diff --git a/lib/policy/rule.go b/lib/policy/rule.go index d7a44a1..608d755 100644 --- a/lib/policy/rule.go +++ b/lib/policy/rule.go @@ -26,6 +26,9 @@ const ( // RuleActionPROXY Proxies request to a backend, with optional path replacements RuleActionPROXY RuleAction = "PROXY" + + // RuleActionCONTEXT Changes Request Context information or properties + RuleActionCONTEXT RuleAction = "CONTEXT" ) type Rule struct {