Aller au contenu

Guacamole

logo

👋 Présentation

Apache Guacamole est une passerelle de bureau à distance qui ne nécessite pas l'installation d'un client lourd. Elle prend en charge les protocoles standard tels que VNC, RDP et SSH.

Grâce à HTML5, une fois Guacamole installé sur un serveur, tout ce dont vous avez besoin pour accéder à vos postes de travail ou serveurs est un navigateur Web pour vous connecter à l'interface web de l'application.

Dans mon cas, j'ai souhaité l'installer et l'utiliser en tant que bastion.

Technos utilisées dans le cadre de cette expérimentation : - Docker - Traefik + Let's Encrypt - Crowdsec - Authelia

Installation de Guacamole avec Docker

Arborescence

Arborescence de /srv/guacamole
.
├── compose.yml
├── db-data
├── drive
├── .env
└── record

Renseignement de compose.yml et .env

Les URL sont à adapter à votre environnement. Il faudra également renseigner une valeur arbitraire concernant la variable OPENID_AUTHORIZATION_ENDPOINT à la place de . Personnellement j'ai utilisé Authelia avec la commande suivante : docker run authelia/authelia:latest authelia crypto rand --length 64 --charset alphanumeric mais vous pouvez également utiliser tr -cd '[:alnum:]' < /dev/urandom | fold -w "64" | head -n 1

compose.yml
---
services:

# daemon
  bastion-dmn:
    image: guacamole/guacd
    container_name: bastion-dmn
    restart: unless-stopped
    volumes:
      - "/etc/localtime:/etc/localtime:ro"
      - ./drive:/drive:rw
      - ./record:/var/lib/guacamole/recordings:rw
    networks:
      bastion_bckd:

# mysql
  bastion-db:
    image: mariadb:10.9.5
    container_name: bastion-db
    restart: always
    environment:
      MYSQL_ROOT_PASSWORD: ${MYSQL_PASSWORD}
      MYSQL_DATABASE: 'guacamole_db'
      MYSQL_USER: ${MYSQL_USER}
      MYSQL_PASSWORD: ${MYSQL_PASSWORD}
    volumes:
      - './db-data:/var/lib/mysql'
    networks:
      bastion_bckd:

# guacamole
  bastion:
    image: guacamole/guacamole:latest
    container_name: bastion
    restart: always
    depends_on:
    - bastion-dmn
    - bastion-db
    environment:
      GUACD_HOSTNAME: bastion-dmn
      # Très important pour récupérer l'IP réelle du client
      REMOTE_IP_VALVE_ENABLED: true
      MYSQL_DATABASE: guacamole_db
      MYSQL_HOSTNAME: bastion-db
      MYSQL_PASSWORD: '${MYSQL_PASSWORD}'
      MYSQL_USER: '${MYSQL_USER}'
      # Afin que de pouvoir régler les permissions des users s'auth. avec Authelia
      MYSQL_AUTO_CREATE_ACCOUNTS: true
      # TOTP à activer si SSO non utilisé
      #TOTP_ENABLED: 'true'
      # SSO avec OIDC
      OPENID_SCOPE: openid profile groups email
      OPENID_AUTHORIZATION_ENDPOINT: https://<URL_AUTHELIA>/api/oidc/authorization?state=<RAMDOM_VALUE>
      OPENID_JWKS_ENDPOINT: https://<URL_AUTHELIA>/jwks.json
      OPENID_ISSUER: https://<URL_AUTHELIA>
      OPENID_CLIENT_ID: <CLIENT_ID_AUTHELIA>
      OPENID_REDIRECT_URI: https://<URL_GUACAMOLE>
      OPENID_USERNAME_CLAIM_TYPE: preferred_username
      OPENID_GROUPS_CLAIM_TYPE: groups
      EXTENSION_PRIORITY: '*,openid'
    volumes:
    - ./drive:/drive:rw
    - ./record:/var/lib/guacamole/recordings
    labels:
      - "traefik.enable=true"
      - "traefik.docker.network=web"
      - "traefik.http.routers.bastion.rule=Host(`bastion.raspot.in`)"
      - "traefik.http.routers.bastion.tls=true"
      - "traefik.http.routers.bastion.tls.certresolver=letsencrypt"
      - "traefik.http.routers.bastion.entrypoints=websecure"
      - "traefik.http.routers.bastion.middlewares=guac-addprefix,crowdsec@file,guac-ipok"
      # Pour remove /guacamole de l'URL
      - "traefik.http.middlewares.guac-addprefix.addprefix.prefix=/guacamole"
      # Je préfère ne pas exposer sur le net et y accéder par le VPN
      - "traefik.http.middlewares.guac-ipok.ipallowlist.sourcerange=10.1.1.0/24, 127.0.0.1/32, 192.168.1.0/24"
      - "traefik.http.services.bastion.loadbalancer.server.port=8080"
      - "traefik.http.routers.bastion.service=bastion"
    networks:
      web:
      bastion_bckd:

networks:
  web:
    external: true
  bastion_bckd:
.env
MYSQL_USER=un_user
MYSQL_PASSWORD=un_mot_de_passe

Lancement de la stack et initialisation de la BDD

Initialisation
docker compose up -d

docker run --rm guacamole/guacamole /opt/guacamole/bin/initdb.sh --mysql > initdb.sql

docker exec -i bastion-db mysql --user un_user --password=un_mot_de_passe guacamole_db < initdb.sql

Configuration d'Authelia

C'est la partie SSO. Ce n'est pas indispensable mais tellement pratique. J'ai rédigé une doc ici 🔒Authentification centralisée avec Authelia et LLDAP

L'objectif est double :

  1. Permettre l'authentification avec Authelia
  2. Limiter l'accès à un groupe d'utilisateurs
Génération du CLIENT ID et SECRET
# Authelia CLIENT_ID:
docker run authelia/authelia:latest authelia crypto rand --length 64 --charset alphanumeric

# Authelia CLIENT_SECRET:
docker run authelia/authelia:latest authelia crypto hash generate pbkdf2 --variant sha512 --random --random.length 72 --random.charset rfc3986
Configuration d'Authelia
(...)
identity_providers:
  oidc:
    jwks:
      - key: {{ secret "/secrets/private.pem" | mindent 10 "|" | msquote }}
        certificate_chain: |
          -----BEGIN CERTIFICATE-----
          ...
          -----END CERTIFICATE-----
    lifespans.access_token: 1h
    lifespans.authorize_code: 1m
    lifespans.id_token: 1h
    lifespans.refresh_token: 90m
    enable_client_debug_messages: false
    # RBAC : on limite l'accès aux users faisant partis du groupe guacamole
    authorization_policies: 
      policy_guac:
        default_policy: 'deny'
        rules:
          - policy: 'two_factor'
            subject: 'group:guacamole'
    clients:
      - client_id: '<RAMDOM_VALUE>'
        # RBAC
        authorization_policy: 'policy_guac'
        client_name: 'Apache Guacamole'
        client_secret: '<RAMDOM_VALUE>'
        public: false
        redirect_uris:
          - 'https://bastion.raspot.in'
        scopes:
          - 'openid'
          - 'profile'
          - 'groups'
          - 'email'
        response_types:
          - 'id_token'
        grant_types:
          - 'implicit'
        consent_mode: 'auto'
        pre_configured_consent_duration: '1y'

Relancez Authelia puis rendez-vous sur l'URL de Guacamole.

Gestion des utilisateurs

J'ai créé 3 utilisateurs dans le groupe guacamole qui utiliseront Authelia pour s'authentifier (en 2FA évidemment).

Je me suis rendu sur l'URL de Guacamole puis je me suis identifié avec le user "gadmin" qui servira d'admin en cliquant sur "OpenID" en bas à gauche de la page web :

Page d'authentification de Guacamole

Je me suis déconnecté puis je me suis connecté cette fois-ci directement dans l'application avec le compte créé par défaut (user:guacadmin, password:guacadmin) pour configurer le compte "gadmin" en tant qu'administrateur. De nouveau je me suis reconnecté avec "gadmin" pour supprimer le compte "guacadmin".

2FA TOTP

Au départ je voulais conserver un compte admin local mais en activant le 2FA sur Guacamole, cela avait pour conséquence de s'appliquer à tous et donc aux users en provenance d'Authelia. Par conséquentj'ai fait le choix de virer le compte admin local et d'utiliser un compte admin passant par Authelia. Après réflexion, j'aurai pu également simplement supprimer le 2FA avec Authelia.

J'ai ensuite créé un groupe guacamole afin d'y régler les permissions au niveau du groupe. J'ai intégré deux utilisateurs (avec lesquels je m'étais connecté auparavant).

alt text

Création d'une session

Connectez-vous avec un user puis cliquez en haut à droite sur le nom du user puis Paramètres.

Cliquez sur l'onglet Connexions, puis sur Nouveau Groupe. Renseignez le nom (Maison dans mon cas) et cliquez sur Enregistrer.

Dépliez le groupe créé et cliquez ensuite sur Nouvelle Connexion se situant juste dessous et légèrement grisé. Il ne vous reste plus qu'à renseigner le type de connexion, les creds...

🏁 Conclusion

Je ne vais pas m'étendre sur l'utilisation de Guacamole dans cette documentation. Je n'ai pas pu encore tester le RDP mais je vois déjà un problème se profiler au loin concernant les utilisateurs Windows faisant partis du groupe "Protected users". À voir.

Enfin un dernier élément que je trouve très gênant est le fait que l'admin de Guacamole a accès aux sessions des users. Il peut très bien utiliser celles-ci pour se connecter en SSH ou en RDP. Cela sera tracé mais je trouve ça très limite. Autre point gênant qui provient peut-être de mon manque d'expérience sur l'application sont les groupes. Si mon user n°1 crée un groupe, l'autre user peut le voir ou pas mais ne peut pas y créer une connexion lui étant propre. Je trouve la gestion des permissions très légères pour en faire un outil multi-utilisateurs. À creuser.

Sachez qu'un petit nouveau est arrivé depuis : Nexterm et qui intègre Guacamole. C'est un projet encore jeune, pas multi-user et il y a encore des fonctionnalités en cours d'implémentation. Ce qui m'a plu est la possibilité de se connecter à un noeud Proxmox VE et de récupérer l'ensemble des VM/CT.

Enfin je lorgne depuis peu sur Teleport (Teleport) qui semble plus poussé mais plus complexe à déployer avec des serveurs Windows.