apache-guacamole

Apache-Guacamole

  1. Apache Guacamole is a bastion host on the web similar to AWS instance connect
  2. We can connect remote machines without agents and it supports RBAC
  3. Please use traefik-forward-auth along with Apache-Guacamole for HTTP header authentication

Installation

Apache Guacamole consists of three services

  1. Guad - Actual service which connects to machines for remote access. It does not handle authentication
  2. Guacamole - Web frontend which handles authentication and others
  3. Database - For storing users and requests data
# Filename: /srv/ras.smounesh.in/docker-compose.yml
# Purpose: to start guacamole docker container along with guad and postgres database
# Authentication: it uses header authentication the username header comes from traefik-forward-auth
#
# Note1: Database will not be initialized with schema during first run.
#       Use this command to generate sql file.
#          "docker run --rm guacamole/guacamole /opt/guacamole/bin/initdb.sh --postgres > initdb.sql"
#
#       Copy the initdb.sql file inside postgres container:
#           "docker-compose cp initdb.sql postgres:/tmp"
#
#       Exec into the postgres container:
#           "docker-compose exec -it postgres bash"
#
#       Use this sql file to initialize the database:
#           "psql -U ${POSTGRES_USER} -d ${POSTGRES_DB} < /tmp/initdb.sql"
#
# Note2: traffic is proxied from traefik in ns1. 

version: "2.4"
name: guacamole

services:
  guacamole:
    image: guacamole/guacamole
    container_name: ras.bitsathy.ac.in
    restart: always

    mem_limit: 4G

    ports:
      - 8080:8080

    environment:
      HEADER_ENABLED: true
      HTTP_AUTH_HEADER: X-Forwarded-User

      REMOTE_IP_VALVE_ENABLED: true

      GUACD_HOSTNAME: guacd
      GUACD_PORT: 4822

      POSTGRES_HOSTNAME: postgres
      POSTGRES_PORT: 5432
      POSTGRES_USER: ${POSTGRES_USER}
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
      POSTGRES_DATABASE: ${POSTGRES_DB}

    depends_on:
      postgres:
        condition: service_healthy

    labels:
      - com.centurylinklabs.watchtower.enable=true
      # - traefik.enable=true

      # see ns1:/opt/traefik.ns1/conf.d/70-ras.bitsathy.ac.in.yaml
      # path /guacamole permanent redirect
      # - traefik.http.routers.guacamole-redirectregex.rule=Host(`ras.bitsathy.ac.in`)
      # - traefik.http.routers.guacamole-redirectregex.tls=true
      # - traefik.http.routers.guacamole-redirectregex.tls.certresolver=lets-encrypt
      # - traefik.http.routers.guacamole-redirectregex.middlewares=guacamole-redirectregex
      # - traefik.http.middlewares.guacamole-redirectregex.redirectregex.regex=^https://ras.bitsathy.ac.in/(.*)
      # - traefik.http.middlewares.guacamole-redirectregex.redirectregex.replacement=https://ras.bitsathy.ac.in/guacamole/${1}
      # - traefik.http.middlewares.guacamole-redirectregex.redirectregex.permanent=true

      # add authResponseHeaders=X-Forwarded-User in traefik-forward-auth config
      # - traefik.http.routers.guacamole.rule=Host(`ras.bitsathy.ac.in`) && PathPrefix(`/guacamole`)
      # - traefik.http.routers.guacamole.tls=true
      # - traefik.http.routers.guacamole.tls.certresolver=lets-encrypt
      # - traefik.http.routers.guacamole.middlewares=google-oauth2@file

  guacd:
    image: guacamole/guacd
    container_name: guacd-guacamole.smounesh.in
    restart: always

    mem_limit: 4G

    labels:
      - com.centurylinklabs.watchtower.enable=true

  postgres:
    image: postgres:15
    container_name: postgres-guacamole.smounesh.in
    restart: always

    mem_limit: 4G

    environment:
      POSTGRES_USER: ${POSTGRES_USER}
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
      POSTGRES_DB: ${POSTGRES_DB}

    volumes:
      - ./pg-data:/var/lib/postgresql/data

    healthcheck:
      test: pg_isready -U ${POSTGRES_USER}
      start_period: 5s
      interval: 5s
      timeout: 5s
      retries: 55

    labels:
      - com.centurylinklabs.watchtower.enable=true

Traefik-forward-auth

  1. By default traefik-forward-auth middleware do not pass http headers after authentication.
  2. These changes will get the job done
# Traefik v2 dynamic config fragment to expose traefik-forward-auth
# Filename: /opt/traefik/conf.d/20-accounts.bitsathy.ac.in.yaml
# URL: https://accounts.bitsathy.ac.in

http:
  services:
    google-oauth2:
      loadBalancer:
        servers:
          - url: http://localhost:4181

  middlewares:
    google-oauth2:
      forwardAuth:
        address: "http://localhost:4181"
        authResponseHeaders:
          - X-Forwarded-User

  routers:
    google-oauth2:
      rule: Host(`accounts.bitsathy.ac.in`)
      entrypoints:
        - https
      middlewares:
        - google-oauth2
      service: google-oauth2
      tls:
        certResolver: lets-encrypt
  1. Proxy pass the traffic to gucamole using yaml config file
# Traefik v2 dynamic config fragment to expose ras.bitsathy.ac.in
# Filename: /opt/traefik/conf.d/70-ras.bitsathy.ac.in
# URL: https://ras.bitsathy.ac.in

http:
  services:
    ras:
      loadBalancer:
        servers:
          - url: http://10.200.2.170:8080

  middlewares:
    ras-redirectregex:
      redirectregex:
        regex: "^https://ras.bitsathy.ac.in/(.*)"
        replacement: "https://ras.bitsathy.ac.in/guacamole"
        permanent: true

  routers:
    ras-redirectregex:
      rule: Host(`ras.bitsathy.ac.in`)
      middlewares: ras-redirectregex
      service: ras
      tls:
        certResolver: lets-encrypt

    ras:
      rule: Host(`ras.bitsathy.ac.in`) && PathPrefix(`/guacamole`)
      middlewares: google-oauth2@file
      service: ras
      tls:
        certResolver: lets-encrypt

Reference

  1. https://guacamole.apache.org/doc/gug/
  2. https://hub.docker.com/r/guacamole/guacamole