LDAP Authentication

TraefikEE can integrate with LDAP in order to restrict the access to applications.

Authentication in TraefikEE is implemented as a middleware. To avoid having sensitive information such as LDAP credentials specified as labels (or in CRDs) by applications and to allow multiple middlewares to reuse the same authentication method, the reusable portion of the configuration is externalized in Authentication Sources.

Authentication Sources are defined in the static configuration of the cluster and are referenced by authentication middlewares.

Authentication Source Configuration

Before configuring an LDAP middleware, an LDAP Authentication Source must be defined in the static configuration.

Below is an example of a minimal LDAP Authentication Source that can be added to a static configuration:

[authSources]
  [authSources.ldapSource]
    [authSources.ldapSource.ldap]
      url = "ldaps://ldap.example.org:636"
authSources:
  ldapSource:
    ldap:
      url: ldaps://ldap.example.org:636

Please refer to the complete static configuration reference to see all the available options.

Connection Pooling

In order to minimize the impact on the performance of requests using an LDAP authentication middleware, TraefikEE keeps a pool of connections to the LDAP server and reuses them instead of opening a new connection for each request. Pooled connections are still meant to be short-lived, so they are closed after roughly one minute by default. This behavior can be modified with the ttl property.

The number of connections managed by the pool can be customized with the size property. burst connections are ephemeral connections that are opened when the pool is already full. Once the number of connection exceeds size + burst a Too Many Connections error is returned.

LDAP Middleware

After declaring an LDAP Authentication Source in the static configuration of the cluster, LDAP middlewares can be added to routers.

The LDAP middleware will look for user credentials in the Authorization header of each request. Credentials must be encoded with the following format: base64(username:password).

Bind Mode vs Search Mode

If no filter is specified in its configuration, the middleware runs in the default bind mode, meaning that it tries to make a simple bind request to the LDAP server with the credentials provided in the request headers. If the bind succeeds, the middleware forwards the request, otherwise it returns a 401 Unauthorized status code.

If a filter query is specified in the middleware configuration and the Authentication Source referenced has a bindDN and a password, then the middleware runs in search mode. In this mode, a search query with the given filter is issued to the LDAP server before trying to bind. If result of this search returns only 1 record, it tries to issue the bind request with this record to LDAP, otherwise it aborts a 401 Unauthorized status code.

Note

Filter queries can use the %s placeholder that is replaced by the username provided in the Authorization header of the request. For example: (&(objectClass=inetOrgPerson)(gidNumber=500)(uid=%s)).

WWW-Authenticate Header

If the LDAP middleware receives a request with a missing or invalid Authorization header and wwwAuthenticateHeader is enabled, it will set a WWW-Authenticate header in the 401 Unauthorized response. See the WWW-Authenticate header documentation for more information.

Note

The realm directive of the WWW-Authenticate header can be customized with the wwwAuthenticateHeaderRealm option.

Configuration Examples

For applications to be secured with LDAP authentication, an ldapAuth middleware must be created and enabled in the router. Here are some examples of the middleware configuration in its simplest form depending on the provider you're using:

labels:
  - "traefik.http.middlewares.test-ldapAuth.plugin.ldapAuth.source=ldapSource"
  - "traefik.http.middlewares.test-ldapAuth.plugin.ldapAuth.baseDN=dc=example,dc=org"
apiVersion: traefik.containo.us/v1alpha1
kind: Middleware
metadata:
  name: test-ldapAuth
spec:
  plugin:
    ldapAuth:
      source: ldapSource
      baseDN: dc=example,dc=org
[http.middlewares]
  [http.middlewares.test-ldapAuth.plugin.ldapAuth]
    source = "ldapSource"
    baseDN = "dc=example,dc=org"
http:
  middlewares:
    test-inflightreq:
      plugin:
        ldapAuth:
          source: ldapSource
          baseDN: dc=example,dc=org

Below is a more advanced configuration example using search, bind and the WWW-Authenticate header:

labels:
  - "traefik.http.middlewares.test-ldapAuth.plugin.ldapAuth.source=ldapSource"
  - "traefik.http.middlewares.test-ldapAuth.plugin.ldapAuth.baseDN=dc=example,dc=org"
  - "traefik.http.middlewares.test-ldapAuth.plugin.ldapAuth.searchFilter=(&(objectClass=inetOrgPerson)(gidNumber=500)(uid=%s))"
  - "traefik.http.middlewares.test-ldapAuth.plugin.ldapAuth.forwardUsername=true"
  - "traefik.http.middlewares.test-ldapAuth.plugin.ldapAuth.forwardUsernameHeader=Custom-Username-Header-Name"
  - "traefik.http.middlewares.test-ldapAuth.plugin.ldapAuth.wwwAuthenticateHeader=true"
  - "traefik.http.middlewares.test-ldapAuth.plugin.ldapAuth.wwwAuthenticateHeaderRealm=traefikee"
apiVersion: traefik.containo.us/v1alpha1
kind: Middleware
metadata:
  name: test-ldapAuth
spec:
  plugin:
    ldapAuth:
      source: ldapSource
      baseDN: dc=example,dc=org
      searchFilter: (&(objectClass=inetOrgPerson)(gidNumber=500)(uid=%s))
      forwardUsername: true
      forwardUsernameHeader: Custom-Username-Header-Name
      wwwAuthenticateHeader: true
      wwwAuthenticateHeaderRealm: traefikee
[http.middlewares]
  [http.middlewares.test-ldapAuth.plugin.ldapAuth]
    source = "ldapSource"
    baseDN = "dc=example,dc=org"
    searchFilter = "(&(objectClass=inetOrgPerson)(gidNumber=500)(uid=%s))"
    forwardUsername = true
    forwardUsernameHeader = "Custom-Username-Header-Name"
    wwwAuthenticateHeader = true
    wwwAuthenticateHeaderRealm = "traefikee"
http:
  middlewares:
    test-inflightreq:
      plugin:
        ldapAuth:
          source: ldapSource
          baseDN: dc=example,dc=org
          searchFilter: (&(objectClass=inetOrgPerson)(gidNumber=500)(uid=%s))
          forwardUsername: true
          forwardUsernameHeader: Custom-Username-Header-Name
          wwwAuthenticateHeader: true
          wwwAuthenticateHeaderRealm: traefikee