Skip to content

v1.1.0

Latest

Choose a tag to compare

@Rau-N Rau-N released this 22 Aug 12:37
bc25e0d

✨ Highlights

  • Custom Deny Responses at path and domain level
    Choose status code, headers (e.g., Location), content type, and body (HTML/JSON/plain).
  • Safe templating for headers and body using [[ ... ]] delimiters
    Avoids conflicts with Traefik’s {{ ... }} templating.
  • Smart defaults
    Auto-detects text/html when the body looks like HTML; falls back to 403 text/plain if no custom response is set.
  • Better developer experience
    New docs and tests (unit + integration) to lock in behavior and prevent regressions.

🔧 What’s New

Custom responses (denyResponse)

You can now configure a response to be sent when a request is denied—either for a specific path rule or as a domain-wide fallback.

  • Precedence: path-level denyResponse → domain-level denyResponse → default 403 Forbidden
  • Templating context: ClientIP, Host, Path, RawQuery, RequestURI, MatchedRule
  • Template helpers: urlquery, json, upper, lower
http:
  middlewares:
    domain-sentinel:
      plugin:
        domainSentinel:
          domainPathRules:
            "demo.localhost":
              sourceIPs:
                - "0.0.0.0/0"
              pathRules:
                - path: "/admin/*"
                  sourceIPs:
                    - "10.10.4.0/24"
                  denyResponse:
                    statusCode: 302
                    headers:
                      Location: "/login?next=[[ .RequestURI | urlquery ]]"
                    contentType: "text/plain; charset=utf-8"
                    body: "Redirecting to login…"
              denyResponse:
                statusCode: 503
                headers:
                  ClientIP: "[[ .ClientIP ]]"
                contentType: "text/html; charset=utf-8"
                body: |
                  <!doctype html>
                  <html><h1>Service unavailable for [[ .Host ]]</h1></html>

Note: Use [[ ... ]] in your dynamic file configs. Using {{ ... }} will be consumed by Traefik’s own templater and fail to load.


🛡️ Security & Behavior

  • Client IP source: By default, DomainSentinel derives the client IP from RemoteAddr and ignores X-Forwarded-For to prevent spoofing.
  • Header hygiene: Rendered header values are sanitized (CR/LF removed).
  • Redirects: Prefer 303 to force GET after POST, or 307/308 to preserve the method.

🧪 Tests & Tooling

  • Unit tests (deny_response_test.go): status/headers/body rendering, templating helpers, HTML detection, fallback behavior.
  • Integration tests (middleware_integration_test.go): full in-process flow Config -> New -> ServeHTTP, path/domain precedence, pass-through to next.

Run everything:

go test -v ./...

📚 Documentation

A new guide is available:

  • Custom Deny Responses: docs/custom-deny-responses.md
    Deep dive into configuration, templating, examples, and troubleshooting.

🔄 Migration / Compatibility

  • No breaking changes. If you don’t set denyResponse, behavior is unchanged (default 403 text/plain).
  • To adopt custom responses, add a denyResponse block at either the path or domain level (see example above).

🧩 Changelog

  • feat: configurable denyResponse at path & domain level (status, headers, content type, body)
  • feat: templating with [[ ... ]] in headers and body; helpers urlquery, json, upper, lower
  • feat: auto content type (HTML/plain) based on body
  • docs: new docs/custom-deny-responses.md
  • test: unit & integration coverage
  • refactor: cleaner deny rendering with header templating and sanitization