caddy_saml_sso

package module
v0.0.12 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Feb 5, 2026 License: Apache-2.0 Imports: 20 Imported by: 0

README

caddy-saml-sso (a caddy plugin for SAML SSO)

saml for the befuddled

A caddy module that provides SSO via SAML. For the SAML implementation we use this wonderful library.

Just write your app behind Caddy with the module enabled and Caddy will proxy the request to your app only if the user has a valid SAML session.

When enabling this module on your routes, if the SAML flow is successful, you will have all the SAML attributes in the header so your application can access them.

⚠️ Warning: SAML is a notoriously difficult to implement right and prone to serious implementation mistakes. Please use this code with caution.

Process to use it.

  1. We want the caddy server with this module enabled (see build).
  2. Use the .env.dev to setup the details of your SAML idp.
  3. Run caddy using the Caddyfile as a reference. Test the root path to make sure all works correctly.

You also can run all this within docker: one container for Caddy another one for your application. See docker-compose.yml for more details.

We need a SAML IDP for testing. I have used testing SAML IDP in the past but it is now a parked domain. You'll have to find an alternative. Please reach out if you find one.

Build

make build will caddy with the plugin.

make saml-cert will create a directory with the cert and key necessary to sign xml documents.

Configuration Options

Directive Required Description
saml_idp_url Yes URL to your SAML IdP metadata endpoint
saml_cert_file Yes Path to your SP's X.509 certificate file
saml_key_file Yes Path to your SP's private key file
saml_root_url Yes Base URL of your service provider
saml_entity_id No Custom Entity ID for the SP (defaults to root URL)
saml_userid_claim No SAML attribute to use as the user identifier (sets X-Remote-User header)
saml_claims No Comma-separated list of SAML claims to pass as HTTP headers and Caddy variables
saml_cookie_name No Session cookie name (default: token)
saml_cookie_samesite No Cookie SameSite policy: strict, lax, or none (default: lax)
saml_remote_user_var No Caddy variable name for the authenticated user (default: REMOTE_USER)
saml_var_prefix No Prefix for SAML claim variables (default: SAML_)

HTTP Headers and Caddy Variables

After successful SAML authentication, the module sets the following:

Source HTTP Header Caddy Variable PHP $_SERVER
saml_userid_claim X-Remote-User REMOTE_USER (configurable) HTTP_X_REMOTE_USER
saml_claims X-SAML-{CLAIM} SAML_{CLAIM} (configurable prefix) HTTP_X_SAML_{CLAIM}

For example, with saml_claims email,displayName,groups:

HTTP Header Caddy Variable PHP $_SERVER
X-Remote-User {vars.REMOTE_USER} $_SERVER['HTTP_X_REMOTE_USER']
X-SAML-EMAIL {vars.SAML_EMAIL} $_SERVER['HTTP_X_SAML_EMAIL']
X-SAML-DISPLAYNAME {vars.SAML_DISPLAYNAME} $_SERVER['HTTP_X_SAML_DISPLAYNAME']
X-SAML-GROUPS {vars.SAML_GROUPS} $_SERVER['HTTP_X_SAML_GROUPS']

This works with FrankenPHP, FastCGI, reverse proxy, and any HTTP-based backend.

Caddyfile example

# https://caddyserver.com/docs/caddyfile/options#order
{
	order saml_sso before header
}

(enable_saml) {
  saml_sso {
    saml_idp_url         {$SAML_IDP_URL}
    saml_cert_file       {$SAML_CERT_FILE}
    saml_key_file        {$SAML_KEY_FILE}
    saml_root_url        {$SAML_ROOT_URL}
    saml_userid_claim    email
    saml_claims          email,displayName,groups
    saml_cookie_samesite lax
  }
}

https://foo.bar.net:12000 {
  handle /ping {
    respond "pong"
  }

  import enable_saml
  respond "you are authenticated now."
}

In this Caddyfile we have a TLS server on foo.bar.net:12000. The first handler /ping is not protected and we use it for testing. The second handler handles the rest of the traffic. It loads the saml_sso plugin and runs the middleware it provides.

The middleware passes SAML requests (/saml) to the SAML library. For other paths, it runs the SAML middleware to make sure that each requests has a valid SAML session. If not, it will redirect the user.

If all goes well, caddy will continue with the next middleware, in this case we send an "ok" back. Here you probably want to redirect all traffic to your app server.

Caddyfile example (proxy requests to app)

# https://caddyserver.com/docs/caddyfile/options#order
{
	order saml_sso before header
}

(enable_saml) {
  saml_sso {
    saml_idp_url   {$SAML_IDP_URL}
    saml_cert_file {$SAML_CERT_FILE}
    saml_key_file  {$SAML_KEY_FILE}
    saml_root_url  {$SAML_ROOT_URL}
  }
}

http://:12000 {
  handle /ping {
    respond "pong"
  }

  import enable_saml

  reverse_proxy saml-app:8182 {
    # https://caddyserver.com/docs/caddyfile/directives/reverse_proxy#headers
    header_up email {http.response.header.mail}
    header_up displayname {http.response.header.displayname}
  }
}

Same as before, but we now proxy the request to a process.

Testing

You'll need a SAML IDP for testing this.

  1. If you are running the server (caddy) in your local machine you are going to have some kind of tunnel setup to map the https traffic to your local machine.

  2. Create a .env.dev in the root of the project with:

SAML_IDP_URL=https://theipd/saml/idp
SAML_CERT_FILE=saml-cert/service.cert
SAML_KEY_FILE=saml-cert/service.key
SAML_ROOT_URL=https://caddy-saml.mydomain.net
DOMAIN=caddy-saml.mydomain.net

You probably only want to change the last two values.

  1. Start the server make and get your SPs metadata:
$ make metadata > /save/here.xml
  1. Upload your metadata to the IDP. Now you have established the trust between your SP and the IDP.

  2. Fire up your browser and point it to: $(SAML_ROOT_URL). If all goes well the SAML flow should keep in and you will be asked to enter a login and password then, if the credentials are correct (the credentials are in the webpage you just have to copy them) the IDP will redirect the browser to root page of your SP.

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func NewSaml

func NewSaml(opts samlsp.Options, claims []string) (*samlsp.Middleware, error)

NewSaml creates a new Middleware with the default providers for the given options.

You can customize the behavior of the middleware in more detail by replacing and/or changing Session, RequestTracker, and ServiceProvider in the returned Middleware.

func SamlSessionProvider

func SamlSessionProvider(opts samlsp.Options, claims []string) samlsp.CookieSessionProvider

SamlSessionProvider returns the default SessionProvider for the provided options, a CookieSessionProvider configured to store sessions in a cookie.

Types

type Attributes

type Attributes map[string][]string

Attributes is a map of attributes provided in the SAML assertion

func (Attributes) Get

func (a Attributes) Get(key string) string

Get returns the first attribute named `key` or an empty string if no such attributes is present.

type Middleware

type Middleware struct {
	SamlIdpUrl         string   `json:"saml_idp_url,omitempty"`
	SamlCertFile       string   `json:"saml_cert_file,omitempty"`
	SamlKeyFile        string   `json:"saml_cert_key,omitempty"`
	SamlRootUrl        string   `json:"saml_root_url,omitempty"`
	SamlEntityID       string   `json:"saml_entity_id,omitempty"`
	SamlUserIdClaim    string   `json:"saml_userid_claim,omitempty"`
	SamlClaims         []string `json:"saml_claims,omitempty"`
	SamlCookieName     string   `json:"saml_cookie_name,omitempty"`
	SamlCookieSameSite string   `json:"saml_cookie_samesite,omitempty"`
	SamlRemoteUserVar  string   `json:"saml_remote_user_var,omitempty"`
	SamlVarPrefix      string   `json:"saml_var_prefix,omitempty"`
	SamlSP             *samlsp.Middleware
	SamlHandler        http.Handler
}

Holds all the module's data

func (Middleware) CaddyModule

func (Middleware) CaddyModule() caddy.ModuleInfo

CaddyModule returns the Caddy module information.

func (*Middleware) Provision

func (m *Middleware) Provision(ctx caddy.Context) error

Provision implements caddy.Provisioner.

func (*Middleware) ServeHTTP

func (m *Middleware) ServeHTTP(w http.ResponseWriter, r *http.Request, next caddyhttp.Handler) error

ServeHTTP implements caddyhttp.MiddlewareHandler.

func (*Middleware) UnmarshalCaddyfile

func (m *Middleware) UnmarshalCaddyfile(d *caddyfile.Dispenser) error

UnmarshalCaddyfile implements caddyfile.Unmarshaler.

func (*Middleware) Validate

func (m *Middleware) Validate() error

Validate implements caddy.Validator.

type SamlJWTSessionClaims

type SamlJWTSessionClaims struct {
	jwt.StandardClaims
	Attributes  Attributes `json:"attr"`
	SAMLSession bool       `json:"saml-session"`
}

SamlJWTSessionClaims represents the JWT claims in the encoded session

func (SamlJWTSessionClaims) GetAttributes

func (c SamlJWTSessionClaims) GetAttributes() Attributes

GetAttributes implements SessionWithAttributes. It returns the SAMl attributes.

type SamlJWTSessionCodec

type SamlJWTSessionCodec struct {
	SigningMethod jwt.SigningMethod
	Audience      string
	Issuer        string
	MaxAge        time.Duration
	Key           crypto.Signer
	Claims        []string
}

SamlJWTSessionCodec implements SessionCoded to encode and decode Sessions from the corresponding JWT.

func SamlSessionCodec

func SamlSessionCodec(opts samlsp.Options, claims []string) SamlJWTSessionCodec

SamltSessionCodec returns the default SessionCodec for the provided options, a SamlJWTSessionCodec configured to issue signed tokens.

func (SamlJWTSessionCodec) Decode

func (c SamlJWTSessionCodec) Decode(signed string) (samlsp.Session, error)

Decode parses the serialized session that may have been returned by Encode and returns a Session.

func (SamlJWTSessionCodec) Encode

Encode returns a serialized version of the Session.

The provided session must be a SamlJWTSessionClaims, otherwise this function will return an error.

func (SamlJWTSessionCodec) New

func (c SamlJWTSessionCodec) New(assertion *saml.Assertion) (samlsp.Session, error)

New creates a Session from the SAML assertion.

The returned Session is a SamlJWTSessionClaims.

Directories

Path Synopsis
saml-app
go command

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL