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.comserver.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;)
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).
127.0.0.1 test-login.example.com
Now, ready to be tested at https://test-login.example.com/cas/login
SSL Offloading
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
- https://apereo.github.io/cas/6.2.x/installation/Troubleshooting-Guide.html#configuring-ssl-behind-load-balancerproxy
- https://apereo.github.io/cas/6.2.x/configuration/Configuration-Properties.html#http-proxying
Change-2
We need to change ingress backend protocol from https to http.
nginx.ingress.kubernetes.io/backend-protocol: "http"