Add a PSM Proxy with Apache Guacamole

Deploy Apache Guacamole as a session-recording proxy for your home PAM lab.

Introduction

30 Second Summary

When someone with admin access does something harmful on a server, the security team needs proof of exactly what happened. Session recording is how organizations capture every keystroke and screen action during privileged access.

In this project, you will deploy Apache Guacamole as a Privileged Session Manager (PSM) proxy in your home PAM lab. You'll build two target VMs, connect them through Guacamole, and verify that every session is recorded and playable from the browser.

What You'll Build

A session-recording proxy layer that captures graphical recordings of every SSH and RDP session passing through it.

By the end of this project, you'll have:

  • Two target VMs (Ubuntu Server and Windows 11) on an isolated host-only network with verified SSH and RDP access.
  • A Docker Compose stack running Apache Guacamole 1.6.0 alongside your Part 1 Conjur deployment on port 8081.
  • Proof of session recording by playing back both SSH and RDP sessions directly from the Guacamole history screen.
  • Secret Mission: export a recorded session to MP4 video for offline compliance archiving.

Are there any prerequisites?

This project is Part 2 of the Home PAM Lab series. You need to have completed Part 1, which leaves you with a Conjur stack running on port 8080.

You should also be comfortable with basic terminal commands and have a Windows host machine with enough resources to run two VMs simultaneously.

Create Target VMs and Host-Only Network

Your tools are installed. Now it's time to grab the OS images you'll need and build the target machines that your PSM proxy will record sessions on.

In a production PAM environment, privileged sessions flow through a proxy into isolated target systems. You'll replicate that architecture locally by creating two VMs on a private network that only your Windows host can reach.

In this step, get ready to:
  • Create a host-only network in VirtualBox with a static subnet.
  • Build an Ubuntu Server VM with SSH access on 192.168.56.10.
  • Build a Windows 11 VM with RDP access on 192.168.56.20.
Create the host-only network

A host-only network creates a private subnet visible only to your Windows host and any VMs attached to it. No internet access, no exposure to your home network.

What is a host-only network?

VirtualBox host-only networking adds a virtual network adapter to your host machine. VMs on this network can talk to each other and to the host, but nothing else.

This isolation mirrors how production PAM targets sit on restricted network segments. The Windows host at 192.168.56.1 acts as the only gateway into this private subnet.

  • In VirtualBox Manager, click File in the top menu bar.
  • Select Tools, then Network Manager.
  • Click Create to add a new host-only network.
  • Select the new network entry that appears.
  • In the Adapter tab, set IPv4 Address to 192.168.56.1.
  • Set IPv4 Network Mask to 255.255.255.0.
  • Switch to the DHCP Server tab.
  • Uncheck Enable Server.
  • Click Apply and close the Network Manager.

Why disable DHCP?

Disabling the DHCP server forces you to assign static IPs to each VM manually. This ensures Guacamole always connects to a predictable address and mimics production environments where privileged targets have fixed addresses.

  • Verify the network was created by opening a terminal on your Windows host and running this command:
ipconfig

Look for an entry called VirtualBox Host-Only Ethernet Adapter showing 192.168.56.1 as the IPv4 address.

Don't see the adapter?

Make sure you clicked Apply in the Network Manager before closing it. If the adapter still doesn't appear, try closing and reopening VirtualBox Manager.

Help me troubleshoot my VirtualBox host-only network not appearing in ipconfig

Download the OS images

You need two ISO files before you can build the VMs. Both are free to download from their official pages.

How big are these downloads?

The Ubuntu Server ISO is roughly 2 GB and the Windows 11 ISO is roughly 5 GB. Both downloads can take a while on slower connections, so start them now and continue setting up the host-only network while they finish.

Create the Ubuntu Server VM

This VM will be your Linux target. Apache Guacamole will proxy SSH connections into it and record every terminal session.

  • In VirtualBox Manager, click New.
  • Set Name to LabLinux.
  • Set Type to Linux.
  • Set Version to Ubuntu (64-bit).
  • Set memory to 2048 MB.
  • Create a virtual hard disk with 20 GB (dynamically allocated).
  • Click Finish to create the VM.

Now configure the VM's network and storage before booting it.

  • Right-click LabLinux in the VM list and select Settings.
  • Go to Network > Adapter 1.
  • Set Attached to to Host-Only Adapter.
  • Select the network you created in the previous substep from the Name dropdown.
  • Go to Storage.
  • Click the empty optical drive icon under Controller: IDE.
  • Click the disc icon on the right and choose Choose a disk file.
  • Select the Ubuntu Server ISO you downloaded earlier.
  • Click OK to save settings.

Start the VM and walk through the Ubuntu Server installer.

How long does the install take?

The Ubuntu Server installation typically takes 10-15 minutes depending on your disk speed. You can continue reading ahead while it installs, but you'll need to respond to a few prompts along the way.

  • Double-click LabLinux to start the VM.
  • Follow the installer prompts with default selections until you reach the network configuration screen.
  • On the network configuration screen, select your network interface.
  • Choose Edit IPv4.
  • Change the method to Manual.
  • Set Subnet to 192.168.56.0/24.
  • Set Address to 192.168.56.10.
  • Leave Gateway blank.
  • Leave Name servers blank.
  • Save the network configuration and continue with the installer.
  • When the installer asks about installing additional server software, check OpenSSH server.
  • Set your username to labuser and password to LabPass123 when prompted for user creation.
  • Complete the installation and reboot when prompted.

After the VM reboots, verify SSH access from your Windows host.

  • Open a terminal on your Windows host and run this command:
ssh labuser@192.168.56.10

Type yes when asked to accept the host key fingerprint. Enter LabPass123 as the password. You should see a Linux shell prompt.

  • Type exit to close the SSH session.

Connection refused or timed out?

If you see a connection refused error, the SSH service may not be running. Log into the VM console directly and run sudo systemctl status ssh to check.

If the connection times out, the static IP may not be configured correctly. In the VM console, run ip addr and confirm the interface shows 192.168.56.10.

Help me troubleshoot SSH connection issues to my Ubuntu Server VM

Create the Windows 11 VM

This VM will be your Windows target. Guacamole will proxy RDP connections into it and record every desktop session.

  • In VirtualBox Manager, click New.
  • Set Name to LabWindows.
  • Set Type to Microsoft Windows.
  • Set Version to Windows 11 (64-bit).
  • Set memory to 4096 MB.
  • Create a virtual hard disk with 50 GB (dynamically allocated).
  • Click Finish to create the VM.

Configure settings for the Windows VM before booting.

  • Right-click LabWindows and select Settings.
  • Go to System and uncheck Secure Boot.
  • Go to Network > Adapter 1.
  • Set Attached to to Host-Only Adapter.
  • Select the same host-only network you used for the Ubuntu VM.
  • Go to Storage.
  • Click the empty optical drive and attach the Windows 11 ISO you downloaded.
  • Click OK to save settings.

Why disable Secure Boot?

The Windows 11 Enterprise Evaluation ISO requires Secure Boot to be disabled when running in VirtualBox. Without this change, the installer will refuse to proceed.

Start the VM and walk through the Windows installer.

  • Double-click LabWindows to start the VM.
  • When the Windows installer loads, click Install now.
  • Click I don't have a product key when prompted.
  • Select Windows 11 Enterprise from the edition list.
  • Choose Custom: Install Windows only for the installation type.
  • Select the virtual disk and click Next to begin installation.
  • When prompted to create a user account, set the username to labuser and password to LabPass123.

Use a local account

The installer may push you toward a Microsoft account. Look for the option to use a local account or offline account instead. You need a simple local login for Guacamole to connect with.

Once Windows finishes installing and you reach the desktop, configure a static IP address.

  • Click the Start menu and open Settings.
  • Go to Network & Internet.
  • Click Ethernet.
  • Click Edit next to IP assignment.
  • Change the setting to Manual.
  • Toggle IPv4 to On.
  • Set IP address to 192.168.56.20.
  • Set Subnet mask to 255.255.255.0.
  • Leave Gateway and DNS blank.
  • Click Save.

Now enable Remote Desktop so Guacamole can connect via RDP.

  • In Settings, go to System.
  • Click Remote Desktop.
  • Toggle Remote Desktop to On.
  • Confirm the prompt to enable it.

Verify RDP access from your Windows host.

  • On your Windows host, press the Windows key to open search.
  • Type Remote Desktop Connection and press Enter.
  • Enter 192.168.56.20 in the Computer field.
  • Click Connect.
  • Enter labuser and LabPass123 when prompted for credentials.
  • Accept the certificate warning if it appears.

You should see the Windows 11 desktop appear inside the Remote Desktop window. Close the Remote Desktop Connection after confirming it works.

RDP connection failing?

If the connection is refused, confirm Remote Desktop is toggled on inside the LabWindows VM. Also verify the static IP is set correctly by opening Command Prompt in the VM and running ipconfig.

If the connection times out, check that LabWindows is using the Host-Only Adapter in its VirtualBox network settings.

Help me troubleshoot my RDP connection to the Windows 11 VM

Both target VMs are online and reachable from your Windows host. Next up, you'll deploy the Guacamole stack that sits between you and these machines, recording every session that passes through.

Deploy the Guacamole Compose Stack

Your target VMs are online and reachable. You verified SSH into the Linux machine and RDP into the Windows machine, confirming both are ready to accept connections.

Now you need the proxy layer that sits between you and those machines, recording every action. Apache Guacamole is a browser-based gateway that supports SSH, RDP, and VNC. In this step, you'll deploy it as a Docker Compose stack on port 8081, running alongside Part 1's Conjur on port 8080.

In this step, get ready to:
  • Generate the PostgreSQL schema script for Guacamole's database.
  • Create a three-service Docker Compose file (guacd, PostgreSQL, Guacamole).
  • Start the stack and confirm the Guacamole dashboard loads in your browser.
Generate the database initialization script

Guacamole stores connection configurations, user accounts, and session history in a relational database. Before the stack can start, PostgreSQL needs a schema that creates all of Guacamole's tables and the default admin user.

  • Create the project directory and generate the schema by running these commands in your terminal:
mkdir C:\guacamole-psm
cd C:\guacamole-psm
docker run --rm guacamole/guacamole:1.6.0 /opt/guacamole/bin/initdb.sh --postgresql > initdb.sql

What do these commands do?

  • mkdir C:\guacamole-psm creates the project folder on your Windows machine.
  • cd C:\guacamole-psm moves your terminal into that folder.
  • The docker run command starts a temporary Guacamole container, runs the built-in initdb.sh script to output the PostgreSQL schema SQL, and pipes it into a file called initdb.sql. The container removes itself immediately after (--rm).
  • Confirm the script was generated by running:
dir initdb.sql

You should see initdb.sql listed with a file size of several kilobytes.

File is empty or missing?

  • Make sure Docker Desktop is running. The docker run command needs the Docker engine to pull and run the image.
  • Check that you typed the image name exactly as guacamole/guacamole:1.6.0 with the tag included.
  • If the file exists but is 0 bytes, delete it and re-run the docker run command. A network timeout during the image pull can cause an empty redirect.

Why isn't my initdb.sql generating correctly?

Create the Docker Compose file

The Compose file defines three containers that work together. The guacd service handles the actual remote connections and writes recordings. PostgreSQL stores Guacamole's configuration data. The guacamole service provides the web interface you'll access in your browser.

  • Create a new file called docker-compose.yml in C:\guacamole-psm by running:
notepad docker-compose.yml
  • Paste the following content into the file:
services:
  guacd:
    image: guacamole/guacd:1.6.0
    container_name: guacamole-psm-guacd-1
    restart: unless-stopped
    volumes:
      - recordings:/var/lib/guacamole/recordings

  postgres:
    image: postgres:17
    container_name: guacamole-psm-postgres-1
    restart: unless-stopped
    environment:
      POSTGRES_DB: guacamole_db
      POSTGRES_USER: guacamole_user
      POSTGRES_PASSWORD: guacamole_pass
    volumes:
      - pgdata:/var/lib/postgresql/data
      - ./initdb.sql:/docker-entrypoint-initdb.d/initdb.sql:ro

  guacamole:
    image: guacamole/guacamole:1.6.0
    container_name: guacamole-psm-guacamole-1
    restart: unless-stopped
    depends_on:
      - guacd
      - postgres
    ports:
      - "8081:8080"
    environment:
      GUACD_HOSTNAME: guacd
      POSTGRESQL_HOSTNAME: postgres
      POSTGRESQL_DATABASE: guacamole_db
      POSTGRESQL_USERNAME: guacamole_user
      POSTGRESQL_PASSWORD: guacamole_pass
      RECORDING_ENABLED: "true"
      RECORDING_SEARCH_PATH: /var/lib/guacamole/recordings
    volumes:
      - recordings:/var/lib/guacamole/recordings:ro

volumes:
  pgdata:
  recordings:

What does each service do?

  • guacd is the connection daemon. It handles SSH, RDP, and VNC traffic and writes graphical recordings to the recordings volume.
  • postgres stores all connection configs, user accounts, and session history. The initdb.sql file is mounted into /docker-entrypoint-initdb.d/ so PostgreSQL automatically runs it on first startup to create the required tables.
  • guacamole is the web application running on Tomcat. It maps port 8081 on your Windows host to its internal port 8080. The RECORDING_ENABLED and RECORDING_SEARCH_PATH environment variables activate the recording playback extension.

The guacamole service mounts the recordings volume as read-only (:ro) because it only needs to read recordings for playback. The guacd service has full write access since it creates the recording files.

  • Save the file in Notepad (**File** > **Save**), then close Notepad.
  • Confirm the file exists by running:
dir docker-compose.yml

You should see docker-compose.yml listed in the output.

File not showing up?

  • Notepad may have saved the file as docker-compose.yml.txt. In the Save dialog, change **Save as type** to **All Files (*.*)** before saving.
  • Make sure your terminal is still in C:\guacamole-psm. Run cd C:\guacamole-psm if you're unsure.

Help me fix my docker-compose.yml file creation on Windows.

✔️ Awesome, I've got everything!

Great. Make sure you've saved docker-compose.yml before moving on.

ⓧ I'd like to double check the full code

Here is the complete docker-compose.yml file. Compare yours against it character by character:

services:
  guacd:
    image: guacamole/guacd:1.6.0
    container_name: guacamole-psm-guacd-1
    restart: unless-stopped
    volumes:
      - recordings:/var/lib/guacamole/recordings

  postgres:
    image: postgres:17
    container_name: guacamole-psm-postgres-1
    restart: unless-stopped
    environment:
      POSTGRES_DB: guacamole_db
      POSTGRES_USER: guacamole_user
      POSTGRES_PASSWORD: guacamole_pass
    volumes:
      - pgdata:/var/lib/postgresql/data
      - ./initdb.sql:/docker-entrypoint-initdb.d/initdb.sql:ro

  guacamole:
    image: guacamole/guacamole:1.6.0
    container_name: guacamole-psm-guacamole-1
    restart: unless-stopped
    depends_on:
      - guacd
      - postgres
    ports:
      - "8081:8080"
    environment:
      GUACD_HOSTNAME: guacd
      POSTGRESQL_HOSTNAME: postgres
      POSTGRESQL_DATABASE: guacamole_db
      POSTGRESQL_USERNAME: guacamole_user
      POSTGRESQL_PASSWORD: guacamole_pass
      RECORDING_ENABLED: "true"
      RECORDING_SEARCH_PATH: /var/lib/guacamole/recordings
    volumes:
      - recordings:/var/lib/guacamole/recordings:ro

volumes:
  pgdata:
  recordings:
Start the stack and verify the dashboard

With both files in place, Docker Compose can pull the images and launch all three containers in the correct order.

  • Start the full stack by running:
docker compose up -d

What does this command do?

The -d flag runs the containers in detached mode (in the background). Docker Compose reads your docker-compose.yml, pulls any missing images, creates the named volumes, and starts the containers in dependency order (guacd and postgres first, then guacamole).

  • Wait 15-20 seconds for PostgreSQL to initialize the schema on first run.
  • Open your browser and navigate to http://localhost:8081/guacamole/.
  • Log in with username guacadmin and password guacadmin.

You should see the Guacamole dashboard with a blank connections screen. This confirms all three containers are running and communicating correctly.

Login page not loading?

  • Check the container logs by running docker compose logs guacamole in your terminal. Look for database connection errors.
  • Make sure you waited at least 15 seconds after docker compose up -d. PostgreSQL needs time to run the schema script on first boot.
  • Verify that port 8081 is not already in use by another application. Run netstat -an | findstr 8081 to check.
  • Make sure the URL includes the trailing /guacamole/ path. The base URL http://localhost:8081/ alone will return a 404.

Why can't I reach the Guacamole login page?

💡 Why port 8081 instead of 8080?

Part 1 of this lab series has Conjur running on port 8080. Guacamole's internal Tomcat server also listens on 8080, but the Compose file maps it to 8081 externally so both stacks can run side by side without a port conflict.

Your PSM proxy is live. Next up, you'll configure it to connect to both target VMs with session recording enabled so every action is captured.

Configure Recorded SSH and RDP Connections

Your Guacamole stack is live and ready for connections. Now comes the core of Privileged Session Management: routing every privileged session through a proxy that records what happens.

In this step, you will add both target VMs as connections in Guacamole with session recording enabled. Every SSH and RDP session that passes through Guacamole will write a graphical recording to disk, stored in a unique directory per session.

In this step, get ready to:
  • Create an SSH connection to LabLinux with session recording enabled.
  • Create an RDP connection to LabWindows with session recording enabled.
  • Test both connections and confirm recordings are written to disk.
Add the SSH connection

Each connection in Guacamole defines how to reach a target machine and what to record. You will configure the LabLinux VM first, with a recording path that creates a unique directory for every session.

  • In the Guacamole dashboard, click your username guacadmin in the top-right corner.
  • Click Settings.
  • Click the Connections tab.
  • Click New Connection.

Fill in the connection details as follows:

  • Enter LabLinux-SSH in the Name field.
  • Select SSH from the Protocol dropdown.

Scroll down to the Network section:

  • Enter 192.168.56.10 in the Hostname field.
  • Enter 22 in the Port field.

Scroll down to the Authentication section:

  • Enter labuser in the Username field.
  • Enter LabPass123 in the Password field.

Scroll down to the Screen Recording section. This is where the PSM magic happens:

  • Enter ${HISTORY_PATH}/${HISTORY_UUID} in the Recording path field.
  • Enter session in the Recording name field.
  • Check the Automatically create recording path checkbox.
  • Check the Include key events checkbox.
  • Click Save at the bottom of the page.

What does the recording path do?

The path ${HISTORY_PATH}/${HISTORY_UUID} tells Guacamole to create a unique subdirectory for every session. ${HISTORY_PATH} resolves to the recording search path you configured in your Docker Compose file (/var/lib/guacamole/recordings).

${HISTORY_UUID} is a unique identifier for each session. This means every recording lands in its own folder, making it easy for the playback extension to locate and display recordings in the history screen.

💡 Why include key events?

Enabling key events means Guacamole records every keystroke alongside the graphical recording. During playback, an auditor can see a timeline of exactly what was typed. This is critical for compliance investigations where you need to prove what commands an operator entered.

Add the RDP connection

Now you will add the Windows target using the same recording configuration. The only differences are the protocol, hostname, port, and an extra security setting for the self-signed certificate.

  • Return to Settings > Connections.
  • Click New Connection.
  • Enter LabWindows-RDP in the Name field.
  • Select RDP from the Protocol dropdown.

In the Network section:

  • Enter 192.168.56.20 in the Hostname field.
  • Enter 3389 in the Port field.

In the Authentication section:

  • Enter labuser in the Username field.
  • Enter LabPass123 in the Password field.

In the Security section:

  • Set Ignore server certificate to true.

Why ignore the server certificate?

The Windows 11 evaluation VM uses a self-signed certificate for RDP. Without this setting, Guacamole would reject the connection because it cannot verify the certificate against a trusted authority. In a production environment, you would use proper certificates instead.

In the Screen Recording section:

  • Enter ${HISTORY_PATH}/${HISTORY_UUID} in the Recording path field.
  • Enter session in the Recording name field.
  • Check the Automatically create recording path checkbox.
  • Check the Include key events checkbox.
  • Click Save.
Test both connections

With both connections configured, you will open each one to generate session recordings. This proves that Guacamole is proxying your access and writing recordings to the volume.

  • Click your username in the top-right corner and select Home to return to the Guacamole home screen.
  • Click LabLinux-SSH to open the SSH session.

You should see a terminal session appear in your browser. This is the SSH connection to your Ubuntu VM, proxied through Guacamole.

  • Type whoami and press Enter. You should see labuser returned.
  • Type hostname and press Enter.
  • Type ls / and press Enter to list the root directory contents.
  • Press Ctrl+Alt+Shift to open the Guacamole side menu.
  • Click Disconnect in the side menu to end the session.

The Guacamole side menu

The Ctrl+Alt+Shift shortcut is the only way to access Guacamole controls during an active session. Without it, all keyboard input goes directly to the remote machine. You will use this shortcut every time you need to disconnect.

Now test the Windows connection:

  • Return to the Guacamole home screen.
  • Click LabWindows-RDP to open the RDP session.

You should see the Windows desktop appear in your browser. This is the RDP connection to your Windows 11 VM, fully proxied through Guacamole.

  • Open Notepad from the Start menu.
  • Type some text (anything you like) to generate keystroke activity.
  • Press Ctrl+Alt+Shift to open the Guacamole side menu.
  • Click Disconnect.

Connection not opening?

If the SSH connection fails, confirm your LabLinux VM is running in VirtualBox and that SSH is accessible. Open a terminal on your Windows host and try ssh labuser@192.168.56.10 directly to rule out a VM-level issue.

If the RDP connection fails, confirm your LabWindows VM is running and that Remote Desktop is still enabled. Also double-check that Ignore server certificate is set to true in the connection settings.

help me troubleshoot why my Guacamole connection is failing

Both sessions are now recorded. Every command you typed in the SSH session and every action you took on the Windows desktop has been captured by Guacamole and written to the recordings volume.

Your PSM proxy is now actively recording sessions. Next up, you will verify that these recordings can be played back directly from the Guacamole history screen.

Verify Session Recording Playback

Your connections are configured and both test sessions have passed through the Guacamole proxy. The recording files should now be sitting on disk inside the guacd container, ready for playback.

In production Privileged Session Management, an auditor needs to pull up any past session and replay exactly what happened. If recordings exist on disk but can't be played back through the interface, the system hasn't delivered on its core promise. This step confirms end-to-end recording and playback.

In this step, get ready to:
  • Confirm recording files exist on disk inside the guacd container.
  • Play back both SSH and RDP sessions from the Guacamole history screen.
  • Verify that keystroke events appear in the playback timeline.
Check recordings on disk

Before trusting the web UI, you can verify that recording files physically exist in the Docker volume mounted inside the guacd container.

  • List the recording directories by running this command in a terminal on your Windows host:
docker exec guacamole-psm-guacd-1 ls /var/lib/guacamole/recordings

You should see UUID-named directories listed in the output. Each directory corresponds to one session you opened in the previous step.

  • Pick one of the UUIDs from the output above. Confirm a recording file exists inside it by replacing the UUID in this command and running it:
docker exec guacamole-psm-guacd-1 ls /var/lib/guacamole/recordings/[[RECORDING_UUID="paste one UUID from the output above"]]

What am I looking at?

Each UUID directory contains a file named session. This is the raw graphical recording written by guacd during the proxied connection. The UUID matches the history entry in Guacamole's database, which is how the recording storage extension locates the correct file for playback.

🙋‍♀️ No directories listed?

Make sure you completed a full connection in the previous step (opened the session, ran commands, then disconnected). If you only connected briefly without disconnecting, the recording file may not have been flushed to disk.

Also confirm the container name is correct. Run docker ps and check that guacamole-psm-guacd-1 is listed.

Help me troubleshoot why no recording directories appear in the guacd container.

Play back recordings from the history screen

Guacamole's recording storage extension makes playback as simple as clicking a link in the connection history. The extension scans the configured search path for UUID-named directories and links them to the matching history entries.

  • Switch back to your browser with Guacamole open at http://localhost:8081/guacamole/.
  • Click the guacadmin username in the top-right corner to open the menu.
  • Click Settings.
  • Click the History tab.

You should see a table listing your recent connections, including entries for both the SSH and RDP sessions you tested.

  • In the Logs column, find the row for your SSH session (LabLinux-SSH).
  • Click View next to that entry.

The recording player loads in the browser. You should see a progress bar and playback controls.

  • Press Play to start the recording playback.
  • Watch the terminal session replay. You should see the exact commands you typed earlier (whoami, hostname, ls /) reproduced on screen.
  • Navigate back to the History tab after the playback finishes or you have seen enough.
  • Click View next to the RDP session entry (LabWindows-RDP).
  • Press Play to watch the RDP recording. You should see the Windows desktop and the Notepad actions you performed.

Why is in-browser playback important for PSM?

Commercial PAM tools charge thousands of dollars for this exact capability. An auditor can review any past privileged session without installing special software or accessing the target system directly. The browser-based player means anyone with Guacamole access and the right permissions can review recordings immediately.

🙋‍♀️ No View link in the Logs column?

The recording storage extension can only find recordings if the directory name matches the session's UUID. Confirm that your connection's recording path is set to ${HISTORY_PATH}/${HISTORY_UUID} and that Automatically create recording path is checked.

Also verify that RECORDING_ENABLED is set to "true" in your docker-compose.yml and the recordings volume is mounted read-only on the guacamole service.

Help me figure out why the View link doesn't appear for my recorded sessions.

Verify keystroke visibility in playback

Guacamole 1.6.0 introduces a key event timeline in the recording player. Because you enabled "Include key events" on both connections, the player should display a histogram of keyboard activity alongside the playback progress bar.

  • Open the SSH recording again from the History tab by clicking View.
  • Look above the playback timeline bar. You should see a histogram showing peaks where keyboard activity occurred.
  • Hover over the progress bar. Heatmaps of screen update activity and key events appear as you move across the timeline.
  • Repeat for the RDP recording to confirm key events are captured there as well.

What does keystroke visibility prove?

In a compliance audit, recording the screen is not always enough. An auditor may need to know exactly what was typed, especially for commands that produced no visible output or for password entries. The key event histogram lets a reviewer jump directly to moments of keyboard activity without watching the entire session.

This is the same "record everything" principle that enterprise PAM products like CyberArk PSM enforce. You now have it running locally for free.

Secret mission

Export a Recording to MP4 Video

In production PAM environments, auditors often need session recordings in a standard video format they can review without logging into Guacamole. Can you export one of your recorded sessions to an MP4 file that plays in any media player?

Clean Up Your Resources

Clean Up Your Resources

Decide whether to keep your resources running, pause them to come back later, or delete them entirely. This project runs entirely locally, so there are no ongoing cloud costs. The VMs and Docker containers consume disk space and memory while running.

Resources you used:

  • 2 VirtualBox VMs (LabLinux, LabWindows) consuming disk space.
  • 3 Docker containers (guacd, postgres, guacamole) consuming memory when running.
  • Docker volumes (pgdata, recordings) storing database and session recording data.
  • VirtualBox host-only network (192.168.56.0/24).
  • Local project directory C:\guacamole-psm with Compose file, init script, and exported video.

Keep everything running

No action needed. Choose this if you are still actively building or plan to continue with Part 3 of the lab series.

  • Leave both VMs running in VirtualBox.
  • Leave the Docker Compose stack running.
  • Guacamole remains accessible at http://localhost:8081/guacamole/ and your recordings stay on the volume.

Pause - I'll come back to this later

Shut down running processes to free up memory. All data is preserved on disk so you can resume later.

  • In VirtualBox Manager, right-click each VM (LabLinux, LabWindows) and select Close then Power Off.
  • Stop the Docker Compose stack by running this command in a terminal:
docker compose -f C:\guacamole-psm\docker-compose.yml stop

Your VMs retain their disk images and the Docker volumes keep all database and recording data. When you are ready to resume, start the VMs from VirtualBox Manager and run docker compose -f C:\guacamole-psm\docker-compose.yml start.

Delete - I don't want to use this again

Remove all project resources and start fresh. This is irreversible.

  • Remove the Docker containers and volumes by running:
docker compose -f C:\guacamole-psm\docker-compose.yml down -v
  • Delete both VMs from VirtualBox Manager. Right-click LabLinux and select Remove, then Delete all files. Repeat for LabWindows.
  • Remove the host-only network. In VirtualBox Manager, go to File > Tools > Network Manager. Select the 192.168.56.1 network and click Remove.
  • Delete the local project directory by running:
rmdir /s /q C:\guacamole-psm

After these steps, all VMs, containers, volumes, recordings, and project files are gone.

Nice Work!

Nice Work!

You just built a fully functional Privileged Session Manager for your home PAM lab. Every SSH and RDP session now flows through a recording proxy, giving you the same "record everything" capability that enterprise PAM tools provide.

Here's what you accomplished:

  • Built two target VMs (Ubuntu Server + Windows 11) on an isolated host-only network with static IPs.
  • Deployed Apache Guacamole 1.6.0 as a PSM proxy alongside Part 1's Conjur, using Docker Compose with PostgreSQL 17.
  • Configured SSH and RDP connections with graphical session recording and verified in-browser playback from the Guacamole history screen.
  • Secret Mission: exported a recording to MP4 video using guacenc for offline compliance review.

Ready to quiz yourself?