abcdesktopio / pyos

abcdesktop API services
https://abcdesktopio.github.io/
GNU General Public License v2.0
2 stars 2 forks source link

ACL restriction on application when using external Auth #9

Closed chintus777 closed 8 months ago

chintus777 commented 9 months ago

Hello Alexandre,

I was trying to implement external Auth method using keycloak . Keycloak has various benefits over Openldap as it provides support for MFA and has an interactive UI . I have deployed keycloak and integrated it with abcdesktop. Following are the keycloak yamls -

apiVersion: apps/v1 kind: Deployment metadata: name: keycloak namespace: vdi spec: replicas: 1 selector: matchLabels: app: keycloak template: metadata: labels: app: keycloak spec: containers:


apiVersion: v1 kind: Service metadata: name: keycloak namespace: vdi spec: selector: app: keycloak ports:

Following is the addition in od.config-

authmanagers: { 'external': { 'providers': { 'keycloak': { 'displayname': 'Keycloak', 'enabled': True, 'basic_auth': True, 'userinfo_auth': True, 'scope' : [ 'openid','email','profile' ], 'client_id': 'demo', 'client_secret': 'W', 'redirect_uri_prefix' : 'https://loginvdi.co/API/auth/oauth', 'redirect_uri_querystring': 'manager=external&provider=keycloak', 'authorization_base_url': 'https://keycloak2.co/auth/realms/test/protocol/openid-connect/auth', 'token_url': 'https://keycloak2.co/auth/realms/test/protocol/openid-connect/token', 'userinfo_url': 'https://keycloak2.co/auth/realms/test/protocol/openid-connect/userinfo', 'policies': { 'acl' : { 'permit': [ 'all' ] }, }

This is the page which appears after clicking "connect with keycloak" on login page -

image

After this it asks for OTP-

image

The login is successful and apps are visible as well

Authentication is working fine with 2FA as well , Apps are also visible inside abcdesktop. The main issue which I am facing is on defining rules for getting labels which I can apply for app restrcition to users . Like in openldap- we define rule such as -

'rule-sample': { 'conditions': [ { 'memberOf': 'cn=ship_crew,ou=people,dc=planetexpress,dc=com', 'expected' : True } ], 'expected' : True, 'label': 'shipcrewgrp' }

How should I define rule for keycloak , Like LDAP we can create groups in keyclaok as well. I think implementation for 2FA is really crucial for this project as it enhances the security. Please tell me how should I proceed . Waiting eagerly for your reply.

Thanks and Regards Chintu

alexandredevely commented 9 months ago

Hello Chintu,

abcdesktop auth works fine with the famous keycloak service. Rules are only supported by Ldap service, in this current release. You ask for a new feature, Add keycloak tag, and match the auth tag to rule. This is a relay good feature, and I like your great idea. I have to check how keycloack tag can be read from the auth provider classes.

Good point, thank you for your comment I add your request as 'New feature or Request'

Alexandre

chintus777 commented 9 months ago

Hello Alexandre,

Thanks for your reply.

After researching a bit, I was wondering that to apply 2FA through RSA, can something be implemented on the submit button that redirects to an external page where an OTP can be authenticated. After authentication if the login is true, user can access their desktop, otherwise redirected to their login page with an error message. Some options I researched upon were: Duo by Cisco JumpCloud

Also do let me know your view, whether implementing this will be better or making ACL Rules over Keycloak is better.

Thank You Chintu

alexandredevely commented 9 months ago

Hello Chintu,

The abcdesktopio/oc.pyos:3.1 has now a support for acl rule over Keycloak. The default memberof attribut name is groups The keycloak IP Addr is a.b.c.d

The keycload external provider is defined in od.config

      'keycloak': {
         'displayname': 'keycloak', 
         'enabled': True,
         'client_id': 'cc18d356-19b0-11ee-ada5-zzzzzzzzzzz', 
         'client_secret': 'xxxxxxxxxxxxxxxx',
         'userinfo_auth': True,
         'scope': [ 'openid', 'email',  'profile', 'roles' ],
         'userinfo_url': 'https://a.b.c.d:8080/realms/master/protocol/openid-connect/userinfo',
         'redirect_uri_prefix' : 'https://www.abcdesktop.local/API/auth/oauth',
         'redirect_uri_querystring': 'manager=external&provider=keycloak',
         'authorization_base_url': 'http://a.b.c.d:8080/realms/master/protocol/openid-connect/auth',
         'token_url': 'http://a.b.c.d:8080/realms/master/protocol/openid-connect/token',
         'revoke_url' : 'https://a.b.c.d:8080/realms/master/protocol/openid-connect/revoke',
         'policies': { 
           'acl' : { 'permit': [ 'all' ] },
           'rules'  : { 
             'rule-ship2': { 
               'conditions' : [ { 'memberOf': 'abcdesktop-role', 'expected' : True  } ], 
               'expected' : True, 
               'label': 'ship' 
             } 
            }
          }
       },

Then I use the keycloack auth, the user is member of ship group.

Screenshot 2023-10-05 at 18 02 59

On the keycloack, you have to define groups, in my case the user is member of 'abcdesktop-role' group.

Screenshot 2023-10-05 at 18 11 43

Add rules, and check the Include in token scope box in the client scope

Screenshot 2023-10-05 at 18 12 48

Look at the log of pyos pod to get the keycloack auth response attribut, if need. I have also defined a role mapping for this user.

See you Alexandre

chintus777 commented 8 months ago

Hello Alexandre,

Thanks a lot for your reply . I am still facing issues in applying ACL rule and still the label is not available inside abcdesktop.

Following is the od.config snippet-

'external': { 'providers': { 'keycloak': { 'displayname': 'Keycloak', 'enabled': True, 'userinfo_auth': True, 'scope' : [ 'openid','email','profile','roles','abcdesktop-role'], 'client_id': 'demo', 'client_secret': 'd3FyRmJWmjYgta1LlhmAQYmtkgHhzjG5', 'redirect_uri_prefix' : 'https://login.co/API/auth/oauth', 'redirect_uri_querystring': 'manager=external&provider=keycloak', 'authorization_base_url': 'https://keycloak1.co/realms/master/protocol/openid-connect/auth', 'token_url': 'https://keycloak1.co/realms/master/protocol/openid-connect/token', 'userinfo_url': 'https://keycloak1.co/realms/master/protocol/openid-connect/userinfo', 'revoke_url': 'https://keycloak1.co/realms/master/protocol/openid-connect/revoke', 'policies': { 'acl' : { 'permit': [ 'all' ] }, 'rules' : { 'rule-net-home': { 'conditions' : [ { 'memberOf': 'firefox', 'expected' : True } ], 'expected' : True, 'label' : 'firefox' } } } }

The deployment , ingress and service yaml is same as provided in previous comment. I am using quay.io/keycloak/keycloak:latest image for keycloak and abcdesktopio/oc.pyos:3.1 image for pyos. On keycloak Portal I did following steps-

  1. Created a client named 'demo' image

2 Created a group named 'firefox'

image

3Created a user john and added to group firefox

image

4 Created a client scope -abcdesktop-role

image

5 Then added the 'abcdesktop-role' scope in demo client.

image

6 Then I added a role named 'test-2' for my client demo

image

7 Then I added role-mapping 'test-2' to the user and group image

image

Still after sign in I cant see label "firefox" inside settings. image

Following are the logs of pyos- 2023-10-13 11:37:18 od [INFO ] main.trace_request:anonymous /core/getmessageinfo {} 2023-10-13 11:37:18 od [INFO ] main.trace_response:3476a799-43d7-4bf2-bd3d-eb4ff4369eef /core/getmessageinfo b'{"status": 200, "result": null, "message": "b.Successfully assigned vdi/3476a799-43d7-4bf2-bd3d-eb4ff4369eef-79f97 to prodclient-w3"}' 2023-10-13 11:37:21 od [INFO ] main.trace_request:anonymous /core/getmessageinfo {} 2023-10-13 11:37:21 od [INFO ] main.trace_response:3476a799-43d7-4bf2-bd3d-eb4ff4369eef /core/getmessageinfo b'{"status": 200, "result": null, "message": null}' 2023-10-13 11:37:22 od [INFO ] main.trace_request:anonymous /core/getmessageinfo {} 2023-10-13 11:37:22 od [INFO ] main.trace_response:3476a799-43d7-4bf2-bd3d-eb4ff4369eef /core/getmessageinfo b'{"status": 200, "result": null, "message": "c.Waiting for desktop graphical service 1/42"}' 2023-10-13 11:37:23 od [INFO ] main.trace_request:anonymous /core/getmessageinfo {} 2023-10-13 11:37:23 od [INFO ] main.trace_response:3476a799-43d7-4bf2-bd3d-eb4ff4369eef /core/getmessageinfo b'{"status": 200, "result": null, "message": "stopinfo"}' 2023-10-13 11:37:23 od [INFO ] main.trace_request:anonymous /core/getkeyinfo {'provider': 'zimbra'} 2023-10-13 11:37:23 od [INFO ] main.trace_response:anonymous /core/getkeyinfo b'{"id": null, "callbackurl": null}' 2023-10-13 11:37:24 od [INFO ] main.trace_request:anonymous /healthz 2023-10-13 11:37:25 od [INFO ] main.trace_request:anonymous /store/get {'key': 'backgroundType'} 2023-10-13 11:37:25 store_controller [DEBUG ] controllers.store_controller.StoreController.wrapped_get:3476a799-43d7-4bf2-bd3d-eb4ff4369eef 2023-10-13 11:37:25 datastore [DEBUG ] oc.datastore.ODMongoDatastoreClient.get_document_value_in_collection:3476a799-43d7-4bf2-bd3d-eb4ff4369eef database=profiles collectionname=3476a799-43d7-4bf2-bd3d-eb4ff4369eef key=backgroundType 2023-10-13 11:37:25 datastore [DEBUG ] oc.datastore.ODMongoDatastoreClient.createclient:3476a799-43d7-4bf2-bd3d-eb4ff4369eef databasename=profiles 2023-10-13 11:37:25 datastore [DEBUG ] oc.datastore.ODMongoDatastoreClient.createclient:3476a799-43d7-4bf2-bd3d-eb4ff4369eef createclient MongoClient mongodb://pyos:Az4MeYWUjZDg4Zjhk@mongodb.vdi.svc.cluster.local/profiles?authSource=profiles 2023-10-13 11:37:25 od [INFO ] main.trace_request:anonymous /core/getkeyinfo {'provider': 'webrtc'} 2023-10-13 11:37:25 od [INFO ] main.trace_response:anonymous /core/getkeyinfo b'{"id": false, "callbackurl": null}' 2023-10-13 11:37:25 store_controller [DEBUG ] controllers.store_controller.StoreController.wrapped_get:3476a799-43d7-4bf2-bd3d-eb4ff4369eef wrapped_get result 3476a799-43d7-4bf2-bd3d-eb4ff4369eef:backgroundType->img 2023-10-13 11:37:25 od [INFO ] main.trace_response:3476a799-43d7-4bf2-bd3d-eb4ff4369eef /store/get b'{"status": 200, "result": "img", "message": "ok"}' 2023-10-13 11:37:32 od [INFO ] main.trace_request:anonymous /auth/labels {} 2023-10-13 11:37:32 od [INFO ] main.trace_request:anonymous /store/getcollection {'key': 'loginHistory'} 2023-10-13 11:37:32 auth_controller [DEBUG ] controllers.auth_controller.AuthController.labels:anonymous 2023-10-13 11:37:32 datastore [DEBUG ] oc.datastore.ODMongoDatastoreClient.getcollection:3476a799-43d7-4bf2-bd3d-eb4ff4369eef database=loginHistory collectionname=3476a799-43d7-4bf2-bd3d-eb4ff4369eef 2023-10-13 11:37:32 od [INFO ] main.trace_response:3476a799-43d7-4bf2-bd3d-eb4ff4369eef /auth/labels b'{"status": 200, "result": {}, "message": "ok"}' 2023-10-13 11:37:32 datastore [DEBUG ] oc.datastore.ODMongoDatastoreClient.createclient:3476a799-43d7-4bf2-bd3d-eb4ff4369eef databasename=loginHistory 2023-10-13 11:37:32 datastore [DEBUG ] oc.datastore.ODMongoDatastoreClient.createclient:3476a799-43d7-4bf2-bd3d-eb4ff4369eef createclient MongoClient mongodb://pyos:Az4MeYWUjZDg4Zjhk@mongodb.vdi.svc.cluster.local/loginHistory?authSource=loginHistory 2023-10-13 11:37:32 od [INFO ] main.trace_response:3476a799-43d7-4bf2-bd3d-eb4ff4369eef /store/getcollection b'{"status": 200, "result": [{"_id": "65292bf25627e22f125143d8", "name": "John Doe", "userid": "3476a799-43d7-4bf2-bd3d-eb4ff4369eef", "locale": "en_US", "posix": {"cn": "3476a799-43d7-4bf2-bd3d-eb4ff43", "uid": "3476a799-43d7-4bf2-bd3d-eb4ff43", "gid": "3476a799-43d7-4bf2-bd3d-eb4ff4369eef", "uidNumber": "4096", "gidNumber": "4096", "homeDirectory": "/home/balloon", "loginShell": "/bin/bash", "description": "abcdesktop default account", "groups": null, "gecos": null}, "provider": "oauth", "date": "2023-10-13T11:37:22.786529", "useragent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/117.0.0.0 Safari/537.36 Edg/117.0.2045.60", "ipaddr": "192.168.178.202", "node": "prodclient-w3", "type": "desktop"}], "message": "ok"}' 2023-10-13 11:37:34 od [INFO ] main.trace_request:anonymous /healthz

Please let me know what can I do to resolve this issue , Also I am still confused about client-scope , roles, role-mapping as u mentioned in previous comment .

Thanks and Regards Chintu

chintus777 commented 8 months ago

Hello Alexandre,

These are the logs of keycloak pod -

Updating the configuration and installing your custom providers, if any. Please wait.
2023-10-16 13:11:41,114 INFO  [io.quarkus.deployment.QuarkusAugmentor] (main) Quarkus augmentation completed in 9478ms
2023-10-16 13:11:42,412 INFO  [org.keycloak.quarkus.runtime.hostname.DefaultHostnameProvider] (main) Hostname settings: Base URL: <unset>, Hostname: <request>, Strict HTTPS: false, Path: <request>, Strict BackChannel: false, Admin URL: <unset>, Admin: <request>, Port: -1, Proxied: true
2023-10-16 13:11:44,038 WARN  [io.quarkus.agroal.runtime.DataSources] (main) Datasource <default> enables XA but transaction recovery is not enabled. Please enable transaction recovery by setting quarkus.transaction-manager.enable-recovery=true, otherwise data may be lost if the application is terminated abruptly
2023-10-16 13:11:44,634 WARN  [org.infinispan.PERSISTENCE] (keycloak-cache-init) ISPN000554: jboss-marshalling is deprecated and planned for removal
2023-10-16 13:11:44,757 WARN  [org.infinispan.CONFIG] (keycloak-cache-init) ISPN000569: Unable to persist Infinispan internal caches as no global state enabled
2023-10-16 13:11:44,857 INFO  [org.infinispan.CONTAINER] (keycloak-cache-init) ISPN000556: Starting user marshaller 'org.infinispan.jboss.marshalling.core.JBossUserMarshaller'
2023-10-16 13:11:45,344 WARN  [io.quarkus.vertx.http.runtime.VertxHttpRecorder] (main) The X-Forwarded-* and Forwarded headers will be considered when determining the proxy address. This configuration can cause a security issue as clients can forge requests and send a forwarded header that is not overwritten by the proxy. Please consider use one of these headers just to forward the proxy address in requests.
2023-10-16 13:11:45,375 INFO  [org.keycloak.connections.infinispan.DefaultInfinispanConnectionProviderFactory] (main) Node name: node_977034, Site name: null
2023-10-16 13:11:45,385 INFO  [org.keycloak.broker.provider.AbstractIdentityProviderMapper] (main) Registering class org.keycloak.broker.provider.mappersync.ConfigSyncEventListener
2023-10-16 13:11:46,538 INFO  [org.keycloak.quarkus.runtime.storage.legacy.liquibase.QuarkusJpaUpdaterProvider] (main) Initializing database schema. Using changelog META-INF/jpa-changelog-master.xml
2023-10-16 13:11:48,456 INFO  [org.keycloak.services] (main) KC-SERVICES0050: Initializing master realm
2023-10-16 13:11:50,263 INFO  [io.quarkus] (main) Keycloak 22.0.4 on JVM (powered by Quarkus 3.2.6.Final) started in 9.031s. Listening on: http://0.0.0.0:8080
2023-10-16 13:11:50,264 INFO  [io.quarkus] (main) Profile dev activated.
2023-10-16 13:11:50,264 INFO  [io.quarkus] (main) Installed features: [agroal, cdi, hibernate-orm, jdbc-h2, jdbc-mariadb, jdbc-mssql, jdbc-mysql, jdbc-oracle, jdbc-postgresql, keycloak, logging-gelf, micrometer, narayana-jta, reactive-routes, resteasy, resteasy-jackson, smallrye-context-propagation, smallrye-health, vertx]
2023-10-16 13:11:50,423 INFO  [org.keycloak.services] (main) KC-SERVICES0009: Added user 'admin' to realm 'master'
2023-10-16 13:11:50,428 WARN  [org.keycloak.quarkus.runtime.KeycloakMain] (main) Running the server in development mode. DO NOT use this configuration in production.
2023-10-16 13:29:06,153 WARN  [org.keycloak.protocol.oidc.utils.OAuth2CodeParser] (executor-thread-48) Code '9504d150-aacd-489b-b913-29966498bc2c' already used for userSession '1f435b0b-7bed-4757-a6c9-7f630c97e985' and client '2925dadc-17a1-4340-958f-0bd7edf4b4a0'.
2023-10-16 13:29:06,155 WARN  [org.keycloak.events] (executor-thread-48) type=CODE_TO_TOKEN_ERROR, realmId=6e1f0fe4-639d-4cab-9f67-278c564d57ff, clientId=demo, userId=null, ipAddress=192.168.178.202, error=invalid_code, grant_type=authorization_code, code_id=1f435b0b-7bed-4757-a6c9-7f630c97e985, client_auth_method=client-secret

Below is the keycloak ingress , deployment and service yaml -

apiVersion: v1
kind: Service
metadata:
  name: keycloak
  namespace: vdi
  labels:
    app: keycloak
spec:
  ports:
    - name: http
      port: 80
      targetPort: 8080
  selector:
    app: keycloak
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: keycloak
  namespace: vdi
  labels:
    app: keycloak
spec:
  replicas: 1
  selector:
    matchLabels:
      app: keycloak
  template:
    metadata:
      labels:
        app: keycloak
    spec:
      containers:
        - name: keycloak
          image: quay.io/keycloak/keycloak:latest
          args: ["start-dev"]
          env:
            - name: KEYCLOAK_ADMIN
              value: "admin"
            - name: KEYCLOAK_ADMIN_PASSWORD
              value: "admin"
            - name: KC_PROXY
              value: "edge"
            - name: PROXY_ADDRESS_FORWARDING
              value: "true"
            - name: KEYCLOAK_HOSTNAME
              value: "keycloak1.co"
          ports:
            - name: http
              containerPort: 8080
          readinessProbe:
            httpGet:
              path: /realms/master
              port: 8080
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: keycloak-ingress
  namespace: vdi
  annotations:
    cert-manager.io/cluster-issuer: letsencrypt-prod
    kubernetes.io/ingress.class: "nginx"
spec:
  tls:
  - hosts:
    - keycloak1.co
    secretName: keycloak1-tls-secret
  rules:
  - host: keycloak1.co
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: keycloak
            port:
              number: 80

I am using keycloak with start-dev cmd and also the hostname is not getting set on deployment but I can access the URL , is that causing the issue ?

Thanks and Regards Chintu

alexandredevely commented 8 months ago

Hello Chintu,

Please read the keycloak jwt response, and check that the groups is included in token scope. It seems that in your case groups is NOT included in token scope. Without groups in token scope, the memberOf rule never runs, as you can read in your user settings session screenshot.

Alexandre

chintus777 commented 8 months ago

Hello Alexandre ,

From where can I get the keycloak-jwt-token ?

alexandredevely commented 8 months ago

Hello Chintus,

I've added this line in authservice.py in abcdesktopio/oc.pyos:3.1 image.

self.logger.debug( f"dump userinfo data={data}" )

This line dumps the json response from keycloak, so you can get read the userinfo values.

For example you can read on pyos stdout or in pyos container file /var/pyos/logs/trace.log

2023-10-17 11:52:38 authservice [DEBUG  ] oc.auth.authservice.ODExternalAuthProvider.getuserinfo:anonymous dump userinfo data=

With the dict values

{
"sub": "f6622d20-f91c-40c9-923d-315b7484e89b", 
"resource_access": {"account": {"roles": ["manage-account", "manage-account-links", "view-profile"]}}, 
"email_verified": false, 
"realm_access": {"roles": ["default-roles-master", "monrole", "offline_access", "uma_authorization", "abcdesktop-role"]}, 
"name": "alexandre devely", 
"groups": ["default-roles-master", "monrole", "offline_access", "uma_authorization", "abcdesktop-role"], 
"preferred_username": "alex", 
"given_name": "alexandre", 
"family_name": "devely", 
"email": "nobody@nowhere.com"
}

You need to pull the image.

I hope this will help you to read the keycloack groups

Alexandre

chintus777 commented 8 months ago

Hello Alexandre , Thanks a lot for your help . I have successfully applied ACL with keycloak Earlier I didn't include "group" token in client scope.

image