feat: add default OpenGraph tags to configuration file (#694)
* feat(config): opengraph passthrough configuration Signed-off-by: Xe Iaso <me@xeiaso.net> * chore(ogtags): use config.OpenGraph for configuration Signed-off-by: Xe Iaso <me@xeiaso.net> * chore: wire up ogtags config in most of the app Signed-off-by: Xe Iaso <me@xeiaso.net> * feat(ogtags): return default tags if they are supplied Signed-off-by: Xe Iaso <me@xeiaso.net> * chore: make OpenGraph legal so we have some sanity in reviewing Signed-off-by: Xe Iaso <me@xeiaso.net> * chore: spelling Signed-off-by: Xe Iaso <me@xeiaso.net> * fix(lib): use OpenGraph.Enabled Signed-off-by: Xe Iaso <me@xeiaso.net> * test(lib): load default config file if one is not specified in spawnAnubis Signed-off-by: Xe Iaso <me@xeiaso.net> * chore(config): fix ST1005 Signed-off-by: Xe Iaso <me@xeiaso.net> * docs: document open graph defaults and its new home in the policy file Signed-off-by: Xe Iaso <me@xeiaso.net> * docs(installation): point to weight threshold new home Signed-off-by: Xe Iaso <me@xeiaso.net> * chore: rename default to override Signed-off-by: Xe Iaso <me@xeiaso.net> * chore(default-config): add off-by-default opengraph settings to bot policy file Signed-off-by: Xe Iaso <me@xeiaso.net> * fix(anubis): make build Signed-off-by: Xe Iaso <me@xeiaso.net> * test(lib): fix build Signed-off-by: Xe Iaso <me@xeiaso.net> --------- Signed-off-by: Xe Iaso <me@xeiaso.net>
This commit is contained in:
parent
7aa732c700
commit
4948036f39
25 changed files with 416 additions and 78 deletions
|
|
@ -44,6 +44,10 @@ func loadPolicies(t *testing.T, fname string, difficulty int) *policy.ParsedConf
|
|||
func spawnAnubis(t *testing.T, opts Options) *Server {
|
||||
t.Helper()
|
||||
|
||||
if opts.Policy == nil {
|
||||
opts.Policy = loadPolicies(t, "", 4)
|
||||
}
|
||||
|
||||
s, err := New(opts)
|
||||
if err != nil {
|
||||
t.Fatalf("can't construct libanubis.Server: %v", err)
|
||||
|
|
|
|||
|
|
@ -21,27 +21,26 @@ import (
|
|||
"github.com/TecharoHQ/anubis/internal/ogtags"
|
||||
"github.com/TecharoHQ/anubis/lib/challenge"
|
||||
"github.com/TecharoHQ/anubis/lib/policy"
|
||||
"github.com/TecharoHQ/anubis/lib/policy/config"
|
||||
"github.com/TecharoHQ/anubis/web"
|
||||
"github.com/TecharoHQ/anubis/xess"
|
||||
)
|
||||
|
||||
type Options struct {
|
||||
Next http.Handler
|
||||
Policy *policy.ParsedConfig
|
||||
Target string
|
||||
CookieDomain string
|
||||
CookieName string
|
||||
BasePrefix string
|
||||
WebmasterEmail string
|
||||
RedirectDomains []string
|
||||
PrivateKey ed25519.PrivateKey
|
||||
CookieExpiration time.Duration
|
||||
OGTimeToLive time.Duration
|
||||
StripBasePrefix bool
|
||||
OGCacheConsidersHost bool
|
||||
OGPassthrough bool
|
||||
CookiePartitioned bool
|
||||
ServeRobotsTXT bool
|
||||
Next http.Handler
|
||||
Policy *policy.ParsedConfig
|
||||
Target string
|
||||
CookieDomain string
|
||||
CookieName string
|
||||
BasePrefix string
|
||||
WebmasterEmail string
|
||||
RedirectDomains []string
|
||||
PrivateKey ed25519.PrivateKey
|
||||
CookieExpiration time.Duration
|
||||
StripBasePrefix bool
|
||||
OpenGraph config.OpenGraph
|
||||
CookiePartitioned bool
|
||||
ServeRobotsTXT bool
|
||||
}
|
||||
|
||||
func LoadPoliciesOrDefault(ctx context.Context, fname string, defaultDifficulty int) (*policy.ParsedConfig, error) {
|
||||
|
|
@ -112,7 +111,7 @@ func New(opts Options) (*Server, error) {
|
|||
policy: opts.Policy,
|
||||
opts: opts,
|
||||
DNSBLCache: decaymap.New[string, dnsbl.DroneBLResponse](),
|
||||
OGTags: ogtags.NewOGTagCache(opts.Target, opts.OGPassthrough, opts.OGTimeToLive, opts.OGCacheConsidersHost),
|
||||
OGTags: ogtags.NewOGTagCache(opts.Target, opts.Policy.OpenGraph),
|
||||
cookieName: cookieName,
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -80,7 +80,7 @@ func (s *Server) RenderIndex(w http.ResponseWriter, r *http.Request, rule *polic
|
|||
challengeStr := s.challengeFor(r, rule.Challenge.Difficulty)
|
||||
|
||||
var ogTags map[string]string = nil
|
||||
if s.opts.OGPassthrough {
|
||||
if s.opts.OpenGraph.Enabled {
|
||||
var err error
|
||||
ogTags, err = s.OGTags.GetOGTags(r.URL, r.Host)
|
||||
if err != nil {
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import (
|
|||
"os"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/TecharoHQ/anubis/data"
|
||||
"k8s.io/apimachinery/pkg/util/yaml"
|
||||
|
|
@ -323,10 +324,11 @@ func (sc StatusCodes) Valid() error {
|
|||
}
|
||||
|
||||
type fileConfig struct {
|
||||
Bots []BotOrImport `json:"bots"`
|
||||
DNSBL bool `json:"dnsbl"`
|
||||
StatusCodes StatusCodes `json:"status_codes"`
|
||||
Thresholds []Threshold `json:"thresholds"`
|
||||
Bots []BotOrImport `json:"bots"`
|
||||
DNSBL bool `json:"dnsbl"`
|
||||
OpenGraph openGraphFileConfig `json:"openGraph,omitempty"`
|
||||
StatusCodes StatusCodes `json:"status_codes"`
|
||||
Thresholds []Threshold `json:"thresholds"`
|
||||
}
|
||||
|
||||
func (c *fileConfig) Valid() error {
|
||||
|
|
@ -342,6 +344,12 @@ func (c *fileConfig) Valid() error {
|
|||
}
|
||||
}
|
||||
|
||||
if c.OpenGraph.Enabled {
|
||||
if err := c.OpenGraph.Valid(); err != nil {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
}
|
||||
|
||||
if err := c.StatusCodes.Valid(); err != nil {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
|
|
@ -376,10 +384,21 @@ func Load(fin io.Reader, fname string) (*Config, error) {
|
|||
}
|
||||
|
||||
result := &Config{
|
||||
DNSBL: c.DNSBL,
|
||||
DNSBL: c.DNSBL,
|
||||
OpenGraph: OpenGraph{
|
||||
Enabled: c.OpenGraph.Enabled,
|
||||
ConsiderHost: c.OpenGraph.ConsiderHost,
|
||||
Override: c.OpenGraph.Override,
|
||||
},
|
||||
StatusCodes: c.StatusCodes,
|
||||
}
|
||||
|
||||
if c.OpenGraph.TimeToLive != "" {
|
||||
// XXX(Xe): already validated in Valid()
|
||||
ogTTL, _ := time.ParseDuration(c.OpenGraph.TimeToLive)
|
||||
result.OpenGraph.TimeToLive = ogTTL
|
||||
}
|
||||
|
||||
var validationErrs []error
|
||||
|
||||
for _, boi := range c.Bots {
|
||||
|
|
@ -426,6 +445,7 @@ type Config struct {
|
|||
Bots []BotConfig
|
||||
Thresholds []Threshold
|
||||
DNSBL bool
|
||||
OpenGraph OpenGraph
|
||||
StatusCodes StatusCodes
|
||||
}
|
||||
|
||||
|
|
|
|||
51
lib/policy/config/opengraph.go
Normal file
51
lib/policy/config/opengraph.go
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
package config
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrInvalidOpenGraphConfig = errors.New("config.OpenGraph: invalid OpenGraph configuration")
|
||||
ErrOpenGraphTTLDoesNotParse = errors.New("config.OpenGraph: ttl does not parse as a Duration, see https://pkg.go.dev/time#ParseDuration (formatted like 5m -> 5 minutes, 2h -> 2 hours, etc)")
|
||||
ErrOpenGraphMissingProperty = errors.New("config.OpenGraph: default opengraph tags missing a property")
|
||||
)
|
||||
|
||||
type openGraphFileConfig struct {
|
||||
Enabled bool `json:"enabled" yaml:"enabled"`
|
||||
ConsiderHost bool `json:"considerHost" yaml:"enabled"`
|
||||
TimeToLive string `json:"ttl" yaml:"ttl"`
|
||||
Override map[string]string `json:"override,omitempty" yaml:"override,omitempty"`
|
||||
}
|
||||
|
||||
type OpenGraph struct {
|
||||
Enabled bool `json:"enabled" yaml:"enabled"`
|
||||
ConsiderHost bool `json:"considerHost" yaml:"enabled"`
|
||||
Override map[string]string `json:"override,omitempty" yaml:"override,omitempty"`
|
||||
TimeToLive time.Duration `json:"ttl" yaml:"ttl"`
|
||||
}
|
||||
|
||||
func (og *openGraphFileConfig) Valid() error {
|
||||
var errs []error
|
||||
|
||||
if _, err := time.ParseDuration(og.TimeToLive); err != nil {
|
||||
errs = append(errs, fmt.Errorf("%w: ParseDuration(%q) returned: %w", ErrOpenGraphTTLDoesNotParse, og.TimeToLive, err))
|
||||
}
|
||||
|
||||
if len(og.Override) != 0 {
|
||||
for _, tag := range []string{
|
||||
"og:title",
|
||||
} {
|
||||
if _, ok := og.Override[tag]; !ok {
|
||||
errs = append(errs, fmt.Errorf("%w: %s", ErrOpenGraphMissingProperty, tag))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(errs) != 0 {
|
||||
return errors.Join(ErrInvalidOpenGraphConfig, errors.Join(errs...))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
67
lib/policy/config/opengraph_test.go
Normal file
67
lib/policy/config/opengraph_test.go
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
package config
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestOpenGraphFileConfigValid(t *testing.T) {
|
||||
for _, tt := range []struct {
|
||||
name string
|
||||
input *openGraphFileConfig
|
||||
err error
|
||||
}{
|
||||
{
|
||||
name: "basic happy path",
|
||||
input: &openGraphFileConfig{
|
||||
Enabled: true,
|
||||
ConsiderHost: false,
|
||||
TimeToLive: "1h",
|
||||
Override: map[string]string{},
|
||||
},
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
name: "basic happy path with default",
|
||||
input: &openGraphFileConfig{
|
||||
Enabled: true,
|
||||
ConsiderHost: false,
|
||||
TimeToLive: "1h",
|
||||
Override: map[string]string{
|
||||
"og:title": "foobar",
|
||||
},
|
||||
},
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
name: "invalid time duration",
|
||||
input: &openGraphFileConfig{
|
||||
Enabled: true,
|
||||
ConsiderHost: false,
|
||||
TimeToLive: "taco",
|
||||
Override: map[string]string{},
|
||||
},
|
||||
err: ErrOpenGraphTTLDoesNotParse,
|
||||
},
|
||||
{
|
||||
name: "missing og:title in defaults",
|
||||
input: &openGraphFileConfig{
|
||||
Enabled: true,
|
||||
ConsiderHost: false,
|
||||
TimeToLive: "1h",
|
||||
Override: map[string]string{
|
||||
"description": "foobar",
|
||||
},
|
||||
},
|
||||
err: ErrOpenGraphMissingProperty,
|
||||
},
|
||||
} {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if err := tt.input.Valid(); !errors.Is(err, tt.err) {
|
||||
t.Logf("wanted error: %v", tt.err)
|
||||
t.Logf("got error: %v", err)
|
||||
t.Error("validation failed")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
12
lib/policy/config/testdata/bad/opengraph_bad_ttl.yaml
vendored
Normal file
12
lib/policy/config/testdata/bad/opengraph_bad_ttl.yaml
vendored
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
bots:
|
||||
- name: everything
|
||||
user_agent_regex: .*
|
||||
action: DENY
|
||||
|
||||
openGraph:
|
||||
enabled: true
|
||||
considerHost: false
|
||||
ttl: taco
|
||||
default:
|
||||
"og:title": "Xe's magic land of fun"
|
||||
"og:description": "We're no strangers to love, you know the rules and so do I"
|
||||
12
lib/policy/config/testdata/good/opengraph_all_good.yaml
vendored
Normal file
12
lib/policy/config/testdata/good/opengraph_all_good.yaml
vendored
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
bots:
|
||||
- name: everything
|
||||
user_agent_regex: .*
|
||||
action: DENY
|
||||
|
||||
openGraph:
|
||||
enabled: true
|
||||
considerHost: false
|
||||
ttl: 1h
|
||||
default:
|
||||
"og:title": "Xe's magic land of fun"
|
||||
"og:description": "We're no strangers to love, you know the rules and so do I"
|
||||
|
|
@ -31,6 +31,7 @@ type ParsedConfig struct {
|
|||
Bots []Bot
|
||||
Thresholds []*Threshold
|
||||
DNSBL bool
|
||||
OpenGraph config.OpenGraph
|
||||
DefaultDifficulty int
|
||||
StatusCodes config.StatusCodes
|
||||
}
|
||||
|
|
@ -38,6 +39,7 @@ type ParsedConfig struct {
|
|||
func NewParsedConfig(orig *config.Config) *ParsedConfig {
|
||||
return &ParsedConfig{
|
||||
orig: orig,
|
||||
OpenGraph: orig.OpenGraph,
|
||||
StatusCodes: orig.StatusCodes,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue