Skip to content

OIDC authentication and authorization termination for Caddy

License

Notifications You must be signed in to change notification settings

relvacode/caddy-oidc

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Caddy OIDC

A Caddy plugin for OIDC authentication and authorization.

Inspired by oauth2-proxy but instead of requiring each application to be configured individually, perform authentication and authorization at the Caddy level.

Advantages over oauth2-proxy

  • Avoids the need to configure each application individually, with N+1 oauth2 proxies per application
  • Centralized access logging that includes user ID
  • Easier integration with security tools like fail2ban, etc
  • Anonymous access and client ip-based authorization rules
  • Support for RFC9728 (OAuth 2.0 Protected Resource Metadata)

Installation

Installation can be done either via the provided Docker image (Caddy with only caddy-oidc installed)

ghcr.io/relvacode/caddy-oidc:latest

Or by building caddy with this plugin via xcaddy

FROM caddy:builder AS builder
RUN xcaddy build \
    --with github.com/relvacode/caddy-oidc

Configuration

caddy-oidc has a global and per-route oidc directive.

The global directive is used to configure the OIDC provider. An example minimum configuration is shown below.

{
    oidc example {
        issuer https://accounts.google.com
        client_id < client_id >
    }
}

Each route then uses the oidc directive to configure the route using the named provider

example.com {
    oidc example {
        allow {
            user *
        }
    }
    reverse_proxy localhost:8080
}

Global Directive

Option Description Default
issuer The OIDC issuer URL
client_id The OIDC client ID
tls_insecure_skip_verify (optional) Skip TLS certificate verification with the OIDC provider.
scope (optional) The scope to request from the OIDC provider. The openid scope is required for browser-based login to work. openid
username (optional) The claim to use as the username. Defaults to sub. sub
protected_resource_metadata (optional) Configure or disable RFC9728 support.
authenticate (optional) Configure authentication methods

Authentication

This module uses a plugin architecture to allow different authentication methods to be configured under the Caddy plugin namespace http.oidc.authenticator.

When a request requires authentication, authentication methods are tried in the order they are configured. The first authenticator to return a valid session from the request is used. An expired session is ignored, and the next authenticator is tried.

The default configuration is shown below. These authenticators are not used if any authenticator is configured.

authenticate bearer
authenticate cookie {
    # Inherits the default cookie configuration
}
authenticate none

By default, any authentication information from any configured authenticator is stripped from the request before passing it upstream. This behavior can be disabled by adding the preserve_request option.

authenticate preserve_request

Bearer

The bearer authenticator is used to authenticate requests using a JWT bearer token. The bearer JWT must be signed by the OIDC provider.

Cookie

The cookie authenticator is used to authenticate requests using a self-signed session cookie.

Enabling session cookie authentication also enables interactive authentication through the browser via the OAuth 2.0 Authorization Code Flow.

When enabled, if the request is not authenticated, the request is made by a browser, and there is no matching explicit allow or deny rule, then the browser will be redirected to the OIDC provider for login.

Option Description
name The name of the cookie.
secret The 32 or 64 byte secret key to encrypt session cookies
domain (optional) The domain of the cookie.
path (optional) The path of the cookie.
insecure (optional) Disable secure cookies.
same_site (optional) The samesite mode of the cookie. One of lax, strict or none
claims (optional) Claims to copy into the session cookie.
redirect_url (optional) The URL to redirect to after authentication. If the URL is relative, the fully qualified URL is constructed using the request host and protocol.

The default configuration is shown below. Any individual option can be overridden whilst preserving the default values for options not specified.

authenticate cookie {
    name caddy
    same_site lax
    path /
    secret {env.COOKIE_SECRET}
    redirect_url /oauth2/callback
}

To minimize the size of the cookie, no claims are copied into the session cookie by default. Claims can be copied by specifying the claims option if needed for access policy rules or placeholder variables (e.g., for logging).

None

The none authenticator always passes authentication by returning an anonymous session.

Note

A none authenticator should always be the last configured authenticator.

Header

The header authenticator authenticates a JWT token passed via an incoming HTTP request header (without any prefix).

authenticate header X-Api-Key

Query

The query authenticator authenticates a JWT token passed via an incoming HTTP request query parameter.

Caution

There are several security implications to using query parameters for authentication. See RFC6750 for more information.

authenticate query access_token

RFC9728 Support (protected_resource_metadata)

Caddy OIDC supports RFC9728 (OAuth 2.0 Protected Resource Metadata) to discover the OIDC provider metadata via the well-known URL /.well-known/oauth-protected-resource.

If the request is unauthenticated, passes at least one allow rule, and the request is not made by a browser, then a 401 Unauthorized response is returned with a WWW-Authenticate header conforming to WWW-Authenticate Response.

Settings can be controlled via the oidc directive protected_resource_metadata. The default behavior is to enable.

# Disable RFC9728 support.
# This makes /.well-known/oauth-protected-resource return a 404 Not Found.
protected_resource_metadata off

Audience

As a custom extension to the standard, resource metadata can be configured to include the expected token audience (aud) claim.

If enabled, the metadata response will contain an additional audience field containing the configured client ID of the OIDC provider configuration.

This is designed as an alternative to dynamic client registration to let another client (e.g. a CLI) use JWT Exchange with its own token with the OIDC provider and make requests to this server without prior knowledge of this server's OAuth configuration.

# Include the expected audience field in the metadata
protected_resource_metadata {
    audience
}

Handler Directive

The handler directive is placed on routes to provide authentication and authorization for that route. Requests are authenticated according to the configured OIDC provider and then authorized according to access policy rules configured in the directive.

The handler directive must contain at least one allow rule.

# Allow any valid authenticated user

example.com {
    oidc example {
        allow {
            user *
        }
    }
    reverse_proxy localhost:8080
}

If the request is unauthenticated, and there is not an explicit allow or deny rule that matches the request, and the request is made by a browser, then the browser will be automatically redirected to the OIDC provider for authentication.

Access Rules

Each access rule can be either allow or deny. Inspired by AWS IAM policies, each request must match at least one allow rule to be authorized.

Access rules match using Caddy's regular request matchers. Additional HTTP matchers are provided for authentication-specific request matching.

Caution

Without an explicit user match in an allow policy rule, all requests will be allowed, even anonymous requests.

If a request matches any deny rule then the request is denied, even if another allow rule matches.

# Allow any authenticated user from example.com except from steve

oidc example {
    allow {
        user *@example.com
    }
    deny {
        user steve@example.com
    }
}

Access rules can be optionally named for logging, if matched then the rule ID will be available as the placeholder variable {http.auth.rule}.

oidc example {
    deny "DenyAnonymousAccess" {
        anonymous
    }
    allow "AllowAnyUserReadAccess" {
        method GET HEAD
        user *
        claim role read
    }
    allow "AllowAdminWriteAccess" {
        method POST PUT PATCH DELETE
        user *
        claim role write
    }
}

HTTP Matchers

In addition to the standard Caddy request matchers, the following matchers are provided. These matchers are only compatible with HTTP requests handled by the handler directive.

User

Matches the username of the authenticated user. A user match will never match an anonymous user.

# Allow any authenticated user

allow {
    user *
}
# Allow any authenticated user from example.com

allow {
    user *@example.com
}
# Allow multiple users

allow {
    user steve
    user bob
    user john
}

Anonymous

Matches request sessions that are anonymous. Anonymous sessions are sessions that have not been authenticated by the OIDC provider.

# Allow anonymous requests to /healthcheck

allow {
    anonymous
    path /healthcheck
}

Claim

Matches claims in the request session.

If the session claim is an array, then the request must match at least one value in the array. Any non-string claim values are ignored and will not match.

Multiple values for a single claim directive are treated as a logical OR. If no values are specified, the matcher only checks for the claim's existence.

Note

Any claims used here must be configured in the cookie authenticator if used.

# Allow requests containing role = write

allow {
    claim role write
}
# Allow requests containing role = read OR role = write

allow {
    claim role read write
}
# Allow requests containing role = read AND role = write

allow {
    claim role read
    claim role write
}
# Allow requests containing sub = steve@example.com AND role = read

allow {
    claim sub steve@example.com
    claim role read
}
# Deny all requests missing the 'role' claim

deny {
    not {
        claim role
    }
}

Replacer variables are supported in both claim name and claim value.

# Allow requests containing host = {http.host}

allow {
    claim host {http.host}
}

Wildcard matching is also supported in claim values.

# Allow requests where the role claim starts with "read:"

allow {
    claim role read:*
}

Auth Method

Matches the authentication method used to authenticate the request.

Possible values are

  • none - The request is not authenticated
  • cookie - The request was authenticated using a session cookie
  • bearer - The request was authenticated using a bearer JWT token
# Deny all requests using JWT bearer authentication

deny {
    auth_method bearer
}

Placeholder Variables

When a request passes through the oidc handler, the following placeholder variables are available:

Placeholder Description
http.auth.user.id The username extracted from the username option of the global directive
http.auth.user.anonymous true if the session is not authenticated otherwise false
http.auth.method The authentication method of the request. One of none, cookie or bearer
http.auth.user.claim.* Set for each claim provided by the matched authenticator
http.auth.rule The named access policy rule that matched the request
http.auth.result The acccess rule evaluation result. One of allow, implicit deny or explicit deny

Because the oidc handler is ordered after the header handler, to set these variables in response headers, you must use the defer option

header X-User-Claim-Email {http.auth.user.claim.email} {
    defer
}

Claim Value Formatting

  • Simple values like strings, booleans, and numbers are formatted as plain values
  • Null values are empty
  • Objects are formatted as JSON
  • Arrays are formatted using the above rules for each element, joined by commas. Nested arrays are formatted as JSON

About

OIDC authentication and authorization termination for Caddy

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors 2

  •  
  •