Module: CloudflareAccessGate

Defined in:
lib/cloudflare_access_gate.rb,
lib/cloudflare_access_gate/gate.rb,
lib/cloudflare_access_gate/version.rb,
lib/cloudflare_access_gate/jwks_cache.rb,
lib/cloudflare_access_gate/audit_logger.rb,
lib/cloudflare_access_gate/structured_logger.rb

Overview

Rack middleware that gates a dashboard (typically Sidekiq::Web) behind Cloudflare Access, plus an audit logger for the requests that get through. See the README for the ENV contract and security notes.

Defined Under Namespace

Classes: AuditLogger, Gate, JwksCache, StructuredLogger

Constant Summary collapse

VERSION =
'0.2.1'

Class Attribute Summary collapse

Class Method Summary collapse

Class Attribute Details

.loggerObject

Logger used by both middlewares. Assign anything that responds to debug/info/warn/error — SemanticLogger, a stdlib Logger, Rails.logger, and so on:

CloudflareAccessGate.logger = Rails.logger

When left unset (nil), SemanticLogger is used if the host app has it loaded (giving per-class named loggers and tagged output), and a stdlib Logger.new($stdout) otherwise.



26
27
28
# File 'lib/cloudflare_access_gate.rb', line 26

def logger
  @logger
end

Class Method Details

.logger_for(name) ⇒ Object

Returns the logger to use for name, wrapped so structured payloads work regardless of the underlying logging library.



30
31
32
# File 'lib/cloudflare_access_gate.rb', line 30

def logger_for(name)
  StructuredLogger.wrap(@logger || default_logger(name))
end

.protect(app, audience:, session_key:, **gate_options) ⇒ Object

Wire the full Cloudflare Access protection stack onto a Rack app in the canonical order. Typically called on Sidekiq::Web from an initializer:

CloudflareAccessGate.protect(
Sidekiq::Web,
audience: ENV['CLOUDFLARE_ACCESS_SIDEKIQ_AUD'],
session_key: '_myapp_sidekiq_session'
)

This installs, in order:

1. Gate            - rejects requests without a valid Cf-Access JWT
2. AuditLogger     - logs who accessed the dashboard
3. ActionDispatch::Cookies
4. ActionDispatch::Session::CookieStore(key: session_key)

ActionDispatch (from Rails/actionpack) must be loaded before this is called, so .protect is for Rails hosts only. Gate and AuditLogger themselves are plain Rack and carry no Rails dependency — outside Rails, install them directly with app.use.

Extra keyword arguments are forwarded to Gate (e.g. team_domain:, jwks_stale_grace:).

Idempotent: the middleware stack is class-level, so re-calling would otherwise re-append it. Returns the app.



72
73
74
75
76
77
78
79
80
81
82
83
84
85
# File 'lib/cloudflare_access_gate.rb', line 72

def self.protect(app, audience:, session_key:, **gate_options)
  @install_mutex.synchronize do
    return app if @installed[app]

    app.use Gate, **gate_options, audience: audience
    app.use AuditLogger
    app.use ActionDispatch::Cookies
    app.use ActionDispatch::Session::CookieStore, key: session_key

    @installed[app] = true
  end

  app
end