Introducing: Guacamole – Remotely accessing your servers (SSH/RDP/VNC) from the web

Sometimes it’s useful to get into your servers when you’re remote. There’s a lot of various ways to accomplish this. You can employ a VPN. You can use a remote access software like LogMeIn (hint: bad idea… don’t do this). You can also create an HTML5 web interface for accessing the machine from any browser. This is where Guacamole comes into the picture. It’s a pretty neat tool that allows you to safely expose SSH/RDP/VNC targets for remote access via SSL. In this post, I will walk through deploying Guacamole using Docker Compose on Ubuntu 22.04.

Prerequisites:

Let’s create the working directory and the base docker-compose file:

mkdir /home/guacamole
cd /home/guacamole
nano docker-compose.yml

The basic docker compose is available on GitHub, but I’ve cleaned it up a bit and included it below:

networks:
  guacnetwork_compose:
    driver: bridge
services:
  guacd:
    container_name: guacd_compose
    image: guacamole/guacd
    networks:
      - guacnetwork_compose
    restart: always
    volumes:
    - ./drive:/drive:rw
    - ./record:/record:rw
  postgres:
    container_name: postgres_guacamole_compose
    environment:
      PGDATA: /var/lib/postgresql/data/guacamole
      POSTGRES_DB: guacamole_db
      POSTGRES_PASSWORD: 'ChangeThisToSomethingSecret'
      POSTGRES_USER: guacamole_user
    image: postgres:15.2-alpine
    networks:
      - guacnetwork_compose
    restart: always
    volumes:
    - ./init:/docker-entrypoint-initdb.d:z
    - ./data:/var/lib/postgresql/data:Z
  guacamole:
    container_name: guacamole_compose
    group_add:
      - "1001"
    depends_on:
    - guacd
    - postgres
    environment:
      GUACD_HOSTNAME: guacd
      POSTGRES_DATABASE: guacamole_db
      POSTGRES_HOSTNAME: postgres
      POSTGRES_PASSWORD: 'ChangeThisToSomethingSecretMatchingAbove'
      POSTGRES_USER: guacamole_user
      RECORDING_SEARCH_PATH: /record
    image: guacamole/guacamole
    networks:
      - guacnetwork_compose
    volumes:
      - ./record:/record:rw
    ports:
    - 8080/tcp
    restart: always
  nginx:
   container_name: nginx_guacamole_compose
   restart: always
   image: nginx:latest
   volumes:
   - ./nginx/templates:/etc/nginx/templates:ro
   - ./nginx/ssl/self.cert:/etc/nginx/ssl/self.cert:ro
   - ./nginx/ssl/self-ssl.key:/etc/nginx/ssl/self-ssl.key:ro
   ports:
   - 8443:443
   networks:
     - guacnetwork_compose

** Note: The file in Github has an error in it referencing the wrong group ID. I corrected it to 1001 in the above.

The docker image is set to include the required extensions based on the environment variables you supply. For example, if you add TOTP_ENABLED: true, the image build will include the TOTP extension.

So, with that being said, I STRONGLY recommend setting up some form of dual factor authentication. Guacamole supports TOTP and DUO. Even if you’re going to use an external authentication provider, such as SSO, LDAP, etc., the local admin account needs to be secured and TOTP or DUO would be the best choices to do that.

Configuring TOTP

In the docker-compose file, you will add the configuration under guacamole>environment. There are 5 configuration options that you should provide:

guacamole:
  environment:
      TOTP_ISSUER: 'Your Service or System'
      TOTP_DIGITS: 8
      TOTP_PERIOD: 30
      TOTP_MODE: sha256
      TOTP_ENABLED: true

Once you add these parameters to the compose, restart the pod with docker compose up -d. Now, when users log in, they will get prompted to setup TOTP.

Configuring LDAP

In the docker-compose file, you will add the configuration under guacamole>environment. There are only 1 parameter that is required to be supplied:

guacamole:
  environment:
      LDAP-USER-BASE-DN: 'OU=folder,DC=my,DC=domain,DC=com'

There are a bunch of other parameters that are quite useful to provide. Here are some of them with comments explaining them:

guacamole:
  environment:
      ## Your LDAP domain
      LDAP_HOSTNAME: my.domain.com
      ## Your LDAP port. Usually 389 or 636
      LDAP_PORT: 389
      ## Encryption Method, Supported values: none, ssl, starttls
      LDAP_ENCRYPTION_METHOD: 'none'
      ## If you want to map LDAP groups to Guacamole groups, point to the parent folder where your groups are stored
      LDAP_GROUP_BASE_DN: 'OU=Groups,DC=my,DC=domain,DC=com'
      ## You probably don't want every single user in LDAP user to have access, so use this to map to a specific group that governs access
      LDAP_USER_SEARCH_FILTER: '(memberOf=CN=GroupName,OU=Groups,DC=my,DC=domain,DC=tech)'
      ## Define the LDAP attribute to map the username to. For Active Directory, this is usually sAMAccountName
      LDAP_USERNAME_ATTRIBUTE: 'sAMAccountName'
      ## Assuming that you don't allow anonymous querying of LDAP, the following two parameters allow Guacamole to log into LDAP to query users
      LDAP_SEARCH_BIND_DN: 'CN=username,OU=Users,DC=my,DC=domain,DC=com'
      LDAP_SEARCH_BIND_PASSWORD: 'password'

Some LDAP tips:

  • You can update your LDAP schema to map users to connections (servers you want to SSH/RDP into), but that’s complex and requires structural changes to your LDAP schema.
  • Alternatively, you can create local users whose username match their LDAP username exactly and configure access on the local user.
    • Security tip: If you check the box “Login disabled” on the local user account, it will force them to use their LDAP credentials to login.

Related Posts

Leave a Reply

Your email address will not be published. Required fields are marked *