Home > Mobile >  nginx application authentication using keycloak - issue with ingress
nginx application authentication using keycloak - issue with ingress

Time:12-29

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" ><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

CodePudding user response:

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"

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
    name: nginx
    namespace: default
    annotations:
      nginx.ingress.kubernetes.io/rewrite-target: /
spec:
  ingressClassName: nginx
  rules:
  - host: demo.localdev.me
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: nginx
            port:
              number: 80

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.

kubectl port-forward --namespace=ingress-nginx service/ingress-nginx-controller 8080:80

Now, i can see the IP address assigned to my ingress resource as shown below.

kubeadmin@kubemaster:~$ kubectl get ingress
NAME    CLASS   HOSTS              ADDRESS           PORTS   AGE
nginx   nginx   demo.localdev.me   192.168.122.198   80      75m

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.

CodePudding user response:

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.

  • Related