I am trying to get an nginx application authenticated using the keycloak identity provider by following this article – https://cloud.redhat.com/blog/adding-authentication-to-your-kubernetes-web-applications-with-keycloak
Here are my setup and config details which i am trying to apply.
ingress controller – https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.1.0/deploy/static/provider/baremetal/deploy.yaml
CNI – https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml
kubeadmin@kubemaster:/etc/kubernetes/manifests$ kubectl get nodes -o wide
NAME STATUS ROLES AGE VERSION INTERNAL-IP EXTERNAL-IP OS-IMAGE KERNEL-VERSION CONTAINER-RUNTIME
kubemaster Ready control-plane,master 4d11h v1.23.1 192.168.122.54 <none> Ubuntu 20.04.3 LTS 5.11.0-43-generic docker://20.10.12
kubenode Ready <none> 4d10h v1.23.1 192.168.122.198 <none> Ubuntu 20.04.3 LTS 5.11.0-43-generic docker://20.10.12
Deployment, service and ingress file trying to apply
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx
namespace: default
spec:
replicas: 1
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx
- name: gatekeeper
image: carlosedp/keycloak-gatekeeper:latest
args:
- --config=/etc/keycloak-gatekeeper.conf
ports:
- containerPort: 3000
name: service
volumeMounts:
- name: gatekeeper-config
mountPath: /etc/keycloak-gatekeeper.conf
subPath: keycloak-gatekeeper.conf
- name: gatekeeper-files
mountPath: /html
volumes:
- name : gatekeeper-config
configMap:
name: gatekeeper-config
- name : gatekeeper-files
configMap:
name: gatekeeper-files
---
apiVersion: v1
kind: ConfigMap
metadata:
name: gatekeeper-config
namespace: default
creationTimestamp: null
data:
keycloak-gatekeeper.conf: |+
# is the url for retrieve the OpenID configuration - normally the <server>/auth/realms/<realm_name>
discovery-url: https://192.168.122.54:8443/auth/realms/local
# skip tls verify
skip-openid-provider-tls-verify: true
# the client id for the 'client' application
client-id: gatekeeper
# the secret associated to the 'client' application
client-secret: 50f6177f-4c66-4def-81f9-a4bd4ec2491b
# the interface definition you wish the proxy to listen, all interfaces is specified as ':<port>', unix sockets as unix://<REL_PATH>|</ABS PATH>
listen: :3000
# whether to enable refresh tokens
enable-refresh-tokens: true
# the location of a certificate you wish the proxy to use for TLS support
#tls-cert:
# the location of a private key for TLS
#tls-private-key:
# the redirection url, essentially the site url, note: /oauth/callback is added at the end
redirection-url: http://kubemaster
secure-cookie: false
# the encryption key used to encode the session state
encryption-key: vGcLt8ZUdPX5fXhtLZaPHZkGWHZrT6aa
# the upstream endpoint which we should proxy request
upstream-url: http://127.0.0.1:80/
forbidden-page: /html/access-forbidden.html
resources:
- uri: /*
groups:
- my-app
---
apiVersion: v1
kind: ConfigMap
metadata:
name: gatekeeper-files
namespace: default
creationTimestamp: null
data:
access-forbidden.html: |+
<html lang="en"><head> <title>Access Forbidden</title><style>*{font-family: "Courier", "Courier New", "sans-serif"; margin:0; padding: 0;}body{background: #233142;}.whistle{width: 20%; fill: #f95959; margin: 100px 40%; text-align: left; transform: translate(-50%, -50%); transform: rotate(0); transform-origin: 80% 30%; animation: wiggle .2s infinite;}@keyframes wiggle{0%{transform: rotate(3deg);}50%{transform: rotate(0deg);}100%{transform: rotate(3deg);}}h1{margin-top: -100px; margin-bottom: 20px; color: #facf5a; text-align: center; font-size: 90px; font-weight: 800;}h2, a{color: #455d7a; text-align: center; font-size: 30px; text-transform: uppercase;}</style> </head><body> <use> <svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 1000 1000" enable-background="new 0 0 1000 1000" xml:space="preserve" class="whistle"><g><g transform="translate(0.000000,511.000000) scale(0.100000,-0.100000)"><path d="M4295.8,3963.2c-113-57.4-122.5-107.2-116.8-622.3l5.7-461.4l63.2-55.5c72.8-65.1,178.1-74.7,250.8-24.9c86.2,61.3,97.6,128.3,97.6,584c0,474.8-11.5,526.5-124.5,580.1C4393.4,4001.5,4372.4,4001.5,4295.8,3963.2z"/><path d="M3053.1,3134.2c-68.9-42.1-111-143.6-93.8-216.4c7.7-26.8,216.4-250.8,476.8-509.3c417.4-417.4,469.1-463.4,526.5-463.4c128.3,0,212.5,88.1,212.5,224c0,67-26.8,97.6-434.6,509.3c-241.2,241.2-459.5,449.9-488.2,465.3C3181.4,3180.1,3124,3178.2,3053.1,3134.2z"/><path d="M2653,1529.7C1644,1445.4,765.1,850,345.8-32.7C62.4-628.2,22.2-1317.4,234.8-1960.8C451.1-2621.3,947-3186.2,1584.6-3500.2c1018.6-501.6,2228.7-296.8,3040.5,515.1c317.8,317.8,561,723.7,670.1,1120.1c101.5,369.5,158.9,455.7,360,553.3c114.9,57.4,170.4,65.1,1487.7,229.8c752.5,93.8,1392,181.9,1420.7,193.4C8628.7-857.9,9900,1250.1,9900,1328.6c0,84.3-67,172.3-147.4,195.3c-51.7,15.3-790.8,19.1-2558,15.3l-2487.2-5.7l-55.5-63.2l-55.5-61.3v-344.6V719.8h-411.7h-411.7v325.5c0,509.3,11.5,499.7-616.5,494C2921,1537.3,2695.1,1533.5,2653,1529.7z"/></g></g></svg></use><h1>403</h1><h2>Not this time, access forbidden!</h2><h2><a href="/oauth/logout?redirect=https://google.com">Logout</h2></body></html>
---
apiVersion: v1
kind: Service
metadata:
labels:
app: nginx
name: nginx
namespace: default
spec:
ports:
- name: http
port: 80
protocol: TCP
targetPort: 3000
selector:
app: nginx
type: ClusterIP
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: nginx
namespace: default
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
spec:
rules:
- host: kubemaster
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: nginx
port:
number: 80
defaultBackend:
service:
name: nginx
port:
number: 80
After applying the yml definition, the pods, services and ingress everything is running
kubeadmin@kubemaster:~/stack/keycloak$ kubectl get pods,svc,rs,ingress -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
pod/nginx-bb47fbd49-sbqhv 2/2 Running 0 34m 192.168.1.30 kubenode <none> <none>
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR
service/kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 4d11h <none>
service/nginx ClusterIP 10.100.36.168 <none> 80/TCP 34m app=nginx
NAME DESIRED CURRENT READY AGE CONTAINERS IMAGES SELECTOR
replicaset.apps/nginx-bb47fbd49 1 1 1 34m nginx,gatekeeper nginx,carlosedp/keycloak-gatekeeper:latest app=nginx,pod-template-hash=bb47fbd49
NAME CLASS HOSTS ADDRESS PORTS AGE
ingress.networking.k8s.io/nginx <none> kubemaster 80 34m
But i am unable to launch the application on http://kubemaster:80/ which as per the flow should route to the nginx service backend hosting the gatekeeper proxy.
The service description shows the endpoints of the pod and also i am able to directly hit the service backend which is http://192.168.1.30:3000/ which takes me to keycloak authentication page.
kubeadmin@kubemaster:~/stack/keycloak$ kubectl describe service nginx
Name: nginx
Namespace: default
Labels: app=nginx
Annotations: <none>
Selector: app=nginx
Type: ClusterIP
IP Family Policy: SingleStack
IP Families: IPv4
IP: 10.100.36.168
IPs: 10.100.36.168
Port: http 80/TCP
TargetPort: 3000/TCP
Endpoints: 192.168.1.30:3000
Session Affinity: None
Events: <none>
Error –
kubeadmin@kubemaster:~/stack/keycloak$ curl http://kubemaster/
curl: (7) Failed to connect to kubemaster port 80: Connection refused
Any suggestions or direction would greatly help..
Thanks
Sudhir
2
Answers
After reading through the nginx ingress controller documentation, i was able to resolve my ingress issue. Here are the steps taken to resolve the issue.
"nginx" deployment yml definition remains the same.
"gatekeeper-config" updated with "redirection-url: http://demo.localdev.me:8080/".
"gatekeeper-files" yml definition remains the same.
"nginx" service yml definition remains the same.
"nginx" ingress yml definition updated as shown below. It basically maps "demo.localdev.me" to "localhost"
After the required changes, applied the yml definitions to create the deployment, configmaps, service and ingress resources. Once the components are running, need to forward a local port to the ingress controller as shown below.
Now, i can see the IP address assigned to my ingress resource as shown below.
Launch the application using "http://demo.localdev.me:8080" which internally route to the ingress controller on port 80. Then the request routes to service backend which is keycloak which authenticates the request and routes back to the application.
if client side URL (domain+port) is different and server is not aware of this URL, then set environment variable "KEYCLOAK_FRONTEND_URL" in the keycloak container.
jfi, this attribute is present in standalone.xml (/standalone-ha.xml) in keycloak config folder and you can hard-code the value here as well.