The Macaw logo, a scarlet macaw in flight

macaw_plug

This software is a very early release, and not ready for general use.

Public home: github.com/dcoai/macaw_plug · Docs: hexdocs.pm

Request guards for Phoenix and Plug hosts, implemented once for the Macaw family's host applications (dco-tek/macaw_compose#34). Plug only: no Phoenix, Ecto or JSON dependency.

ModuleWhat it does
Macaw.Plug.ClientIpthe client's address behind trusted proxies (none trusted by default); sets conn.remote_ip
Macaw.Plug.Ipaddress and CIDR parsing, membership, IPv4-mapped normalization
Macaw.Plug.RateLimitGCRA rate limiting in ETS, by client address or any key; 429 with Retry-After (start Macaw.Plug.RateLimit.Table in the host's tree)
Macaw.Plug.SecurityHeadersa closed Content-Security-Policy, script hashes added per response, the standard headers and a Permissions-Policy by feature; replaces Phoenix's put_secure_browser_headers
plug Macaw.Plug.ClientIp, trusted_proxies: ["10.0.0.0/8"]
plug Macaw.Plug.RateLimit, id: :site, rate: {10, :second}, burst: 50
plug Macaw.Plug.SecurityHeaders

Security headers

The default is closed, and every loosening is named in the host's code:

OptionDefault
preset::strict:compatible starts from Phoenix's put_secure_browser_headers set, plus HSTS over https
csp:[]directives by name, [img_src: "'self' https://tiles.example"]
csp_mode::enforce:report_only reports violations and blocks nothing; :off sends no policy
permissions:[]Permissions-Policy features by name: [geolocation: :self, camera: [:self, "https://meet.example"]]; nil removes one
headers:%{}any header by name; nil removes it
hsts:"max-age=31536000"over https; not a string turns it off

Every option applies on top of the preset; an unknown option raises.

Migrating from put_secure_browser_headers

  1. Swap the plug in with preset: :compatible: the same headers Phoenix sends, so nothing breaks.
  2. Try the closed policy unenforced: drop the preset and set csp_mode: :report_only. The browser console lists what it would block; csp: [report_uri: "/csp-reports"] sends the reports to the host.
  3. Allow what the pages need, by name: an inline script by its hash (Macaw.Plug.SecurityHeaders.put_script_hash/2), a source with csp:, a browser feature with permissions:. Phoenix's live-reload iframe needs, in development only, csp: [frame_ancestors: "'self'"] and headers: %{"x-frame-options" => "SAMEORIGIN"}.
  4. Enforce: drop csp_mode:.

The full rules are in spec/03.

Part of Macaw, whose spec lists it among the host libraries (spec/00 §4). From Hex:

{:macaw_plug, "~> 0.2.0"}