package caddyanubis import ( "fmt" "io" "net/http" "os" "github.com/TecharoHQ/anubis" libanubis "github.com/TecharoHQ/anubis/lib" "github.com/caddyserver/caddy/v2" "github.com/caddyserver/caddy/v2/caddyconfig/caddyfile" "github.com/caddyserver/caddy/v2/caddyconfig/httpcaddyfile" "github.com/caddyserver/caddy/v2/modules/caddyhttp" ) func init() { caddy.RegisterModule(Middleware{}) httpcaddyfile.RegisterHandlerDirective("anubis", parseCaddyfile) } // Middleware implements an HTTP handler that writes the // visitor's IP address to a file or stream. type Middleware struct { // The file or stream to write to. Can be "stdout" // or "stderr". Output string `json:"output,omitempty"` w io.Writer } // CaddyModule returns the Caddy module information. func (Middleware) CaddyModule() caddy.ModuleInfo { return caddy.ModuleInfo{ ID: "http.handlers.anubis", New: func() caddy.Module { return new(Middleware) }, } } // Provision implements caddy.Provisioner. func (m *Middleware) Provision(ctx caddy.Context) error { switch m.Output { case "stdout": m.w = os.Stdout case "stderr": m.w = os.Stderr default: return fmt.Errorf("an output stream is required") } return nil } // Validate implements caddy.Validator. func (m *Middleware) Validate() error { if m.w == nil { return fmt.Errorf("no writer") } return nil } // ServeHTTP implements caddyhttp.MiddlewareHandler. func (m Middleware) ServeHTTP(w http.ResponseWriter, r *http.Request, next caddyhttp.Handler) error { m.w.Write([]byte(r.RemoteAddr)) policy, err := libanubis.LoadPoliciesOrDefault("", anubis.DefaultDifficulty) if err != nil { return err } errChan := make(chan error) anubisServer, err := libanubis.New(libanubis.Options{ Next: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { errChan <- next.ServeHTTP(w, r) }), Policy: policy, ServeRobotsTXT: true, }) if err != nil { return err } anubisServer.ServeHTTP(w, r) for err := range errChan { if err != nil { return err } } return nil } // UnmarshalCaddyfile implements caddyfile.Unmarshaler. func (m *Middleware) UnmarshalCaddyfile(d *caddyfile.Dispenser) error { d.Next() // consume directive name // require an argument if !d.NextArg() { return d.ArgErr() } // store the argument m.Output = d.Val() return nil } // parseCaddyfile unmarshals tokens from h into a new Middleware. func parseCaddyfile(h httpcaddyfile.Helper) (caddyhttp.MiddlewareHandler, error) { var m Middleware err := m.UnmarshalCaddyfile(h.Dispenser) return m, err } // Interface guards var ( _ caddy.Provisioner = (*Middleware)(nil) _ caddy.Validator = (*Middleware)(nil) _ caddyhttp.MiddlewareHandler = (*Middleware)(nil) _ caddyfile.Unmarshaler = (*Middleware)(nil) )