Apereo CAS Kubernetes Deployment

Deniz G
4 min readJul 28, 2021

--

In this article, I will talk about how to deploy CAS application to the kubernetes environment and some tricks about the problems I encountered during this deployment. The main topics will be kubernetes manifest files (deployment, service, ingress controller and secret) and SSL offloading.

Apereo CAS

If you’re looking at this tutorial, I’m assuming you already have a developed CAS application. So, I only talk about a small configuration in this section.

server.port=8443
cas.server.name=https://test-login.example.com
server.ssl.enabled=true
...

The key part here is the value given to ${cas.server.name}. This value corresponds to hostname that I will define in the ingress controller.

Deployment

apiVersion: apps/v1
kind: Deployment
metadata:
name: apereocas
namespace: medium-test
labels:
name: apereocas
spec:
replicas: 1
selector:
matchLabels:
app: apereocas
template:
metadata:
labels:
app: apereocas
spec:
containers:
- name: apereocas
image: <image-name>
ports:
- containerPort: 8443
protocol: TCP

There is nothing surprising in this part. Just look at the 8443 containerPort definition (which is also defined in the cas properties file).

Service

kind: Service
apiVersion: v1
metadata:
name: apereocas-service
namespace: medium-test
spec:
ports:
- protocol: TCP
port: 443
targetPort: 8443
selector:
app: apereocas
type: ClusterIP

There are 2 option to access CAS application running on kubernetes: defining NodePort or ClusterIP as service type. If you define NodePort, you can access your CAS app directly with <kubernetes-ip:nodeport> without ingress controller. In this case, you should pass <kubernetes-ip:nodeport> as the ${cas.server.name} value to your cas properties file. If you define ClusterIP, your service is not publicly accessible. In this case, you need to define ingress controller for external access.

Ingress Controller

apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
name: cas-ingress
namespace: medium-test
annotations:
nginx.ingress.kubernetes.io/backend-protocol: "https"
nginx.ingress.kubernetes.io/configuration-snippet: |
proxy_cookie_flags ~ secure samesite=none;

spec:
tls:
- hosts:
- test-login.example.com
secretName: cas-secret-tls
rules:
- host: test-login.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
serviceName: apereocas-service
servicePort: 443

2 important parts here.

  • Ingress must be set to use SSL. The secure=true attribute is set for the cookies that will return from CAS to the client. If ingress doesn’t have SSL, there will be insecure communication between client and ingress and cookies will be blocked.
  • Cookies that will return from cas to client will be blocked by the browser due to SameSite attribute. Therefore, the SameSite attribute should be set to None on ingress controller before cas generated cookies return to the client. (proxy_cookie_flags ~ secure samesite=none;)
if SameSite attribute is not set

Does setting SameSite=None negatively affect security?

No. The SameSite attribute was developed to take additional protection against CSRF. Attention! This is not the real protection (https://security.stackexchange.com/questions/234386/do-i-still-need-csrf-protection-when-samesite-is-set-to-lax), it’s just an addition. It is not and cannot be a stand-alone solution. There are primary solution methods such as Synchronizer Token Pattern, Cookie-to-Header Token and Double-Submit Cookie. Apereo CAS already uses Double-Submit Cookie method.

Secret

We defined a secret called cas-secret-tls in ingress controller. Let’s create this.

First of all, we need to create a certificate (I prefer using openssl).

openssl req -x509 -nodes -days 3650 -newkey rsa:2048 -keyout cas-selfsigned.key -out cas-selfsigned.crt -subj "/CN=test-login.example.com" -addext "subjectAltName = DNS:test-login.example.com"

Public key and private key values should be extracted from this certificate.

> base64 -w0 cas-selfsigned.crt
copy output
> base64 -w0 cas-selfsigned.key
copy output

Use them in secret manifest

apiVersion: v1
kind: Secret
metadata:
name: cas-secret-tls
namespace: medium-test
data:
tls.crt: <paste crt output>
tls.key: <paste key output>
type: kubernetes.io/tls

At final step, we must add the ingress endpoint to our local DNS (/etc/hosts in Linux, C:/Windows/System32/drivers/etc/hosts in Windows).

endpoint value in development kubernetes environment
127.0.0.1 test-login.example.com

Now, ready to be tested at https://test-login.example.com/cas/login

SSL Offloading

flow overview

There are 2 connections in this flow; between client-ingress and then ingress controller-pods. Secure communication is provided with SSL on both connections.

But it is enough that the connection between the client and the ingress is secure. Because the only restriction here is that the cookies sent to the client reach the client over a secure connection. So we can communicate between ingress and pods without SSL. In this way, we reduce the complexity in communication.

Check for more information about this: https://en.wikipedia.org/wiki/TLS_termination_proxy

Change-1

We need to change ${server.ssl.enabled} property from false to true and add some proxying attributes to cas application.

server.ssl.enabled=falsecas.server.tomcat.http.enabled=false
cas.server.tomcat.httpProxy.enabled=true
cas.server.tomcat.httpProxy.secure=true
cas.server.tomcat.httpProxy.scheme=https
cas.server.tomcat.httpProxy.protocol=HTTP/1.1

Change-2

We need to change ingress backend protocol from https to http.

nginx.ingress.kubernetes.io/backend-protocol: "http"

--

--