mirror of
https://github.com/TecharoHQ/anubis.git
synced 2025-08-03 09:48:08 -04:00

* chore(web/js): delete proof-of-work-slow.mjs This code has served its purpose and now needs to be retired to the great beyond. There is no replacement for this, the fast implementation will be used instead. Signed-off-by: Xe Iaso <me@xeiaso.net> * chore(web): handle building multiple JS entrypoints and web workers Signed-off-by: Xe Iaso <me@xeiaso.net> * feat(web): rewrite frontend worker handling This completely rewrites how the proof of work challenge works based on feedback from browser engine developers and starts the process of making the proof of work function easier to change out. - Import @aws-crypto/sha256-js to use in Firefox as its implementation of WebCrypto doesn't jump directly from highly optimized browser internals to JIT-ed JavaScript like Chrome's seems to. - Move the worker code to `web/js/worker/*` with each worker named after the hashing method and hash method implementation it uses. - Update bench.mjs to import algorithms the new way. - Delete video.mjs, it was part of a legacy experiment that I never had time to finish. - Update LibreJS comment to add info about the use of @aws-crypto/sha256-js. - Also update my email to my @techaro.lol address. Signed-off-by: Xe Iaso <me@xeiaso.net> * fix(web): don't hard dep webcrypto anymore Signed-off-by: Xe Iaso <me@xeiaso.net> * chore(lib/policy): start the deprecation process for slow This mostly adds a warning, but the "slow" method is in the process of being removed. Warn admins with slog.Warn. Signed-off-by: Xe Iaso <me@xeiaso.net> * docs: update CHANGELOG Signed-off-by: Xe Iaso <me@xeiaso.net> * feat(web/js): allow running Anubis in non-secure contexts Signed-off-by: Xe Iaso <me@xeiaso.net> * Update metadata check-spelling run (pull_request) for Xe/purge-slow Signed-off-by: check-spelling-bot <check-spelling-bot@users.noreply.github.com> on-behalf-of: @check-spelling <check-spelling-bot@check-spelling.dev> --------- Signed-off-by: Xe Iaso <me@xeiaso.net> Signed-off-by: check-spelling-bot <check-spelling-bot@users.noreply.github.com>
214 lines
5.7 KiB
Go
214 lines
5.7 KiB
Go
package policy
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"io"
|
|
"log/slog"
|
|
"sync/atomic"
|
|
|
|
"github.com/TecharoHQ/anubis/lib/policy/checker"
|
|
"github.com/TecharoHQ/anubis/lib/policy/config"
|
|
"github.com/TecharoHQ/anubis/lib/store"
|
|
"github.com/TecharoHQ/anubis/lib/thoth"
|
|
"github.com/prometheus/client_golang/prometheus"
|
|
"github.com/prometheus/client_golang/prometheus/promauto"
|
|
|
|
_ "github.com/TecharoHQ/anubis/lib/store/all"
|
|
)
|
|
|
|
var (
|
|
Applications = promauto.NewCounterVec(prometheus.CounterOpts{
|
|
Name: "anubis_policy_results",
|
|
Help: "The results of each policy rule",
|
|
}, []string{"rule", "action"})
|
|
|
|
ErrChallengeRuleHasWrongAlgorithm = errors.New("config.Bot.ChallengeRules: algorithm is invalid")
|
|
warnedAboutThresholds = &atomic.Bool{}
|
|
)
|
|
|
|
type ParsedConfig struct {
|
|
orig *config.Config
|
|
|
|
Bots []Bot
|
|
Thresholds []*Threshold
|
|
DNSBL bool
|
|
Impressum *config.Impressum
|
|
OpenGraph config.OpenGraph
|
|
DefaultDifficulty int
|
|
StatusCodes config.StatusCodes
|
|
Store store.Interface
|
|
}
|
|
|
|
func newParsedConfig(orig *config.Config) *ParsedConfig {
|
|
return &ParsedConfig{
|
|
orig: orig,
|
|
OpenGraph: orig.OpenGraph,
|
|
StatusCodes: orig.StatusCodes,
|
|
}
|
|
}
|
|
|
|
func ParseConfig(ctx context.Context, fin io.Reader, fname string, defaultDifficulty int) (*ParsedConfig, error) {
|
|
c, err := config.Load(fin, fname)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var validationErrs []error
|
|
|
|
tc, hasThothClient := thoth.FromContext(ctx)
|
|
|
|
result := newParsedConfig(c)
|
|
result.DefaultDifficulty = defaultDifficulty
|
|
|
|
for _, b := range c.Bots {
|
|
if berr := b.Valid(); berr != nil {
|
|
validationErrs = append(validationErrs, berr)
|
|
continue
|
|
}
|
|
|
|
parsedBot := Bot{
|
|
Name: b.Name,
|
|
Action: b.Action,
|
|
}
|
|
|
|
cl := checker.List{}
|
|
|
|
if len(b.RemoteAddr) > 0 {
|
|
c, err := NewRemoteAddrChecker(b.RemoteAddr)
|
|
if err != nil {
|
|
validationErrs = append(validationErrs, fmt.Errorf("while processing rule %s remote addr set: %w", b.Name, err))
|
|
} else {
|
|
cl = append(cl, c)
|
|
}
|
|
}
|
|
|
|
if b.UserAgentRegex != nil {
|
|
c, err := NewUserAgentChecker(*b.UserAgentRegex)
|
|
if err != nil {
|
|
validationErrs = append(validationErrs, fmt.Errorf("while processing rule %s user agent regex: %w", b.Name, err))
|
|
} else {
|
|
cl = append(cl, c)
|
|
}
|
|
}
|
|
|
|
if b.PathRegex != nil {
|
|
c, err := NewPathChecker(*b.PathRegex)
|
|
if err != nil {
|
|
validationErrs = append(validationErrs, fmt.Errorf("while processing rule %s path regex: %w", b.Name, err))
|
|
} else {
|
|
cl = append(cl, c)
|
|
}
|
|
}
|
|
|
|
if len(b.HeadersRegex) > 0 {
|
|
c, err := NewHeadersChecker(b.HeadersRegex)
|
|
if err != nil {
|
|
validationErrs = append(validationErrs, fmt.Errorf("while processing rule %s headers regex map: %w", b.Name, err))
|
|
} else {
|
|
cl = append(cl, c)
|
|
}
|
|
}
|
|
|
|
if b.Expression != nil {
|
|
c, err := NewCELChecker(b.Expression)
|
|
if err != nil {
|
|
validationErrs = append(validationErrs, fmt.Errorf("while processing rule %s expressions: %w", b.Name, err))
|
|
} else {
|
|
cl = append(cl, c)
|
|
}
|
|
}
|
|
|
|
if b.ASNs != nil {
|
|
if !hasThothClient {
|
|
slog.Warn("You have specified a Thoth specific check but you have no Thoth client configured. Please read https://anubis.techaro.lol/docs/admin/thoth for more information", "check", "asn", "settings", b.ASNs)
|
|
continue
|
|
}
|
|
|
|
cl = append(cl, tc.ASNCheckerFor(b.ASNs.Match))
|
|
}
|
|
|
|
if b.GeoIP != nil {
|
|
if !hasThothClient {
|
|
slog.Warn("You have specified a Thoth specific check but you have no Thoth client configured. Please read https://anubis.techaro.lol/docs/admin/thoth for more information", "check", "geoip", "settings", b.GeoIP)
|
|
continue
|
|
}
|
|
|
|
cl = append(cl, tc.GeoIPCheckerFor(b.GeoIP.Countries))
|
|
}
|
|
|
|
if b.Challenge == nil {
|
|
parsedBot.Challenge = &config.ChallengeRules{
|
|
Difficulty: defaultDifficulty,
|
|
ReportAs: defaultDifficulty,
|
|
Algorithm: "fast",
|
|
}
|
|
} else {
|
|
parsedBot.Challenge = b.Challenge
|
|
if parsedBot.Challenge.Algorithm == "" {
|
|
parsedBot.Challenge.Algorithm = config.DefaultAlgorithm
|
|
}
|
|
|
|
if parsedBot.Challenge.Algorithm == "slow" {
|
|
slog.Warn("use of deprecated algorithm \"slow\" detected, please update this to \"fast\" when possible", "name", parsedBot.Name)
|
|
}
|
|
}
|
|
|
|
if b.Weight != nil {
|
|
parsedBot.Weight = b.Weight
|
|
}
|
|
|
|
result.Impressum = c.Impressum
|
|
|
|
parsedBot.Rules = cl
|
|
|
|
result.Bots = append(result.Bots, parsedBot)
|
|
}
|
|
|
|
for _, t := range c.Thresholds {
|
|
if t.Challenge != nil && t.Challenge.Algorithm == "slow" {
|
|
slog.Warn("use of deprecated algorithm \"slow\" detected, please update this to \"fast\" when possible", "name", t.Name)
|
|
}
|
|
|
|
if t.Name == "legacy-anubis-behaviour" && t.Expression.String() == "true" {
|
|
if !warnedAboutThresholds.Load() {
|
|
slog.Warn("configuration file does not contain thresholds, see docs for details on how to upgrade", "fname", fname, "docs_url", "https://anubis.techaro.lol/docs/admin/configuration/thresholds/")
|
|
warnedAboutThresholds.Store(true)
|
|
}
|
|
|
|
t.Challenge.Difficulty = defaultDifficulty
|
|
t.Challenge.ReportAs = defaultDifficulty
|
|
}
|
|
|
|
threshold, err := ParsedThresholdFromConfig(t)
|
|
if err != nil {
|
|
validationErrs = append(validationErrs, fmt.Errorf("can't compile threshold config for %s: %w", t.Name, err))
|
|
continue
|
|
}
|
|
|
|
result.Thresholds = append(result.Thresholds, threshold)
|
|
}
|
|
|
|
stFac, ok := store.Get(c.Store.Backend)
|
|
switch ok {
|
|
case true:
|
|
store, err := stFac.Build(ctx, c.Store.Parameters)
|
|
if err != nil {
|
|
validationErrs = append(validationErrs, err)
|
|
} else {
|
|
result.Store = store
|
|
}
|
|
case false:
|
|
validationErrs = append(validationErrs, config.ErrUnknownStoreBackend)
|
|
}
|
|
|
|
if len(validationErrs) > 0 {
|
|
return nil, fmt.Errorf("errors validating policy config JSON %s: %w", fname, errors.Join(validationErrs...))
|
|
}
|
|
|
|
result.DNSBL = c.DNSBL
|
|
|
|
return result, nil
|
|
}
|