Skip to content

Forward authentication service that provides OIDC authentication and/or opaque token validation for the traefik reverse proxy.

License

Notifications You must be signed in to change notification settings

Creoox/cx-traefik-forward-auth

Repository files navigation

Creoox Traefik Forward-Auth

Inspired by traefik-forward-auth by thomseddon (MIT License).

cx-traefik-forward-auth is a standalone authorization middleware for Traefik that provides OIDC authentication and/or opaque token validation (introspection) for the traefik reverse proxy. It's main goal is to work as authentication feature in API Gateway solution that Traefik provides. At the current state of implementation, the authentication is based on reading the Authorization header from the request and verifying it.

"Authorization": "Bearer <access-token>"

Basic Authentication

There are two types of verification possible:

In both cases the introspection endpoint or provider signature keys that are needed to verify the token are read from its discovery endpoint from OIDC_ISSUER_URL variable. Please mind two aspects:

  1. introspection is only possible if the client secret was provided.
  2. Currently decoding of symetrical (e.g. HS256) or eliptical (e.g. ES256 - TODO) keys is not supported.

In addition to that, it is possible to use this service to obtain the token (so it acts as OIDC Client). In order to that the LOGIN_WHEN_NO_TOKEN should be set to true.

WARNING! This feature is NOT meant to be used on production.

Login Authentication

At the current state of implementation, two authentication flows are possible (both OIDC-conformant):

Currently tested providers:

Provider Version Result Comment
Keycloak 17.1
ZITADEL 2.64.1
SAP Commerce ?
Google ? Planned
GitHub

Project usage

Prerequisites

  1. Prepared traefik-based infrastructure
  2. [OPTIONAL] ModHeader to include authorization header in browser request

Variables


Environmental variables:
Variable Name Type Obligatory Comment
APP_NAME string No Displayed service (app) name
APP_VERSION string No Displayed service (app) version
APP_PORT int No Service running port
HOST_URI string Yes URI of the host the service is running on
ENVIRONMENT string Yes 'development' or 'production'
OIDC_ISSUER_URL string Yes Main Issuer's URL - all data are retrieved from there
OIDC_CLIENT_ID string Yes OIDC client id
OIDC_CLIENT_SECRET string No OIDC client secret (if set)
OIDC_VERIFICATION_TYPE string Yes 'jwt' - decoding or 'introspection' - asking AS
JWT_STRICT_AUDIENCE boolean Yes true if token should be used for strict audinence only
JWT_TOKEN_TYPE string No Used token, either 'access_token' (default) or 'id_token'
AUTH_ENDPOINT string No Service redirection endpoint, '/_oauth' by default
AUTH_ALLOW_UNSEC_OPTIONS boolean No Allow unsecured OPTIONS request, false by default
LOGIN_WHEN_NO_TOKEN boolean Yes true if login functionality should be on (dev only!)
LOGIN_AUTH_FLOW string No 'code' (default) or 'id_token token' (implicit flow)
LOGIN_SCOPE string No Requested scope(s), defaults to "openid email profile"
LOGIN_COOKIE_NAME string No Name of the browser cookie, only if LOGIN_WHEN_NO_TOKEN=true
LOGIN_SESSION_SECRET string No Randomized secret for cookie-session
AUTH_ROLES_STRUCT string No Structure of roles (list) in token payload (dot notation)
AUTH_ROLE_NAME string No Role name to check in token roles

Please mind that if AUTH_ALLOW_UNSEC_OPTIONS is set to true, then the endpoint that should accept OPTIONS request, should provide separate rule for that and pass X-Forwarded-Method: OPTIONS header to cx-traefik-forward-auth there, for instance (docker).

    ...
    labels:
      - "traefik.enable=true"
      - "traefik.http.middlewares.add-options-header.headers.customrequestheaders.X-Forwarded-Method=OPTIONS"
      - "traefik.http.routers.your-endpoint-options.rule=Host(`your-endpoint.com`) && Method(`OPTIONS`)"
      - "traefik.http.routers.your-endpoint-options.middlewares=add-options-header,cx-traefik-forward-auth"
      ...

Additionally, both AUTH_ROLES_STRUCT and AUTH_ROLE_NAME have to be either set or empty. Object dot notation is presented below:

resource_access.dummy-client.roles

and is equall to JSON notation:

...
"resource_access": {
  "dummy-client": {
    "roles": [...],
  },
},

Examples

Docker Standalone:
traefik:
    image: traefik:latest
    container_name: cx-example-traefik
    restart: unless-stopped
    security_opt:
      - no-new-privileges:true
    networks:
      - cx-example-net
    ports:
      - 80:80
      - 443:443
    volumes:
      - /etc/localtime:/etc/localtime:ro
      # - /var/run/docker.sock:/var/run/docker.sock:ro
      - ./traefik/traefik.toml:/etc/traefik/traefik.toml:ro
      - ./traefik/services.toml:/etc/traefik/services.toml:ro
      - ./traefik/acme.json:/etc/traefik/acme.json
      - ./logs/traefik-access.log:/traefik-access.log
      - ./logs/traefik-service.log:/traefik-service.log
    labels:
      - "traefik.enable=true"
      - "traefik.http.middlewares.traefik-https-redirect.redirectscheme.scheme=https"

      - "traefik.http.routers.traefik.entrypoints=web"
      - "traefik.http.routers.traefik.rule=Host(`localhost`)"
      - "traefik.http.routers.traefik.middlewares=traefik-https-redirect"

      - "traefik.http.routers.traefik-secure.entrypoints=websecure"
      - "traefik.http.routers.traefik-secure.rule=Host(`localhost`)"
      - "traefik.http.routers.traefik-secure.tls=true"
      - "traefik.http.routers.traefik-secure.tls.certresolver=hypercpq"
      - "traefik.http.routers.traefik-secure.service=api@internal"
      - "traefik.http.routers.traefik-secure.middlewares=traefik-forward-auth"

  # https://doc.traefik.io/traefik/providers/docker/#docker-api-access
  socket-proxy:
      image: tecnativa/docker-socket-proxy
      container_name: cx-example-socket-proxy
      restart: unless-stopped
      volumes:
        - /var/run/docker.sock:/var/run/docker.sock:ro
      environment:
        CONTAINERS: 1
      networks:
        - cx-example-net

  traefik-forward-auth:
    image: creoox/cx-traefik-forward-auth:1.1.5
    container_name: cx-example-traefik-forward-auth
    env_file:
      - ./cx-traefik-forward-auth.env
    networks:
      - cx-example-net
    labels:
      - "traefik.enable=true"
      - "traefik.docker.network=cx-example-net"
      - "traefik.http.middlewares.traefik-forward-auth.forwardauth.address=http://traefik-forward-auth:4181"
      - "traefik.http.middlewares.traefik-forward-auth.forwardauth.authResponseHeaders=X-Forwarded-User"
      - "traefik.http.services.traefik-forward-auth.loadbalancer.server.port=4181"

Docker Swarm:

Not tested -> TODO


Kubernetes:

Not implemented -> TODO


Project setup (containerized)

Prerequisites

  1. docker
  2. docker-compose
  3. [Optional & HIGHLY Recommended] GNU make (see below)

GNU make - Make use of Makefile

It is recommended to make use of make commands and in order to do so install GNU make

  • Unix/Linux -> ready-to-go more info
  • Windows (Powershell) -> install chocolatey and then run choco install make in Powershell
  • MacOS -> for most recent versions you should be ready-to-go, if not try installing it with homebrew

Environments setup

Mind that all below commands can be run natively using docker-compose (not recommended, see Makefile for details)


Development Environment:

Prepare development environment

make build-dev-env

Run development environment

make run-dev-env

Run unit tests (in separate container)

make run-unit-tests

Run unit tests with coverage HTML-report (in separate container)

make run-ut-coverage-html

Run lint check (in separate container)

make run-lint-check

Shut down and clean development environment

make down-dev-env

Production Environment:

Prepare production environment

make pull-prod-env

You may use make build-prod-env for environment build, mind that it's meant for developers only!

Run production environment

make run-prod-env

Shut down and clean production environment

make down-prod-env