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}