Anubis module for Caddy
1package caddy_anubis 2 3import ( 4 "fmt" 5 "net" 6 "net/http" 7 8 "github.com/TecharoHQ/anubis" 9 libanubis "github.com/TecharoHQ/anubis/lib" 10 "github.com/caddyserver/caddy/v2" 11 "github.com/caddyserver/caddy/v2/caddyconfig/caddyfile" 12 "github.com/caddyserver/caddy/v2/caddyconfig/httpcaddyfile" 13 "github.com/caddyserver/caddy/v2/modules/caddyhttp" 14 "go.uber.org/zap" 15) 16 17func init() { 18 caddy.RegisterModule(AnubisMiddleware{}) 19 httpcaddyfile.RegisterHandlerDirective("anubis", parseCaddyfileHandler) 20 httpcaddyfile.RegisterDirectiveOrder("anubis", httpcaddyfile.Before, "push") 21} 22 23func (AnubisMiddleware) CaddyModule() caddy.ModuleInfo { 24 return caddy.ModuleInfo{ 25 ID: "http.handlers.anubis", 26 New: func() caddy.Module { return new(AnubisMiddleware) }, 27 } 28} 29 30type AnubisMiddleware struct { 31 Options libanubis.Options `json:"options"` 32 PolicyFname string `json:"policy_fname,omitempty"` 33 DefaultDifficulty int `json:"default_difficulty,omitempty"` 34 35 anubis *libanubis.Server 36 log *zap.Logger 37 next caddyhttp.Handler 38} 39 40// Interface guards 41var ( 42 _ caddyhttp.MiddlewareHandler = (*AnubisMiddleware)(nil) 43 _ caddyfile.Unmarshaler = (*AnubisMiddleware)(nil) 44 _ caddy.Provisioner = (*AnubisMiddleware)(nil) 45) 46 47func (m *AnubisMiddleware) Provision(ctx caddy.Context) error { 48 m.log = ctx.Logger(m) 49 50 m.log.Debug("loading anubis policies", zap.String("policy_file", m.PolicyFname), zap.Int("default_difficulty", m.DefaultDifficulty)) 51 policy, err := libanubis.LoadPoliciesOrDefault(ctx, m.PolicyFname, m.DefaultDifficulty) 52 if err != nil { 53 return fmt.Errorf("failed to load anubis policies from '%s': %w", m.PolicyFname, err) 54 } 55 56 m.Options.Next = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 57 if err := m.next.ServeHTTP(w, r); err != nil { 58 m.log.Error("error from next handler", zap.Error(err)) 59 } 60 }) 61 m.Options.Policy = policy 62 m.anubis, err = libanubis.New(m.Options) 63 64 if err != nil { 65 return err 66 } 67 68 return nil 69} 70 71func (m *AnubisMiddleware) ServeHTTP(w http.ResponseWriter, r *http.Request, next caddyhttp.Handler) error { 72 remoteHost, _, err := net.SplitHostPort(r.RemoteAddr) 73 if err != nil { 74 return err 75 } 76 r.Header.Set("X-Real-Ip", remoteHost) 77 78 m.next = next 79 m.anubis.ServeHTTP(w, r) 80 81 return nil 82} 83 84func (m *AnubisMiddleware) UnmarshalCaddyfile(d *caddyfile.Dispenser) error { 85 d.Next() 86 87 m.DefaultDifficulty = anubis.DefaultDifficulty 88 89 for nesting := d.Nesting(); d.NextBlock(nesting); { 90 switch d.Val() { 91 case "target": 92 } 93 } // anubis options 94 95 if d.NextArg() { 96 return d.ArgErr() 97 } // too many args 98 99 return nil 100} 101 102func parseCaddyfileHandler(h httpcaddyfile.Helper) (caddyhttp.MiddlewareHandler, error) { 103 var m AnubisMiddleware 104 err := m.UnmarshalCaddyfile(h.Dispenser) 105 return &m, err 106}