This commit is contained in:
WeebDataHoarder
2025-04-25 12:08:55 +02:00
parent bc0eaeca21
commit 4d7436c51b
9 changed files with 125 additions and 357 deletions

7
go.mod
View File

@@ -5,6 +5,7 @@ go 1.24.0
toolchain go1.24.2
require (
codeberg.org/gone/http-cel v1.0.0
codeberg.org/meta/gzipped/v2 v2.0.0-20231111234332-aa70c3194756
github.com/alphadose/haxmap v1.4.1
github.com/go-jose/go-jose/v4 v4.1.0
@@ -26,9 +27,9 @@ require (
github.com/itchyny/timefmt-go v0.1.6 // indirect
github.com/kevinpollet/nego v0.0.0-20211010160919-a65cd48cee43 // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/prometheus/client_model v0.6.1 // indirect
github.com/prometheus/common v0.62.0 // indirect
github.com/prometheus/procfs v0.15.1 // indirect
github.com/prometheus/client_model v0.6.2 // indirect
github.com/prometheus/common v0.63.0 // indirect
github.com/prometheus/procfs v0.16.1 // indirect
github.com/stoewer/go-strcase v1.3.0 // indirect
golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0 // indirect
golang.org/x/net v0.39.0 // indirect

14
go.sum
View File

@@ -1,5 +1,7 @@
cel.dev/expr v0.23.1 h1:K4KOtPCJQjVggkARsjG9RWXP6O4R73aHeJMa/dmCQQg=
cel.dev/expr v0.23.1/go.mod h1:hLPLo1W4QUmuYdA72RBX06QTs6MXw941piREPl3Yfiw=
codeberg.org/gone/http-cel v1.0.0 h1:flEv/KzEye4W7vjwkdAkwo7VCbuj9xZLjyTn/rjWFDQ=
codeberg.org/gone/http-cel v1.0.0/go.mod h1:uRkxygsQp5EFE3e9dRkJ4HK453G5YZDHCq9DEG5CoDw=
codeberg.org/meta/gzipped/v2 v2.0.0-20231111234332-aa70c3194756 h1:bDqEUEYt4UJy8mfLCZeJuXx+xNJvdqTbkE4Ci11NQYU=
codeberg.org/meta/gzipped/v2 v2.0.0-20231111234332-aa70c3194756/go.mod h1:aJ/ghJW7viYfwZ6OizDst+uJgbb6r/Hvoqhmi1OPTTw=
github.com/alphadose/haxmap v1.4.1 h1:VtD6VCxUkjNIfJk/aWdYFfOzrRddDFjmvmRmILg7x8Q=
@@ -39,12 +41,12 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_golang v1.22.0 h1:rb93p9lokFEsctTys46VnV1kLCDpVZ0a/Y92Vm0Zc6Q=
github.com/prometheus/client_golang v1.22.0/go.mod h1:R7ljNsLXhuQXYZYtw6GAE9AZg8Y7vEW5scdCXrWRXC0=
github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E=
github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY=
github.com/prometheus/common v0.62.0 h1:xasJaQlnWAeyHdUBeGjXmutelfJHWMRr+Fg4QszZ2Io=
github.com/prometheus/common v0.62.0/go.mod h1:vyBcEuLSvWos9B1+CyL7JZ2up+uFzXhkqml0W5zIY1I=
github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc=
github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk=
github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk=
github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE=
github.com/prometheus/common v0.63.0 h1:YR/EIY1o3mEFP/kZCD7iDMnLPlGyuU2Gb3HIcXnA98k=
github.com/prometheus/common v0.63.0/go.mod h1:VVFF/fBIoToEnWRVkYoXEkq3R3paCoxG9PXP74SnV18=
github.com/prometheus/procfs v0.16.1 h1:hZ15bTNuirocR6u0JZ6BAHHmwS1p8B4P6MRqxtzMyRg=
github.com/prometheus/procfs v0.16.1/go.mod h1:teAbpZRB1iIAJYREa1LsoWUXykVXA1KlTmWl8x/U+Is=
github.com/stoewer/go-strcase v1.3.0 h1:g0eASXYtp+yvN9fK8sH94oCIk0fau9uV1/ZdJ0AVEzs=
github.com/stoewer/go-strcase v1.3.0/go.mod h1:fAH5hQ5pehh+j3nZfvwdk2RgEgQjAoM8wodgtPmh1xo=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=

View File

@@ -1,13 +1,13 @@
package challenge
import (
http_cel "codeberg.org/gone/http-cel"
"context"
"crypto/rand"
"crypto/sha256"
"encoding/hex"
"errors"
"fmt"
"git.gammaspectra.live/git/go-away/lib/condition"
"git.gammaspectra.live/git/go-away/utils"
"github.com/google/cel-go/cel"
"github.com/google/cel-go/common/types"
@@ -73,8 +73,8 @@ func CreateRequestData(r *http.Request, state StateInterface) (*http.Request, *R
}
}
data.query = condition.NewValuesMap(r.URL.Query())
data.header = condition.NewMIMEMap(textproto.MIMEHeader(r.Header))
data.query = http_cel.NewValuesMap(r.URL.Query())
data.header = http_cel.NewMIMEMap(textproto.MIMEHeader(r.Header))
sum := sha256.New()
sum.Write([]byte(r.Host))

View File

@@ -2,10 +2,10 @@ package challenge
import (
"bytes"
http_cel "codeberg.org/gone/http-cel"
"crypto/ed25519"
"errors"
"fmt"
"git.gammaspectra.live/git/go-away/lib/condition"
"git.gammaspectra.live/git/go-away/lib/policy"
"github.com/go-jose/go-jose/v4"
"github.com/go-jose/go-jose/v4/jwt"
@@ -67,11 +67,11 @@ func (r Register) Create(state StateInterface, name string, pol policy.Challenge
}
if len(conditions) > 0 {
ast, err := condition.FromStrings(state.ProgramEnv(), condition.OperatorOr, conditions...)
ast, err := http_cel.NewAst(state.ProgramEnv(), http_cel.OperatorOr, conditions...)
if err != nil {
return nil, 0, fmt.Errorf("error compiling conditions: %v", err)
}
reg.Condition, err = condition.Program(state.ProgramEnv(), ast)
reg.Condition, err = http_cel.ProgramAst(state.ProgramEnv(), ast)
if err != nil {
return nil, 0, fmt.Errorf("error compiling program: %v", err)
}

View File

@@ -1,177 +0,0 @@
package condition
import (
"fmt"
"github.com/google/cel-go/cel"
"github.com/google/cel-go/common/types"
"github.com/google/cel-go/common/types/ref"
"github.com/google/cel-go/ext"
"github.com/yl2chen/cidranger"
"log/slog"
"net"
"strings"
)
type Condition struct {
Expression *cel.Ast
}
const (
OperatorOr = "||"
OperatorAnd = "&&"
)
func NewRulesEnvironment(networks map[string]cidranger.Ranger) (*cel.Env, error) {
return cel.NewEnv(
ext.Strings(
ext.StringsLocale("en_US"),
ext.StringsValidateFormatCalls(true),
),
cel.DefaultUTCTimeZone(true),
//TODO: custom type for remoteAddress
cel.Variable("remoteAddress", cel.BytesType),
cel.Variable("host", cel.StringType),
cel.Variable("method", cel.StringType),
cel.Variable("userAgent", cel.StringType),
cel.Variable("path", cel.StringType),
cel.Variable("query", cel.MapType(cel.StringType, cel.StringType)),
cel.Variable("fp", cel.MapType(cel.StringType, cel.StringType)),
// http.Header
cel.Variable("headers", cel.MapType(cel.StringType, cel.StringType)),
//TODO: dynamic type?
cel.Function("inDNSBL",
cel.Overload("inDNSBL_ip",
[]*cel.Type{cel.AnyType},
cel.BoolType,
cel.UnaryBinding(func(val ref.Val) ref.Val {
slog.Error("inDNSBL function has been deprecated, replace with dnsbl challenge")
return types.Bool(false)
}),
),
),
cel.Function("network",
cel.MemberOverload("netIP_network_string",
[]*cel.Type{cel.BytesType, cel.StringType},
cel.BoolType,
cel.BinaryBinding(func(lhs ref.Val, rhs ref.Val) ref.Val {
var ip net.IP
switch v := lhs.Value().(type) {
case []byte:
ip = v
case net.IP:
ip = v
}
if ip == nil {
panic(fmt.Errorf("invalid ip %v", lhs.Value()))
}
val, ok := rhs.Value().(string)
if !ok {
panic(fmt.Errorf("invalid network value %v", rhs.Value()))
}
network, ok := networks[val]
if !ok {
_, ipNet, err := net.ParseCIDR(val)
if err != nil {
panic("network not found")
}
return types.Bool(ipNet.Contains(ip))
} else {
ok, err := network.Contains(ip)
if err != nil {
panic(err)
}
return types.Bool(ok)
}
}),
),
),
cel.Function("inNetwork",
cel.Overload("inNetwork_string_ip",
[]*cel.Type{cel.StringType, cel.BytesType},
cel.BoolType,
cel.BinaryBinding(func(lhs ref.Val, rhs ref.Val) ref.Val {
var ip net.IP
switch v := rhs.Value().(type) {
case []byte:
ip = v
case net.IP:
ip = v
}
if ip == nil {
panic(fmt.Errorf("invalid ip %v", rhs.Value()))
}
val, ok := lhs.Value().(string)
if !ok {
panic(fmt.Errorf("invalid value %v", lhs.Value()))
}
slog.Debug(fmt.Sprintf("inNetwork function has been deprecated and will be removed in a future release, use remoteAddress.network(\"%s\") instead", val))
network, ok := networks[val]
if !ok {
_, ipNet, err := net.ParseCIDR(val)
if err != nil {
panic("network not found")
}
return types.Bool(ipNet.Contains(ip))
} else {
ok, err := network.Contains(ip)
if err != nil {
panic(err)
}
return types.Bool(ok)
}
}),
),
),
)
}
func Program(env *cel.Env, ast *cel.Ast) (cel.Program, error) {
return env.Program(ast,
cel.EvalOptions(cel.OptOptimize),
)
}
func FromStrings(env *cel.Env, operator string, conditions ...string) (*cel.Ast, error) {
var asts []*cel.Ast
for _, c := range conditions {
ast, issues := env.Compile(c)
if issues != nil && issues.Err() != nil {
return nil, fmt.Errorf("condition %s: %s", issues.Err(), c)
}
asts = append(asts, ast)
}
return Merge(env, operator, asts...)
}
func Merge(env *cel.Env, operator string, conditions ...*cel.Ast) (*cel.Ast, error) {
if len(conditions) == 0 {
return nil, nil
} else if len(conditions) == 1 {
return conditions[0], nil
}
var asts []string
for _, c := range conditions {
ast, err := cel.AstToString(c)
if err != nil {
return nil, err
}
asts = append(asts, "("+ast+")")
}
condition := strings.Join(asts, " "+operator+" ")
ast, issues := env.Compile(condition)
if issues != nil && issues.Err() != nil {
return nil, issues.Err()
}
return ast, nil
}

View File

@@ -1,158 +0,0 @@
package condition
import (
"fmt"
"github.com/google/cel-go/common/types"
"github.com/google/cel-go/common/types/ref"
"github.com/google/cel-go/common/types/traits"
"net/textproto"
"reflect"
"strings"
)
type mimeLike struct {
m textproto.MIMEHeader
}
func (a mimeLike) ConvertToNative(typeDesc reflect.Type) (any, error) {
return nil, fmt.Errorf("type conversion error from map to '%v'", typeDesc)
}
func (a mimeLike) ConvertToType(typeVal ref.Type) ref.Val {
switch typeVal {
case types.MapType:
return a
case types.TypeType:
return types.MapType
}
return types.NewErr("type conversion error from '%s' to '%s'", types.MapType, typeVal)
}
func (a mimeLike) Equal(other ref.Val) ref.Val {
return types.Bool(false)
}
func (a mimeLike) Type() ref.Type {
return types.MapType
}
func (a mimeLike) Value() any {
return a.m
}
func (a mimeLike) Contains(key ref.Val) ref.Val {
_, found := a.Find(key)
return types.Bool(found)
}
func (a mimeLike) Get(key ref.Val) ref.Val {
v, found := a.Find(key)
if !found {
return types.ValOrErr(v, "no such key: %v", key)
}
return v
}
func (a mimeLike) Iterator() traits.Iterator {
panic("implement me")
}
func (a mimeLike) IsZeroValue() bool {
return len(a.m) == 0
}
func (a mimeLike) Size() ref.Val {
return types.Int(len(a.m))
}
func (a mimeLike) Find(key ref.Val) (ref.Val, bool) {
k, ok := key.(types.String)
if !ok {
return nil, false
}
return singleVal(a.m.Values(string(k)), true)
}
type valuesLike struct {
m map[string][]string
}
func (a valuesLike) ConvertToNative(typeDesc reflect.Type) (any, error) {
return nil, fmt.Errorf("type conversion error from map to '%v'", typeDesc)
}
func (a valuesLike) ConvertToType(typeVal ref.Type) ref.Val {
switch typeVal {
case types.MapType:
return a
case types.TypeType:
return types.MapType
}
return types.NewErr("type conversion error from '%s' to '%s'", types.MapType, typeVal)
}
func (a valuesLike) Equal(other ref.Val) ref.Val {
return types.Bool(false)
}
func (a valuesLike) Type() ref.Type {
return types.MapType
}
func (a valuesLike) Value() any {
return a.m
}
func (a valuesLike) Contains(key ref.Val) ref.Val {
_, found := a.Find(key)
return types.Bool(found)
}
func (a valuesLike) Get(key ref.Val) ref.Val {
v, found := a.Find(key)
if !found {
return types.ValOrErr(v, "no such key: %v", key)
}
return v
}
func (a valuesLike) Iterator() traits.Iterator {
panic("implement me")
}
func (a valuesLike) IsZeroValue() bool {
return len(a.m) == 0
}
func (a valuesLike) Size() ref.Val {
return types.Int(len(a.m))
}
func (a valuesLike) Find(key ref.Val) (ref.Val, bool) {
k, ok := key.(types.String)
if !ok {
return nil, false
}
val, ok := a.m[string(k)]
return singleVal(val, ok)
}
func singleVal(values []string, ok bool) (ref.Val, bool) {
if len(values) == 0 || !ok {
return nil, false
}
if len(values) > 1 {
return types.String(strings.Join(values, ",")), true
}
return types.String(values[0]), true
}
func NewMIMEMap(m textproto.MIMEHeader) traits.Mapper {
return mimeLike{m: m}
}
func NewValuesMap(m map[string][]string) traits.Mapper {
return valuesLike{m: m}
}

View File

@@ -1,11 +1,111 @@
package lib
import (
"git.gammaspectra.live/git/go-away/lib/condition"
http_cel "codeberg.org/gone/http-cel"
"fmt"
"github.com/google/cel-go/cel"
"github.com/google/cel-go/common/types"
"github.com/google/cel-go/common/types/ref"
"log/slog"
"net"
)
func (state *State) initConditions() (err error) {
state.programEnv, err = condition.NewRulesEnvironment(state.networks)
state.programEnv, err = http_cel.NewEnvironment(
cel.Variable("fp", cel.MapType(cel.StringType, cel.StringType)),
cel.Function("inDNSBL",
cel.Overload("inDNSBL_ip",
[]*cel.Type{cel.AnyType},
cel.BoolType,
cel.UnaryBinding(func(val ref.Val) ref.Val {
slog.Error("inDNSBL function has been deprecated, replace with dnsbl challenge")
return types.Bool(false)
}),
),
),
cel.Function("network",
cel.MemberOverload("netIP_network_string",
[]*cel.Type{cel.BytesType, cel.StringType},
cel.BoolType,
cel.BinaryBinding(func(lhs ref.Val, rhs ref.Val) ref.Val {
var ip net.IP
switch v := lhs.Value().(type) {
case []byte:
ip = v
case net.IP:
ip = v
}
if ip == nil {
panic(fmt.Errorf("invalid ip %v", lhs.Value()))
}
val, ok := rhs.Value().(string)
if !ok {
panic(fmt.Errorf("invalid network value %v", rhs.Value()))
}
network, ok := state.networks[val]
if !ok {
_, ipNet, err := net.ParseCIDR(val)
if err != nil {
panic("network not found")
}
return types.Bool(ipNet.Contains(ip))
} else {
ok, err := network.Contains(ip)
if err != nil {
panic(err)
}
return types.Bool(ok)
}
}),
),
),
cel.Function("inNetwork",
cel.Overload("inNetwork_string_ip",
[]*cel.Type{cel.StringType, cel.BytesType},
cel.BoolType,
cel.BinaryBinding(func(lhs ref.Val, rhs ref.Val) ref.Val {
var ip net.IP
switch v := rhs.Value().(type) {
case []byte:
ip = v
case net.IP:
ip = v
}
if ip == nil {
panic(fmt.Errorf("invalid ip %v", rhs.Value()))
}
val, ok := lhs.Value().(string)
if !ok {
panic(fmt.Errorf("invalid value %v", lhs.Value()))
}
slog.Debug(fmt.Sprintf("inNetwork function has been deprecated and will be removed in a future release, use remoteAddress.network(\"%s\") instead", val))
network, ok := state.networks[val]
if !ok {
_, ipNet, err := net.ParseCIDR(val)
if err != nil {
panic("network not found")
}
return types.Bool(ipNet.Contains(ip))
} else {
ok, err := network.Contains(ip)
if err != nil {
panic(err)
}
return types.Bool(ok)
}
}),
),
),
)
if err != nil {
return err
}

View File

@@ -1,12 +1,12 @@
package lib
import (
http_cel "codeberg.org/gone/http-cel"
"crypto/sha256"
"encoding/hex"
"fmt"
"git.gammaspectra.live/git/go-away/lib/action"
"git.gammaspectra.live/git/go-away/lib/challenge"
"git.gammaspectra.live/git/go-away/lib/condition"
"git.gammaspectra.live/git/go-away/lib/policy"
"github.com/google/cel-go/cel"
"github.com/google/cel-go/common/types"
@@ -66,12 +66,12 @@ func NewRuleState(state challenge.StateInterface, r policy.Rule, replacer *strin
conditions = append(conditions, cond)
}
ast, err := condition.FromStrings(state.ProgramEnv(), condition.OperatorOr, conditions...)
ast, err := http_cel.NewAst(state.ProgramEnv(), http_cel.OperatorOr, conditions...)
if err != nil {
return RuleState{}, fmt.Errorf("error compiling conditions: %w", err)
}
program, err := condition.Program(state.ProgramEnv(), ast)
program, err := http_cel.ProgramAst(state.ProgramEnv(), ast)
if err != nil {
return RuleState{}, fmt.Errorf("error compiling program: %w", err)
}

View File

@@ -1,12 +1,12 @@
package lib
import (
http_cel "codeberg.org/gone/http-cel"
"crypto/ed25519"
"crypto/rand"
"encoding/json"
"fmt"
"git.gammaspectra.live/git/go-away/lib/challenge"
"git.gammaspectra.live/git/go-away/lib/condition"
"git.gammaspectra.live/git/go-away/lib/policy"
"git.gammaspectra.live/git/go-away/lib/settings"
"git.gammaspectra.live/git/go-away/utils"
@@ -191,7 +191,7 @@ func NewState(p policy.Policy, opt settings.Settings, settings policy.StateSetti
var replacements []string
for k, entries := range p.Conditions {
ast, err := condition.FromStrings(state.programEnv, condition.OperatorOr, entries...)
ast, err := http_cel.NewAst(state.programEnv, http_cel.OperatorOr, entries...)
if err != nil {
return nil, fmt.Errorf("conditions %s: error compiling conditions: %v", k, err)
}