Apereo CAS - Dockerized Deployment via Traefik Reverse Proxy

Posted by Misagh Moayyed on September 26, 2020 · 10 mins read ·

Traefik is an open-source cloud-native, modern reverse proxy and edge Router that makes publishing services quite simple. Its key characteristic is that it can automatically discover the right configuration for services as it inspects infrastructure to find relevant information on which service serves which request.

In this post, we will take a look at how Apereo CAS can be deployed via Docker and sit behind Traefik. A dockerized CAS deployment is an existing CAS overlay project wrapped in Spring Boot, Docker, and Docker Compose. This setup requires a few extra modifications in order to allow an additional integration with Traefik for http and https access.

Our starting position is as follows:

HTTP Setup

The first step is to allow the CAS server to serve requests on a designated port 8080 and under http. To do this, the cas.properties file should be adjusted to include the following settings:

server.ssl.enabled=false

server.port=8080

server.tomcat.remoteip.protocol-header-https-value=http

Port Detection

You can choose any port you prefer, but you do need to make sure the same port is exposed in the Dockerfile for the CAS server via the EXPOSE directive. Traefik retrieves the private IP and port of containers from the Docker API as part of its auto-configuration and discovery strategy.

Ports detection works as follows:

  • If a container exposes only one port, then Traefik uses this port for private communication.
  • If a container exposes multiple ports, or does not expose any port, then you must manually specify which port Traefik should use for communication by using the label traefik.http.services.cas.loadbalancer.server.port

Note that the CAS Dockerfile found in the overlay project does include EXPOSE 8080 8443 for port exposure.

Docker Setup

Next, the docket-compose.yml file should be adjusted to pull down the Traefik docker image and auto-configure it:

version: '3.8'
services:
  cas:
    build: .
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.cas.rule=HOST(`auth.example.org`)"
      - "traefik.http.services.cas.loadbalancer.server.port=8080"

  traefik:
    image: traefik:v2.3.0
    ports:
      - "80:80"
      - "8080:8080"
    volumes:
      - "/var/run/docker.sock:/var/run/docker.sock:ro"
      - "$PWD/traefik.toml:/etc/traefik/traefik.toml"

The labels will be read later by Traefik to auto-configure the service. Specifically,

  • traefik.enable ensures that Traefik sees our CAS container and routes traffic to it. This directive can be replaced with exposedByDefault = true in the Traefik configuration.
  • traefik.http.routers.cas.rule create a cas router rule for Traefik that allows it to route traffic to the CAS container if the host header matches auth.example.org.
  • traefik.http.services.cas.loadbalancer.server.port specifies the target destination port for traffic into the running service.

The Traefik container itself is exposed over port 80 for routing traffic and we also allow for port 8080 which grants access to the Traefik dashboard. We are also mapping two volumes:

  • The first volume makes Traefik aware of Docker containers.
  • The second volume mounts a traefik.toml configuration file inside the Traefik container which is loaded by Traefik on startup.

At this point, the only remaining task is to create and design the traefik.toml file.

[entryPoints]
  [entryPoints.cas]
    address = ":80"

[api]
insecure = true

[log]
level = "INFO"

[accessLog]

[providers]
  [providers.docker]
    exposedByDefault = true

Build & Deploy

We can use the following to build and run our Docker containers:

docker-compose down && docker-compose up --build

Please be patient, as doing a build for the first time might take a while depending on your bandwidth. Once ready, you should be able to browse to http://localhost:8080/dashboard and access the Traefik dashboard:

image

We can also examine our CAS router and its configuration:

image

Of course, you should be able to get to the CAS server using http://auth.example.org/cas/login.

For extra credit, you can in fact examine the CAS service and its configuration that is automatically discovered and configured by Traefik to note the expected port:

image

HTTPS Setup

Setting up TLS is quite similar to previous steps. First, we need to make sure the correct protocol header that is passed from Traefik to CAS is defined as https:

server.tomcat.remoteip.protocol-header-https-value=https

Our docker-compose.yml file must be adjusted to turn on TLS, relevant ports and map volumes and certificates:

version: '3.8'
services:
  cas:
    build: .
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.cas.rule=HOST(`auth.example.org`)"
      - "traefik.http.services.cas.loadbalancer.server.port=8080"
      - "traefik.http.routers.cas.tls=true"

  traefik:
    image: traefik:v2.3.0
    ports:
      - "80:80"
      - "443:443"
      - "8080:8080"
    volumes:
      - "/var/run/docker.sock:/var/run/docker.sock:ro"
      - "$PWD/traefik.toml:/etc/traefik/traefik.toml"
      - "$PWD/server.crt:/etc/traefik/server.crt"
      - "$PWD/server.key:/etc/traefik/server.key"
      - "$PWD/certificates.toml:/etc/traefik/certificates.toml"

The most notable differences are,

  • The traefik.http.routers.cas.tls label enables TLS for the CAS service.
  • Port 443 is now enabled to front HTTPS requests.
  • We are mapping the server private key and certificate into the Traefik container to support HTTPS requests. Certificates and keys can be generated using openssl and Traefik also has excellent support for Let’s Encrypt and other ACME providers for automatic certificate generation.
  • An additional certificates.toml how the above private key and certificate should be loaded by Traefik.

The certificates.toml file simply points to the certificates that are mapped inside the Traefik container:

[[tls.certificates]] 
   certFile = "/etc/traefik/server.crt"
   keyFile = "/etc/traefik/server.key"

[tls.stores]
  [tls.stores.default]
    [tls.stores.default.defaultCertificate]
      certFile = "/etc/traefik/server.crt"
      keyFile  = "/etc/traefik/server.key"

Finally, we should adjust the Traefik configuration file to enable redirection from port 80 to 443 and specify how Traefik should load our certificate configuration file:

[entryPoints]
  [entryPoints.cas]
    address = ":80"
  [entryPoints.cas.http]
    [entryPoints.cas.http.redirections]
      [entryPoints.cas.http.redirections.entryPoint]
        to = "websecure"
        scheme = "https"
  [entryPoints.websecure]
    address = ":443"

[api]
insecure = true

[providers.file]
  filename = "/etc/traefik/certificates.toml"

[log]
level = "INFO"

[accessLog]

[providers]
  [providers.docker]
    exposedByDefault = true

Build & Deploy

If you rebuild and launch the containers again, the Traefik dashboard should indicate that port 443 is enabled to serve secure traffic:

image

Of course, our router setup also should indicate that TLS is now activated:

image

At this point, you should be able to get to the CAS server using https://auth.example.org/cas/login.

So…

I hope this review was of some help to you and I am sure that both this post as well as the functionality it attempts to explain can be improved in any number of ways. Please know that all other use cases, scenarios, features, and theories certainly are possible as well. Feel free to engage and contribute as best as you can.

Happy Coding,

Misagh Moayyed