|
@@ -43,6 +43,15 @@ const (
|
|
RuleBenchmark Rule = "DEBUG_BENCHMARK"
|
|
RuleBenchmark Rule = "DEBUG_BENCHMARK"
|
|
)
|
|
)
|
|
|
|
|
|
|
|
+func (r Rule) Valid() error {
|
|
|
|
+ switch r {
|
|
|
|
+ case RuleAllow, RuleDeny, RuleChallenge, RuleWeigh, RuleBenchmark:
|
|
|
|
+ return nil
|
|
|
|
+ default:
|
|
|
|
+ return ErrUnknownAction
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
const DefaultAlgorithm = "fast"
|
|
const DefaultAlgorithm = "fast"
|
|
|
|
|
|
type BotConfig struct {
|
|
type BotConfig struct {
|
|
@@ -184,13 +193,18 @@ type ChallengeRules struct {
|
|
}
|
|
}
|
|
|
|
|
|
var (
|
|
var (
|
|
- ErrChallengeDifficultyTooLow = errors.New("config.Bot.ChallengeRules: difficulty is too low (must be >= 1)")
|
|
|
|
- ErrChallengeDifficultyTooHigh = errors.New("config.Bot.ChallengeRules: difficulty is too high (must be <= 64)")
|
|
|
|
|
|
+ ErrChallengeDifficultyTooLow = errors.New("config.ChallengeRules: difficulty is too low (must be >= 1)")
|
|
|
|
+ ErrChallengeDifficultyTooHigh = errors.New("config.ChallengeRules: difficulty is too high (must be <= 64)")
|
|
|
|
+ ErrChallengeMustHaveAlgorithm = errors.New("config.ChallengeRules: must have algorithm name set")
|
|
)
|
|
)
|
|
|
|
|
|
func (cr ChallengeRules) Valid() error {
|
|
func (cr ChallengeRules) Valid() error {
|
|
var errs []error
|
|
var errs []error
|
|
|
|
|
|
|
|
+ if cr.Algorithm == "" {
|
|
|
|
+ errs = append(errs, ErrChallengeMustHaveAlgorithm)
|
|
|
|
+ }
|
|
|
|
+
|
|
if cr.Difficulty < 1 {
|
|
if cr.Difficulty < 1 {
|
|
errs = append(errs, fmt.Errorf("%w, got: %d", ErrChallengeDifficultyTooLow, cr.Difficulty))
|
|
errs = append(errs, fmt.Errorf("%w, got: %d", ErrChallengeDifficultyTooLow, cr.Difficulty))
|
|
}
|
|
}
|
|
@@ -312,18 +326,19 @@ type fileConfig struct {
|
|
Bots []BotOrImport `json:"bots"`
|
|
Bots []BotOrImport `json:"bots"`
|
|
DNSBL bool `json:"dnsbl"`
|
|
DNSBL bool `json:"dnsbl"`
|
|
StatusCodes StatusCodes `json:"status_codes"`
|
|
StatusCodes StatusCodes `json:"status_codes"`
|
|
|
|
+ Thresholds []Threshold `json:"threshold"`
|
|
}
|
|
}
|
|
|
|
|
|
-func (c fileConfig) Valid() error {
|
|
|
|
|
|
+func (c *fileConfig) Valid() error {
|
|
var errs []error
|
|
var errs []error
|
|
|
|
|
|
if len(c.Bots) == 0 {
|
|
if len(c.Bots) == 0 {
|
|
errs = append(errs, ErrNoBotRulesDefined)
|
|
errs = append(errs, ErrNoBotRulesDefined)
|
|
}
|
|
}
|
|
|
|
|
|
- for _, b := range c.Bots {
|
|
|
|
|
|
+ for i, b := range c.Bots {
|
|
if err := b.Valid(); err != nil {
|
|
if err := b.Valid(); err != nil {
|
|
- errs = append(errs, err)
|
|
|
|
|
|
+ errs = append(errs, fmt.Errorf("bot %d: %w", i, err))
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
@@ -331,6 +346,16 @@ func (c fileConfig) Valid() error {
|
|
errs = append(errs, err)
|
|
errs = append(errs, err)
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ if len(c.Thresholds) == 0 {
|
|
|
|
+ errs = append(errs, ErrNoThresholdRulesDefined)
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ for i, t := range c.Thresholds {
|
|
|
|
+ if err := t.Valid(); err != nil {
|
|
|
|
+ errs = append(errs, fmt.Errorf("threshold %d: %w", i, err))
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
if len(errs) != 0 {
|
|
if len(errs) != 0 {
|
|
return fmt.Errorf("config is not valid:\n%w", errors.Join(errs...))
|
|
return fmt.Errorf("config is not valid:\n%w", errors.Join(errs...))
|
|
}
|
|
}
|
|
@@ -339,11 +364,14 @@ func (c fileConfig) Valid() error {
|
|
}
|
|
}
|
|
|
|
|
|
func Load(fin io.Reader, fname string) (*Config, error) {
|
|
func Load(fin io.Reader, fname string) (*Config, error) {
|
|
- var c fileConfig
|
|
|
|
- c.StatusCodes = StatusCodes{
|
|
|
|
- Challenge: http.StatusOK,
|
|
|
|
- Deny: http.StatusOK,
|
|
|
|
|
|
+ c := &fileConfig{
|
|
|
|
+ StatusCodes: StatusCodes{
|
|
|
|
+ Challenge: http.StatusOK,
|
|
|
|
+ Deny: http.StatusOK,
|
|
|
|
+ },
|
|
|
|
+ Thresholds: DefaultThresholds,
|
|
}
|
|
}
|
|
|
|
+
|
|
if err := yaml.NewYAMLToJSONDecoder(fin).Decode(&c); err != nil {
|
|
if err := yaml.NewYAMLToJSONDecoder(fin).Decode(&c); err != nil {
|
|
return nil, fmt.Errorf("can't parse policy config YAML %s: %w", fname, err)
|
|
return nil, fmt.Errorf("can't parse policy config YAML %s: %w", fname, err)
|
|
}
|
|
}
|
|
@@ -379,6 +407,15 @@ func Load(fin io.Reader, fname string) (*Config, error) {
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ for _, t := range c.Thresholds {
|
|
|
|
+ if err := t.Valid(); err != nil {
|
|
|
|
+ validationErrs = append(validationErrs, err)
|
|
|
|
+ continue
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ result.Thresholds = append(result.Thresholds, t)
|
|
|
|
+ }
|
|
|
|
+
|
|
if len(validationErrs) > 0 {
|
|
if len(validationErrs) > 0 {
|
|
return nil, fmt.Errorf("errors validating policy config %s: %w", fname, errors.Join(validationErrs...))
|
|
return nil, fmt.Errorf("errors validating policy config %s: %w", fname, errors.Join(validationErrs...))
|
|
}
|
|
}
|
|
@@ -388,6 +425,7 @@ func Load(fin io.Reader, fname string) (*Config, error) {
|
|
|
|
|
|
type Config struct {
|
|
type Config struct {
|
|
Bots []BotConfig
|
|
Bots []BotConfig
|
|
|
|
+ Thresholds []Threshold
|
|
DNSBL bool
|
|
DNSBL bool
|
|
StatusCodes StatusCodes
|
|
StatusCodes StatusCodes
|
|
}
|
|
}
|