context: add CONTEXT action to apply options on current request

This commit is contained in:
WeebDataHoarder
2025-04-27 17:20:57 +02:00
parent d353286a08
commit c5ad9cdf03
6 changed files with 71 additions and 1 deletions

View File

@@ -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.

50
lib/action/context.go Normal file
View File

@@ -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, &params)
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
}

View File

@@ -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

View File

@@ -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[:])
}

View File

@@ -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 {