---
title: "Control an Ubuntu Laptop from Windows 11: SSH, Hermes and GNOME Remote Desktop"
url: "https://raoofaltaher.com/tutorials/ubuntu-remote-access-from-windows"
description: "Set up a Windows 11 laptop to control a physical Ubuntu laptop on your home network: a static IP, SSH key access, Hermes Agent commands on Ubuntu, and GNOME Remote Desktop for the real screen and a separate login, with one-click shortcuts."
---

# Control an Ubuntu Laptop from Windows 11: SSH, Hermes and GNOME Remote Desktop

-   ubuntu
-   windows
-   openssh
-   gnome-remote-desktop
-   hermes-agent
-   powershell

This tutorial turns a Windows 11 laptop into the control seat for a physical Ubuntu 26.04 laptop on the same home network. You get passwordless SSH, Hermes Agent running its terminal commands on Ubuntu, and Ubuntu’s built-in GNOME Remote Desktop in two modes: the real screen, and a separate login session, each one click away.

![A title card for a workspace setup guide in yellow lettering over a blurred red and blue photo, with the labels SSH, remote control, Termius, Hermes, Claude Code, Kali, Ubuntu and Windows](/_astro/remote-workspace-setup.CEe_X3hK_1webH6.avif)

I use this setup on my own machines, and I repeat it every time I add a laptop. The steps below are the ones I follow, with my own addresses, names and keys replaced by example values. It is the companion of [Control a Kali Linux Laptop from Windows 11](/tutorials/kali-linux-remote-access-from-windows): same Windows laptop, and the Kali laptop sits on the same network.

What this tutorial covers:

-   the LAN and a static IP;
-   SSH key access;
-   Hermes **Mode A** (agent on Windows, commands on Ubuntu);
-   Ubuntu’s built-in **GNOME Remote Desktop**, in two modes: **Desktop Sharing** (the real screen) and **Remote Login** (a separate session);
-   one-click desktop shortcuts with an Ubuntu icon.

## 0\. Before you start

[Section titled “0. Before you start”](#0-before-you-start)

### 0.1 The values this tutorial uses

[Section titled “0.1 The values this tutorial uses”](#01-the-values-this-tutorial-uses)

Every command in this tutorial uses the example values below, so you can copy it as it is. **These are not your values.** Replace each one with your own before you run a command.

| What | Value in this tutorial | How to find yours |
| --- | --- | --- |
| Home network | 192.168.50.0/24 | ipconfig on Windows |
| Router (gateway and DNS) | 192.168.50.1 | ipconfig → Default Gateway |
| Windows laptop address | 192.168.50.10 | ipconfig → Wi-Fi → IPv4 Address |
| Ubuntu laptop address (static) | 192.168.50.21 | You choose it in Phase 1 |
| Kali laptop address (other tutorial) | 192.168.50.20 | Only if you also follow the Kali tutorial |
| Wi-Fi connection name on Ubuntu | HomeWiFi | nmcli connection show --active on Ubuntu |
| Ubuntu Wi-Fi interface | wlp2s0 | ip -4 -br addr on Ubuntu |
| Ubuntu account | labuser | whoami on Ubuntu |
| Ubuntu hostname | lab-Ubuntu | hostname on Ubuntu |
| Windows account folder | C:\Users\Lab User | echo $env:USERPROFILE in PowerShell |
| SSH alias, key file and Hermes profile | ubuntu-lab, ubuntu_lab, ubuntu-lab | You choose them in Phases 4 and 5 |
| Folder for the RDP files and the icon | C:\RemoteAccess | You choose it in Appendix B |

The example Windows account folder contains a space on purpose. Many real account folders have one, and a space breaks a path that is not in quotes.

A short **Your values** note marks each command that holds a value from this table.

### 0.2 Name consistency

[Section titled “0.2 Name consistency”](#02-name-consistency)

-   The SSH **alias** is `ubuntu-lab` (hyphen).
-   The **key file** is `ubuntu_lab` (underscore).
-   The machine’s **hostname** is `lab-Ubuntu`.
-   The **login user** is `labuser`.
-   The **Hermes profile** is `ubuntu-lab`.

All the hostname spellings work with SSH because they share one `Host` block (Phase 4.3).

### 0.3 Ports and services

[Section titled “0.3 Ports and services”](#03-ports-and-services)

| Port | Service on Ubuntu | Purpose | Windows uses |
| --- | --- | --- | --- |
| TCP 22 | OpenSSH (ssh.socket → ssh.service) | SSH, Hermes Mode A | ssh ubuntu-lab, Hermes profile ubuntu-lab |
| TCP 3390 | GNOME Remote Desktop, user daemon: Desktop Sharing | The real screen | Shortcut Ubuntu Screen (C:\RemoteAccess\Ubuntu-Screen.rdp) |
| TCP 3389 | GNOME Remote Desktop, system daemon: Remote Login | A separate login session | Shortcut Ubuntu Login (C:\RemoteAccess\Ubuntu-Login.rdp) |

Both modes default to 3389. **When both are enabled, Desktop Sharing moves to 3390 and Remote Login keeps 3389** (Ubuntu documentation).

### 0.4 What you need, and the versions this was tested with

[Section titled “0.4 What you need, and the versions this was tested with”](#04-what-you-need-and-the-versions-this-was-tested-with)

-   A Windows 11 laptop with **Hermes Desktop** installed and signed in to a model provider, and PowerShell 7.3 or later.
-   A physical **Ubuntu 26.04** laptop with the default GNOME desktop, and an administrator account (a member of the `sudo` group).
-   Both laptops on the same home Wi-Fi network, which you control.

| Machine | Software | Tested version |
| --- | --- | --- |
| Ubuntu | Ubuntu Desktop | 26.04.1 LTS, GNOME 50 on Wayland |
| Ubuntu | openssh-server | 1:10.2p1-2ubuntu3.8 |
| Ubuntu | GNOME | gnome-shell 50.1, gdm3 50.1, gnome-control-center 50.3 |
| Ubuntu | gnome-remote-desktop | 50.2 (grdctl command-line tool) |
| Ubuntu | Firewall | ufw installed, not enabled (ENABLED=no) |
| Ubuntu | tmux, Hermes | not installed (not needed for Mode A; tmux optional) |
| Windows | OpenSSH client | OpenSSH_for_Windows_9.5p2 |
| Windows | Hermes Desktop | Store package NousResearchInc.HermesAgent, agent v0.22.5 |
| Windows | Remote Desktop client | built-in mstsc.exe |

The example screens are a 4K Ubuntu panel at **200 %** scale (1920×1080 logical) and a 4K Windows laptop at **200 %**. The RDP settings in Phase 6.5 refer to these.

**Administrator-account warning:** `labuser` is an administrator, so the agent in profile `ubuntu-lab` can do anything `labuser` can.

-   Never configure passwordless unrestricted `sudo`.
-   Never put the sudo password or the RDP passwords in prompts, `.env`, or scripts.
-   Keep Hermes command approvals on.

## 1\. Understand the connections

[Section titled “1. Understand the connections”](#1-understand-the-connections)

### 1.1 Architecture

[Section titled “1.1 Architecture”](#11-architecture)

flowchart LR
    subgraph W\["Windows 11 (192.168.50.10)"\]
      HD\["Hermes Desktop<br/>gateway: This device"\]
      PU\["Profile ubuntu-lab<br/>(agent on Windows)"\]
      RS\["Shortcut: Ubuntu Screen<br/>mstsc → :3390"\]
      RL\["Shortcut: Ubuntu Login<br/>mstsc → :3389"\]
    end
    subgraph U\["Ubuntu laptop (192.168.50.21, user labuser)"\]
      SSHD\["OpenSSH :22"\]
      DS\["Desktop Sharing :3390<br/>(user daemon, real screen)"\]
      RLD\["Remote Login :3389<br/>(system daemon → GDM → session)"\]
    end
    HD --> PU
    PU -- "Mode A: commands over SSH (key ubuntu\_lab)" --> SSHD
    RS -- "RDP login labuser + RDP password" --> DS
    RL -- "RDP login labuser + RDP password, then Linux password at GDM" --> RLD

### 1.2 Hermes: Mode A only

[Section titled “1.2 Hermes: Mode A only”](#12-hermes-mode-a-only)

The Hermes agent and model run on Windows. The profile `ubuntu-lab` sends only its **terminal commands** to Ubuntu over SSH. Nothing Hermes-related is installed on Ubuntu, and there is no Desktop SSH gateway for Ubuntu.

To add Mode B later, follow Phase 6 Option B of the [Kali tutorial](/tutorials/kali-linux-remote-access-from-windows). Ubuntu’s shell is bash, so the `~/.zshenv` step there does not apply.

### 1.3 Choose how to see Ubuntu

[Section titled “1.3 Choose how to see Ubuntu”](#13-choose-how-to-see-ubuntu)

| Aspect | Desktop Sharing (“Ubuntu Screen”) | Remote Login (“Ubuntu Login”) |
| --- | --- | --- |
| Shows | The real screen of the logged-in user (mirror-primary) | A new session for your account, started remotely |
| Requires | You are logged in on the laptop | You are not logged in locally. Otherwise Ubuntu offers to end the local session |
| Resolution | Ubuntu’s own (for example 3840×2160), scaled by Windows to fit (smart sizing) | Follows the Windows window (dynamic resolution); sharp |
| Daemon | systemctl --user service gnome-remote-desktop (runs as your account) | system service gnome-remote-desktop + gnome-remote-desktop-handover |
| Where its login is stored | your GNOME login keyring | root-only GKeyFile under /var/lib/gnome-remote-desktop (on a laptop without a TPM chip) |
| Port (both enabled) | 3390 | 3389 |

## Phase 1: Confirm the LAN and give Ubuntu its own static IP

[Section titled “Phase 1: Confirm the LAN and give Ubuntu its own static IP”](#phase-1-confirm-the-lan-and-give-ubuntu-its-own-static-ip)

### 1.1 Confirm Windows’ address

[Section titled “1.1 Confirm Windows’ address”](#11-confirm-windows-address)

```
ipconfig                     # Wireless LAN adapter Wi-Fi: 192.168.50.10 / 255.255.255.0 / 192.168.50.1Get-NetConnectionProfile     # Wi-Fi -> Private
```

### 1.2 Inspect Ubuntu’s network

[Section titled “1.2 Inspect Ubuntu’s network”](#12-inspect-ubuntus-network)

On the Ubuntu laptop, in a terminal (no sudo):

```
echo "host=$(hostname) user=$(whoami) shell=$SHELL session=$XDG_SESSION_TYPE desktop=$XDG_CURRENT_DESKTOP"grep PRETTY_NAME /etc/os-releaseip -4 -br addr | grep -v '^lo'ip route | grep defaultnmcli -t -f NAME,DEVICE connection show --active
```

Example output, before the static address is set:

```
host=lab-Ubuntu user=labuser shell=/bin/bash session=wayland desktop=ubuntu:GNOMEPRETTY_NAME="Ubuntu 26.04.1 LTS"wlp2s0   UP   192.168.50.20/24default via 192.168.50.1 dev wlp2s0 proto dhcp src 192.168.50.20 metric 600HomeWiFi:wlp2s0
```

**An address conflict to watch for.** In the example above, the router’s DHCP gave Ubuntu **`192.168.50.20`**, which is the Kali laptop’s static address from the other tutorial. This happens when Kali is switched off: the router considers the address free, because Kali’s static address is set on Kali only and is **not reserved in the router**.

Running both laptops like that makes SSH and RDP reach whichever one answers first. It also confuses Windows: `lab-ubuntu.local` resolves to the Kali address, and port 22 is refused.

**Rule:** every laptop gets its **own** static address. In this tutorial, Ubuntu gets **`192.168.50.21`**. Before you choose an address, confirm from Windows that nothing answers there:

```
foreach ($ip in '192.168.50.21','192.168.50.22','192.168.50.23') {  "{0}: ping={1}" -f $ip, (Test-Connection $ip -Count 1 -TimeoutSeconds 1 -Quiet) }# 192.168.50.21: ping=False  (and no ARP entry), so it looks unused. Not absolute proof.
```

If the router’s DHCP pool includes the address you choose, a future device could still receive it. If that ever happens, pick an address outside the pool.

### 1.3 Set the static address on Ubuntu (NetworkManager)

[Section titled “1.3 Set the static address on Ubuntu (NetworkManager)”](#13-set-the-static-address-on-ubuntu-networkmanager)

At the Ubuntu laptop’s own keyboard (the network drops briefly):

```
CON=$(nmcli -t -f NAME,DEVICE connection show --active | grep -v ':lo$' | head -1 | cut -d: -f1); echo "connection: $CON"   # HomeWiFisudo nmcli connection modify "$CON" ipv4.method manual ipv4.addresses 192.168.50.21/24 ipv4.gateway 192.168.50.1 ipv4.dns 192.168.50.1sudo nmcli connection up "$CON"ip -4 -br addr | grep -v '^lo'          # wlp2s0 UP 192.168.50.21/24
```

GUI alternative: **Settings → Wi-Fi → (gear on your network) → IPv4 → Manual**, with address `192.168.50.21`, netmask `255.255.255.0`, gateway `192.168.50.1`, DNS `192.168.50.1`.

### 1.4 Verify

[Section titled “1.4 Verify”](#14-verify)

```
nmcli -g ipv4.method,ipv4.addresses,ipv4.gateway,ipv4.dns,connection.autoconnect connection show HomeWiFi# manual / 192.168.50.21/24 / 192.168.50.1 / 192.168.50.1 / yesip route | head -2        # default via 192.168.50.1 dev wlp2s0 proto static metric 600ping -c 3 192.168.50.1 && getent hosts ubuntu.com
```

Reboot once and confirm the address persists. From Windows: `ping 192.168.50.21`.

## Phase 2: Verify the existing Ubuntu account

[Section titled “Phase 2: Verify the existing Ubuntu account”](#phase-2-verify-the-existing-ubuntu-account)

Do not create another user.

```
whoami; id; sudo -v
```

Expected:

-   `labuser`, uid 1000.
-   Groups: `labuser adm cdrom sudo dip plugdev users lpadmin lxd`. `adm` lets `labuser` read system logs, including Remote Login’s, without sudo.
-   Shell `/bin/bash`, so non-interactive SSH sees `~/.local/bin` normally (unlike Kali’s zsh).
-   `sudo` asks for the password (`sudo -n true` → “interactive authentication is required”). That is why you run every `sudo` step in this tutorial yourself, never through an automated tool.

## Phase 3: Install and start the SSH server on Ubuntu

[Section titled “Phase 3: Install and start the SSH server on Ubuntu”](#phase-3-install-and-start-the-ssh-server-on-ubuntu)

Ubuntu Desktop ships **without** an SSH server, so Windows sees port 22 **refused** at first.

```
sudo apt update && sudo apt install -y openssh-server && sudo systemctl enable --now sshssh-keygen -lf /etc/ssh/ssh_host_ed25519_key.pub# 256 SHA256:<your Ubuntu host key fingerprint> root@lab-Ubuntu (ED25519)   <- write this down
```

The install pulls in `openssh-sftp-server`, `ssh-import-id`, and `ncurses-term`, creates the `sshd` user, and enables **socket activation** (`ssh.socket`), so systemd starts sshd on the first connection. Verify:

```
systemctl is-active ssh.socket ssh.service      # active / activesystemctl is-enabled ssh                        # enabledss -ltn | grep ':22 '                           # LISTEN 0.0.0.0:22 and [::]:22grep -vE '^\s*(#|$)' /etc/ssh/sshd_config       # stock: Include sshd_config.d, KbdInteractiveAuthentication no, UsePAM yes, X11Forwarding yes, ...
```

The stock config allows both key and password login. You need the password exactly once, to install the key (Phase 4.4).

**Firewall:** `ufw` is installed but **not enabled** (`/etc/ufw/ufw.conf: ENABLED=no`), so ports 22, 3389, and 3390 need no rules. If you enable UFW later, allow the Windows laptop first:

```
sudo ufw allow from 192.168.50.10 to any port 22 proto tcpsudo ufw allow from 192.168.50.10 to any port 3389:3390 proto tcp
```

**OpenSSH 10 penalty:** like Kali, Ubuntu’s sshd temporarily blocks a source address after repeated failed logins (`PerSourcePenalties`). Never leave a misconfigured client retrying (Kali tutorial, Phase 3.1).

## Phase 4: Windows key, host key, and the `ubuntu-lab` alias

[Section titled “Phase 4: Windows key, host key, and the ubuntu-lab alias”](#phase-4-windows-key-host-key-and-the-ubuntu-lab-alias)

### 4.1 Create a dedicated key (PowerShell 7, normal user)

[Section titled “4.1 Create a dedicated key (PowerShell 7, normal user)”](#41-create-a-dedicated-key-powershell-7-normal-user)

```
$key = "$env:USERPROFILE\.ssh\ubuntu_lab"if (Test-Path $key) { 'ubuntu_lab already exists - leaving it untouched' } else { ssh-keygen -q -t ed25519 -N '' -C 'lab user@winpc ubuntu_lab' -f $key }ssh-keygen -lf "$key.pub"                                   # 256 SHA256:... lab user@winpc ubuntu_lab (ED25519)if (ssh-keygen -y -P '' -f $key 2>$null) { 'no passphrase (required for Hermes)' }
```

A separate key per machine (`kali_lab`, `ubuntu_lab`) means you can revoke one without touching the other. **Never overwrite** an existing key (answer **n** to `Overwrite (y/n)?`).

### 4.2 Record Ubuntu’s host key, verified out-of-band

[Section titled “4.2 Record Ubuntu’s host key, verified out-of-band”](#42-record-ubuntus-host-key-verified-out-of-band)

Accept the host key only after comparing it with the fingerprint printed **on the Ubuntu screen** in Phase 3. The Windows `ssh-keyscan` cannot fetch it:

```
choose_kex: unsupported KEX method sntrup761x25519-sha512@openssh.com
```

This is a limitation of OpenSSH for Windows 9.5 (LibreSSL build). Capture the key with `ssh` itself, attempting **no** login, into a temporary file. Then compare it and append it only if it matches:

```
$expected = 'SHA256:PASTE-THE-FINGERPRINT-FROM-PHASE-3'$tmp = Join-Path $env:TEMP 'ubuntu_known_hosts'ssh -o BatchMode=yes -o StrictHostKeyChecking=accept-new -o "UserKnownHostsFile=$tmp" -o HostKeyAlgorithms=ssh-ed25519 `    -o PubkeyAuthentication=no -o PasswordAuthentication=no -o KbdInteractiveAuthentication=no labuser@192.168.50.21 exit 2>&1 | Out-Null$line = Get-Content $tmp | Where-Object { $_ -match '^192\.168\.50\.21 ssh-ed25519 ' } | Select-Object -First 1$fp = ((ssh-keygen -lf $tmp) -split ' ')[1]; [IO.File]::Delete($tmp)if ($fp -eq $expected) { Add-Content "$env:USERPROFILE\.ssh\known_hosts" $line; 'MATCH - added' } else { 'MISMATCH - stop' }
```

The simpler alternative is to answer `yes` at the first interactive `ssh labuser@192.168.50.21`, but **only** after comparing the fingerprint it shows.

### 4.3 Add the alias block (`%USERPROFILE%\.ssh\config`)

[Section titled “4.3 Add the alias block (%USERPROFILE%\\.ssh\\config)”](#43-add-the-alias-block-userprofilesshconfig)

Append this, keeping any Kali and other blocks:

```
# Ubuntu laptop (lab-Ubuntu) on the home LAN, static 192.168.50.21Host ubuntu-lab lab-ubuntu lab-Ubuntu 192.168.50.21    HostName 192.168.50.21    User labuser    Port 22    IdentityFile ~/.ssh/ubuntu_lab    IdentitiesOnly yes
```

Why every name is listed:

-   `ubuntu-lab` is the short alias.
-   `lab-Ubuntu` / `lab-ubuntu` is what you naturally type (the hostname). Without these entries, Windows resolves that name over mDNS (Ubuntu’s local name broadcasting) to an IPv6 link-local address (`fe80::…%11`) and asks you to trust a “new” host.
-   The raw IP covers tools that connect by address.

Because all names share one `HostName`, one `known_hosts` entry serves them all. Alias matching is case-sensitive, so both spellings of the hostname are listed.

```
foreach ($n in 'ubuntu-lab','lab-ubuntu','lab-Ubuntu','192.168.50.21') {  "$n -> " + ((ssh -G $n | Select-String '^(user|hostname|identityfile) ' | ForEach-Object Line) -join ' | ') }# each: user labuser | hostname 192.168.50.21 | identityfile ~/.ssh/ubuntu_lab
```

### 4.4 Install the public key on Ubuntu (asks the Linux password once)

[Section titled “4.4 Install the public key on Ubuntu (asks the Linux password once)”](#44-install-the-public-key-on-ubuntu-asks-the-linux-password-once)

Run this in your own PowerShell window:

```
$pub = Get-Content "$env:USERPROFILE\.ssh\ubuntu_lab.pub"ssh ubuntu-lab "mkdir -p ~/.ssh && chmod 700 ~/.ssh && echo '$pub' >> ~/.ssh/authorized_keys && chmod 600 ~/.ssh/authorized_keys && echo KEY-INSTALLED"
```

Type your Ubuntu account’s Linux password, then expect `KEY-INSTALLED`.

**Do not pipe the key into ssh** (`Get-Content key.pub | ssh ...`). On the first connection, the “Are you sure you want to continue connecting (yes/no)?” question reads from that same input stream. It gets the key text instead of your `yes` and aborts with `Host key verification failed`. Passing the key inside the remote command keeps the input free.

Running the install twice appends the key twice. Remove duplicates while keeping order:

```
awk '!seen[$0]++' ~/.ssh/authorized_keys > ~/.ssh/ak.tmp && mv ~/.ssh/ak.tmp ~/.ssh/authorized_keys && chmod 600 ~/.ssh/authorized_keyswc -l < ~/.ssh/authorized_keys      # 1
```

### 4.5 Test

[Section titled “4.5 Test”](#45-test)

```
ssh -o BatchMode=yes ubuntu-lab "hostname && whoami && pwd"     # lab-Ubuntu / labuser / /home/labuser   (no prompts)ssh -o BatchMode=yes lab-Ubuntu "hostname"                      # lab-Ubuntussh ubuntu-lab                                                  # interactive: "Welcome to Ubuntu 26.04.1 LTS ..."
```

`BatchMode=yes` must pass, because Hermes connects the same way.

## Phase 5: Hermes Mode A with profile `ubuntu-lab`

[Section titled “Phase 5: Hermes Mode A with profile ubuntu-lab”](#phase-5-hermes-mode-a-with-profile-ubuntu-lab)

### 5.1 Create the profile

[Section titled “5.1 Create the profile”](#51-create-the-profile)

If you followed the Kali tutorial, clone its working profile:

```
hermes profile create ubuntu-lab --clone-from kali-lab --no-alias `  --description "Agent on Windows; terminal commands run on the Ubuntu laptop over SSH"
```

Expected output:

-   `Cloned config, .env, SOUL.md, and skills from kali-lab`, plus the installed plugins.
-   `⚠ This profile has no API keys yet`: harmless when your model provider uses a sign-in (such as Nous Portal) instead of an API key. The model is the same as in `kali-lab`.

Without a Kali profile, clone your default profile instead, then switch its terminal backend to SSH:

```
hermes profile create ubuntu-lab --clone-from default --no-alias `  --description "Agent on Windows; terminal commands run on the Ubuntu laptop over SSH"hermes -p ubuntu-lab config set terminal.backend ssh
```

`--no-alias` skips the wrapper command; use `hermes -p ubuntu-lab ...`. Confirm the backend:

```
hermes -p ubuntu-lab config get terminal.backend      # ssh
```

### 5.2 Point its SSH settings at Ubuntu

[Section titled “5.2 Point its SSH settings at Ubuntu”](#52-point-its-ssh-settings-at-ubuntu)

A profile cloned from `kali-lab` still targets Kali. This script replaces any `TERMINAL_SSH_*` lines and keeps everything else (PowerShell 7):

```
$envFile = "$env:LOCALAPPDATA\hermes\profiles\ubuntu-lab\.env"if (-not (Test-Path $envFile)) { New-Item -ItemType File -Force $envFile | Out-Null }$lines = @(Get-Content $envFile | Where-Object { $_ -notmatch '^\s*TERMINAL_SSH_' -and $_ -notmatch '^# SSH terminal backend:' })$lines += @("# SSH terminal backend: this profile's commands run on the Ubuntu laptop as 'labuser'",            'TERMINAL_SSH_HOST=192.168.50.21', 'TERMINAL_SSH_USER=labuser', 'TERMINAL_SSH_PORT=22',            'TERMINAL_SSH_KEY="C:/Users/Lab User/.ssh/ubuntu_lab"')Set-Content -Path $envFile -Value $lines -Encoding utf8NoBOMSelect-String -Path $envFile -Pattern '^TERMINAL_SSH_' | ForEach-Object Line
```

The quotes around the key path are required when your Windows account folder contains a space, as `Lab User` does. Use forward slashes and the **private** key. A Kali profile stays untouched: `hermes -p kali-lab ...` still targets the Kali laptop.

### 5.3 Restart the gateway and test

[Section titled “5.3 Restart the gateway and test”](#53-restart-the-gateway-and-test)

```
hermes gateway restart          # "Gateway started via direct spawn"; profile list shows ubuntu-lab "running"hermes profile listhermes -p ubuntu-lab chat -Q -q "Use your terminal tool to run: hostname && whoami && pwd && grep PRETTY_NAME /etc/os-release. Reply with only the output."
```

Expected output:

```
lab-Ubuntulabuser/home/labuserPRETTY_NAME="Ubuntu 26.04.1 LTS"
```

**In Hermes Desktop:**

1.  In the Sessions sidebar, select the gateway **This device**.
2.  In the profile rail, select **ubuntu-lab**.
3.  Ask the same question, and expect the same output.

The profiles side by side:

| Profile | Commands run on |
| --- | --- |
| default | Windows |
| kali-lab | Kali (192.168.50.20), from the Kali tutorial |
| ubuntu-lab | Ubuntu (192.168.50.21) |

**Desktop window backend:** `%APPDATA%\Hermes\connection.json` holds the connection mode that Desktop’s window boots into. If it points over SSH at another laptop, for example Kali’s Hermes from the Kali tutorial (its section B.5), Desktop fails at launch whenever that laptop is off, with `Desktop boot failed: Could not reach ... over SSH`. For everyday use of `ubuntu-lab`, set **Settings → Gateways →** connection mode to **Local / This device**. Kali’s own Hermes stays reachable through its **Kali Lab** gateway entry.

## Phase 6: GNOME Remote Desktop (Desktop Sharing + Remote Login)

[Section titled “Phase 6: GNOME Remote Desktop (Desktop Sharing + Remote Login)”](#phase-6-gnome-remote-desktop-desktop-sharing--remote-login)

### 6.1 Key facts before you start

[Section titled “6.1 Key facts before you start”](#61-key-facts-before-you-start)

-   Both modes speak **RDP**, so Windows uses its built-in **Remote Desktop Connection** (`mstsc`). No extra software is needed.
-   Each mode has its **own RDP username and password**, set in **Settings → System → Remote Desktop**. They are **not** your Linux password and not your Windows/Microsoft account. This tutorial uses the username **`labuser`** for both modes; use the same password for both (6.6).
-   **Remote Login** adds a second step: after the RDP login, Ubuntu’s own login screen (GDM) appears, and you sign in there with the **Linux** password.
-   Ports: Desktop Sharing **3390** and Remote Login **3389** when both are on.

### 6.2 Desktop Sharing (the real screen)

[Section titled “6.2 Desktop Sharing (the real screen)”](#62-desktop-sharing-the-real-screen)

On the Ubuntu laptop:

1.  Open **Settings → System → Remote Desktop → Desktop Sharing**.
2.  Turn on **Desktop Sharing** and **Remote Control** (Remote Control lets you use mouse and keyboard, not just watch).
3.  Under **Login Details**, set the username (`labuser` in this tutorial) and a password.

Verify (as `labuser`, in a local terminal or over SSH):

```
export XDG_RUNTIME_DIR=/run/user/$(id -u) DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/$(id -u)/bus   # only needed over SSHgrdctl status                         # RDP Status: enabled, View-only: no, Negotiate port: yes, Username/Password: (hidden)gsettings get org.gnome.desktop.remote-desktop.rdp screen-share-mode    # 'mirror-primary'systemctl --user is-active gnome-remote-desktop                          # activess -ltn | grep -E ':(3389|3390)\b'
```

Expected:

-   A TLS certificate at `~/.local/share/gnome-remote-desktop/certificates/rdp-tls.crt`.
-   Port 3390 once Remote Login holds 3389.

`grdctl status` keeps showing the _configured_ `Port: 3389`. The _actual_ port is in `ss` (`Negotiate port: yes` lets it move).

**If logins fail with `SEC_E_NO_CREDENTIALS`** in the log (Phase 10), the running daemon has no login loaded. Fix:

1.  Switch Desktop Sharing **off and on** and re-type the password under **Login Details**.
    
2.  Run `systemctl --user restart gnome-remote-desktop`.
    
3.  Check it’s loaded (prints set/empty only, never the value):
    
    ```
    grdctl status --show-credentials | sed -E 's/^(\s*(Username|Password):)\s+\S.*$/\1 <set>/'
    ```
    

`--show-credentials` goes **after** `status`; `grdctl --show-credentials status` only prints usage.

### 6.3 Remote Login (a separate session)

[Section titled “6.3 Remote Login (a separate session)”](#63-remote-login-a-separate-session)

On the Ubuntu laptop, open **Settings → System → Remote Desktop → Remote Login → Unlock** (admin password), switch **Remote Login** on, and fill **Login Details** (username `labuser` + password).

**Settings can display the Remote Login details without delivering them to the service.** The system daemon then keeps logging `[RDP] Credentials are not set, denying client`, and Windows shows error **0x904 / 0x7**. The reliable way is to store them with `grdctl --system` and restart.

In an SSH or local terminal on Ubuntu:

```
sudo grdctl --system rdp set-credentials        # prompts: "Username:" then "Password:" (nothing on the command line)sudo systemctl daemon-reload                     # clears "unit file ... changed on disk" warningssudo systemctl restart gnome-remote-desktopsudo grdctl --system status | grep -E 'Status|Port|Username'    # Status: enabled / Port: 3389 / Username: (hidden)
```

Notes:

-   `Init TPM credentials failed because No TPM device found, using GKeyFile as fallback` is **informational**. On a laptop without a TPM chip, the login is stored in a root-only key file under `/var/lib/gnome-remote-desktop`.
-   If you must pass the values as arguments, start the line with a **space**. Ubuntu’s default `HISTCONTROL=ignoreboth` then keeps it out of bash history:
    
    ```
     sudo grdctl --system rdp set-credentials labuser 'NEW_PASSWORD'
    ```
    
    Never type such a command in Windows PowerShell (PSReadLine saves history), and never put the password in a screenshot or chat. If that happens, set a new password.
-   Each successful connection is logged by the system daemon as `[RDP] Sending server redirection`, followed by `gnome-remote-desktop-handover.service` starting in the session. Read it with `journalctl -u gnome-remote-desktop` (no sudo needed for a member of `adm`).

### 6.4 What happens on connect

[Section titled “6.4 What happens on connect”](#64-what-happens-on-connect)

**Ubuntu Screen (Desktop Sharing):** RDP login → Ubuntu’s live screen, scaled to the window. If the screen is locked, you see the lock screen; unlock it with the Linux password.

**Ubuntu Login (Remote Login):**

1.  RDP login.
2.  Ubuntu’s login screen (GDM) appears inside the window.
3.  Sign in to your account (GDM shows its display name) with the **Linux** password.
4.  If you are logged in on the laptop, Ubuntu offers to **end that local session** and continue remotely. Accepting it also closes any open Ubuntu Screen window, because that local session is the one being shared.

Use Ubuntu Screen while you are logged in at the laptop, and Ubuntu Login when nobody is.

### 6.5 Windows connection files and desktop shortcuts

[Section titled “6.5 Windows connection files and desktop shortcuts”](#65-windows-connection-files-and-desktop-shortcuts)

Two RDP connection files hold the settings. Desktop shortcuts with an Ubuntu icon open them.

`C:\RemoteAccess\Ubuntu-Screen.rdp` (Desktop Sharing):

```
full address:s:192.168.50.21:3390username:s:labuserscreen mode id:i:1smart sizing:i:1dynamic resolution:i:0prompt for credentials on client:i:1authentication level:i:2
```

-   `smart sizing:i:1` scales Ubuntu’s 4K screen to fit any window size; it’s a mirror, so its resolution can’t change.
-   `username:s:labuser` stops Windows from pre-filling your Microsoft account.
-   `authentication level:i:2` warns on the self-signed certificate instead of refusing it.

`C:\RemoteAccess\Ubuntu-Login.rdp` (Remote Login):

```
full address:s:192.168.50.21:3389username:s:labuserscreen mode id:i:2use multimon:i:0dynamic resolution:i:1smart sizing:i:0desktopscalefactor:i:200prompt for credentials on client:i:1authentication level:i:2
```

-   `screen mode id:i:2` opens full screen on one monitor.
-   `dynamic resolution:i:1` makes Ubuntu re-fit its desktop whenever the window size changes.
-   `desktopscalefactor:i:200` asks Ubuntu to draw at 200 %, to match a Windows display at 200 %.

Without the size settings, the Remote Login window opens tiny (about 430×340), with content at 100 % scale. If text is still small with the settings above, set **Settings → Displays → Scale → 200 %** inside the remote session.

Create both files and shortcuts with the commands in Appendix B.

### 6.6 How Windows remembers these logins

[Section titled “6.6 How Windows remembers these logins”](#66-how-windows-remembers-these-logins)

Windows saves remembered RDP logins **per host, not per port**. The target is `TERMSRV/192.168.50.21`, the same for 3389 and 3390; a Kali laptop from the other tutorial would have its own entry, `TERMSRV/192.168.50.20`. So both Ubuntu shortcuts share **one** remembered login.

Give Desktop Sharing and Remote Login the **same username and password**, and tick **Remember me** once. Otherwise leave **Remember me** unticked, or each shortcut will overwrite the other’s saved password.

List or remove saved entries (target names only):

```
(cmdkey /list) | Select-String 'Target:.*TERMSRV'cmdkey /delete:TERMSRV/192.168.50.21
```

## Phase 7: Keep the Ubuntu laptop reachable

[Section titled “Phase 7: Keep the Ubuntu laptop reachable”](#phase-7-keep-the-ubuntu-laptop-reachable)

Remote access stops if the laptop suspends. Check these settings (as `labuser`):

```
gsettings get org.gnome.settings-daemon.plugins.power sleep-inactive-ac-type        # 'nothing'gsettings get org.gnome.settings-daemon.plugins.power sleep-inactive-battery-type   # 'nothing'gsettings get org.gnome.desktop.session idle-delay                                  # uint32 0 (screen never blanks)gsettings get org.gnome.desktop.screensaver lock-enabled                            # true (locks when blanked)
```

In the GUI: **Settings → Power → Automatic Suspend: Off**. If you change these, Desktop Sharing still works on a locked screen (you unlock it remotely). A suspended laptop needs to be woken locally.

Optional, for long jobs over SSH (`tmux` keeps them running if SSH drops):

```
ssh -t ubuntu-lab "sudo apt install -y tmux"
```

## Phase 8: Security boundaries

[Section titled “Phase 8: Security boundaries”](#phase-8-security-boundaries)

-   **LAN only.** Do not port-forward 22, 3389, or 3390 on the router. Use a VPN for outside access.
-   **Keys:** `ubuntu_lab` stays on Windows; Ubuntu holds one `authorized_keys` line (mode 600). Revoke Windows’ access by deleting that line.
-   **Host key:** stop if Ubuntu’s SSH fingerprint ever changes from the one you recorded in Phase 3.
-   **RDP passwords:**
    -   They are stored in your account’s keyring (Desktop Sharing) and in a root-only file (Remote Login).
    -   Use strong, unique values.
    -   Change them with **Settings** (Desktop Sharing) or `sudo grdctl --system rdp set-credentials` (Remote Login).
    -   Never share them in screenshots or chats; rotate them if that happens.
-   **Certificates:** both RDP modes use self-signed TLS certificates. Accept the prompt only on your home network, and tick “Don’t ask me again” for this host once verified.
-   **Hermes:**
    -   Profile `ubuntu-lab` acts as `labuser` (an administrator).
    -   Keep approvals on, and never store the sudo password.
    -   Keep `%LOCALAPPDATA%\hermes\profiles\ubuntu-lab\.env` private.
    -   The SSH backend copies some Hermes cache folders (documents, images, attachments) to Ubuntu during sessions; treat Ubuntu as trusted.

## Phase 9: Verify the complete workflow

[Section titled “Phase 9: Verify the complete workflow”](#phase-9-verify-the-complete-workflow)

```
# Network and portsTest-NetConnection 192.168.50.21 -Port 22      # TrueTest-NetConnection 192.168.50.21 -Port 3389    # True (Remote Login)Test-NetConnection 192.168.50.21 -Port 3390    # True (Desktop Sharing)
# SSH (no prompts)ssh -o BatchMode=yes ubuntu-lab "hostname && whoami && grep PRETTY_NAME /etc/os-release"
# Hermes Mode Ahermes -p ubuntu-lab config get terminal.backend      # sshhermes -p ubuntu-lab chat -Q -q "Use your terminal tool to run: hostname && whoami. Reply with only the output."
```

Then:

1.  **Ubuntu Screen:** while you are logged in at the laptop, double-click it and sign in as `labuser` with the RDP password. You see the live screen.
2.  **Ubuntu Login:** while you are logged out at the laptop (or accept “end session”), double-click it and sign in as `labuser` with the RDP password, then with the Linux password at GDM. You get a separate session.

## Phase 10: Troubleshooting

[Section titled “Phase 10: Troubleshooting”](#phase-10-troubleshooting)

### 10.1 Common errors, with cause and fix

[Section titled “10.1 Common errors, with cause and fix”](#101-common-errors-with-cause-and-fix)

| Symptom / exact message | Cause | Fix |
| --- | --- | --- |
| lab-ubuntu.local → 192.168.50.20; port 22 refused at that address | Router DHCP gave Ubuntu Kali’s static address (Kali off; the address not reserved in the router) | Give Ubuntu its own static address (Phase 1.3) |
| TcpTestSucceeded : False on port 22 / connection refused | Ubuntu Desktop has no SSH server by default | sudo apt install -y openssh-server && sudo systemctl enable --now ssh |
| Are you sure you want to continue connecting (yes/no/[fingerprint])? then Host key verification failed. while piping the key | The yes/no prompt read the piped key text | Pass the key inside the command (4.4), or record the host key first (4.2) |
| choose_kex: unsupported KEX method sntrup761x25519-sha512@openssh.com from ssh-keyscan | Windows OpenSSH 9.5 ssh-keyscan limitation | Capture the host key with ssh and a temporary known_hosts file (4.2) |
| ssh lab-Ubuntu: The authenticity of host 'lab-ubuntu (fe80::...%11)' can't be established | The hostname isn’t an alias; mDNS resolved it to IPv6 link-local | Add lab-ubuntu lab-Ubuntu to the Host line (4.3) |
| authorized_keys has the key twice | Install command run twice | awk '!seen[$0]++' dedupe (4.4) |
| ssh-keygen creates a key with a passphrase, or fails on -N '' | Windows PowerShell 5.1 drops the empty argument | Run the command in PowerShell 7.3 or later (0.1) |
| hermes profile create: “This profile has no API keys yet” | Expected for sign-in (OAuth) providers such as Nous Portal | Ignore; test with the one-shot chat (5.3) |
| Desktop: Desktop boot failed: Could not reach 192.168.50.20 over SSH | Desktop’s window backend is SSH → Kali, and Kali is off | Connection mode Local / This device (5.3) |
| Desktop shortcut “not visible” | Explorer has not refreshed | Click the desktop and press F5 (the file is in your Desktop folder) |
| Windows Security: “Your credentials did not work”, user pre-filled MicrosoftAccount\… | The Remote Desktop app defaults to your Windows account | More choices → Use a different account → labuser + RDP password; the username:s:labuser line pre-sets it |
| Desktop Sharing log: AcceptSecurityContext status SEC_E_NO_CREDENTIALS / client authentication failure | The running user daemon had no RDP login loaded | Re-enter Login Details (toggle off/on), systemctl --user restart gnome-remote-desktop (6.2) |
| grdctl --show-credentials status prints only Usage: ... | Option in the wrong position | grdctl status --show-credentials |
| 0x904, extended 0x7 “This computer can’t connect to the remote computer” on 3389 after enabling Remote Login | Remote Login took 3389 and Desktop Sharing moved to 3390; the shortcut still targeted 3389 | Ubuntu Screen → 192.168.50.21:3390 (6.5) |
| 0x904 / 0x7 on Ubuntu Login; system log [RDP] Credentials are not set, denying client | Settings showed Remote Login details but never stored them for the system daemon | sudo grdctl --system rdp set-credentials + restart (6.3) |
| Init TPM credentials failed because No TPM device found, using GKeyFile as fallback | No TPM chip; informational | None |
| Warning: The unit file ... of gnome-remote-desktop.service changed on disk. Run 'systemctl daemon-reload' | Unit changed when Remote Login was enabled | sudo systemctl daemon-reload && sudo systemctl restart gnome-remote-desktop |
| Ubuntu Login opens as a tiny window with small content | No size or scale in the RDP file | Full screen + dynamic resolution + desktopscalefactor:i:200 (6.5) |
| One shortcut’s saved password breaks the other | Windows saves RDP logins per host (TERMSRV/192.168.50.21), not per port | Same login for both modes, or don’t tick Remember me (6.6) |

### 10.2 General checks

[Section titled “10.2 General checks”](#102-general-checks)

| Symptom | Check |
| --- | --- |
| Can’t reach Ubuntu at all | Laptop on and awake? ping 192.168.50.21; nmcli -g ipv4.addresses connection show HomeWiFi |
| SSH works, Hermes fails | hermes -p ubuntu-lab config get terminal.backend; the profile’s .env (quoted key path); SSH penalty after failed logins |
| Hermes ran commands on the wrong machine | Selected profile: ubuntu-lab on gateway This device |
| Desktop Sharing shows the lock screen | Normal when locked; unlock with the Linux password |
| Ubuntu Screen fails, Ubuntu Login works (or vice versa) | ss -ltn \| grep -E ':(3389\|3390)' on Ubuntu; check which port each .rdp uses |

### 10.3 Where the logs are

[Section titled “10.3 Where the logs are”](#103-where-the-logs-are)

| Machine | Log | Command |
| --- | --- | --- |
| Ubuntu | Desktop Sharing (user daemon) | journalctl --user -u gnome-remote-desktop --since "-30 min" |
| Ubuntu | Remote Login (system daemon) | journalctl -u gnome-remote-desktop --since "-30 min" (no sudo for a member of adm) |
| Ubuntu | Handover / GDM | journalctl --since "-30 min" \| grep -iE 'handover\|gdm' |
| Ubuntu | SSH | journalctl -u ssh -n 50 --no-pager |
| Windows | RDP client | Event Viewer → Applications and Services Logs → Microsoft → Windows → TerminalServices-RDPClient → Operational |
| Windows | Hermes Desktop | %LOCALAPPDATA%\hermes\logs\desktop.log |

Filter the noisy FreeRDP lines when reading GNOME Remote Desktop logs:

```
journalctl -u gnome-remote-desktop --since "-30 min" --no-pager | grep -vE 'winpr|freerdp'journalctl --user -u gnome-remote-desktop --since "-30 min" --no-pager | grep -oE 'SEC_E_[A-Z_]+|Credentials are not set|Sending server redirection' | sort | uniq -c
```

Windows RDP client events from the last 30 minutes:

```
Get-WinEvent -FilterHashtable @{ LogName='Microsoft-Windows-TerminalServices-RDPClient/Operational'; StartTime=(Get-Date).AddMinutes(-30) } |  Sort-Object TimeCreated | Select-Object TimeCreated, Id, @{n='Msg';e={($_.Message -replace '\s+',' ').Substring(0,[Math]::Min(140,($_.Message -replace '\s+',' ').Length))}}
```

## Appendix A: Configuration reference

[Section titled “Appendix A: Configuration reference”](#appendix-a-configuration-reference)

| Where | What |
| --- | --- |
| Ubuntu NetworkManager HomeWiFi | ipv4.method manual, 192.168.50.21/24, gw/DNS 192.168.50.1, autoconnect yes |
| Ubuntu ~/.ssh/authorized_keys | 1 line: ssh-ed25519 … lab user@winpc ubuntu_lab; ~/.ssh 700, the file 600 |
| Ubuntu GNOME Remote Desktop (user) | RDP enabled, mirror-primary, view-only no, negotiate port yes → listening 3390, username labuser |
| Ubuntu GNOME Remote Desktop (system) | enabled, port 3389, username labuser, credentials in GKeyFile (no TPM) |
| Ubuntu power | suspend nothing (AC and battery), idle-delay 0 |
| Windows ~\.ssh\config | Host ubuntu-lab lab-ubuntu lab-Ubuntu 192.168.50.21 block (4.3) |
| Windows ~\.ssh\known_hosts | 192.168.50.21 ssh-ed25519 … (the fingerprint you recorded in Phase 3) |
| Windows Hermes profile ubuntu-lab | terminal.backend: ssh; .env: TERMINAL_SSH_HOST=192.168.50.21, USER=labuser, PORT=22, KEY="C:/Users/Lab User/.ssh/ubuntu_lab" |
| Windows RDP files | C:\RemoteAccess\Ubuntu-Screen.rdp (3390), C:\RemoteAccess\Ubuntu-Login.rdp (3389), contents in 6.5 |
| Windows shortcuts | Ubuntu Screen.lnk and Ubuntu Login.lnk in your Desktop folder → mstsc.exe "<rdp>", icon C:\RemoteAccess\ubuntu.ico |

## Appendix B: Create the RDP files and desktop shortcuts (Ubuntu icon)

[Section titled “Appendix B: Create the RDP files and desktop shortcuts (Ubuntu icon)”](#appendix-b-create-the-rdp-files-and-desktop-shortcuts-ubuntu-icon)

Any `.ico` file works for the icon. Download an Ubuntu icon from a source whose license lets you use it, and save it as `C:\RemoteAccess\ubuntu.ico`.

### B.1 With PowerShell (normal user)

[Section titled “B.1 With PowerShell (normal user)”](#b1-with-powershell-normal-user)

```
# 0. A folder for the RDP files and the iconNew-Item -ItemType Directory -Force 'C:\RemoteAccess' | Out-Null
# 1. RDP connection files (Unicode encoding, as mstsc writes them)@'full address:s:192.168.50.21:3390username:s:labuserscreen mode id:i:1smart sizing:i:1dynamic resolution:i:0prompt for credentials on client:i:1authentication level:i:2'@ | Set-Content 'C:\RemoteAccess\Ubuntu-Screen.rdp' -Encoding Unicode
@'full address:s:192.168.50.21:3389username:s:labuserscreen mode id:i:2use multimon:i:0dynamic resolution:i:1smart sizing:i:0desktopscalefactor:i:200prompt for credentials on client:i:1authentication level:i:2'@ | Set-Content 'C:\RemoteAccess\Ubuntu-Login.rdp' -Encoding Unicode
# 2. Desktop shortcuts with the Ubuntu icon$desk = [Environment]::GetFolderPath('Desktop'); $sh = New-Object -ComObject WScript.Shellforeach ($s in @(  @{ Name = 'Ubuntu Screen'; Rdp = 'C:\RemoteAccess\Ubuntu-Screen.rdp'; Desc = "Ubuntu laptop's real screen (GNOME Desktop Sharing, RDP port 3390)" },  @{ Name = 'Ubuntu Login';  Rdp = 'C:\RemoteAccess\Ubuntu-Login.rdp';  Desc = 'Ubuntu laptop: separate session (GNOME Remote Login, RDP port 3389)' })) {  $l = $sh.CreateShortcut("$desk\$($s.Name).lnk")  $l.TargetPath = "$env:SystemRoot\System32\mstsc.exe"; $l.Arguments = "`"$($s.Rdp)`""; $l.WorkingDirectory = 'C:\RemoteAccess'  $l.IconLocation = 'C:\RemoteAccess\ubuntu.ico,0'; $l.Description = $s.Desc; $l.Save()}
# 3. Make Explorer show them immediately (same as pressing F5 on the desktop)Add-Type -Namespace Shell32 -Name Notify -MemberDefinition '[DllImport("shell32.dll")] public static extern void SHChangeNotify(int e, uint f, System.IntPtr a, System.IntPtr b);'[Shell32.Notify]::SHChangeNotify(0x08000000, 0, [IntPtr]::Zero, [IntPtr]::Zero)
```

### B.2 By hand

[Section titled “B.2 By hand”](#b2-by-hand)

1.  Open **Remote Desktop Connection** (`mstsc`), then **Show Options**:
    -   **General:** Computer `192.168.50.21:3390`, User name `labuser`.
    -   **Save As** `C:\RemoteAccess\Ubuntu-Screen.rdp`.
    -   Smart sizing isn’t in this dialog. Open the saved file in Notepad and add the line `smart sizing:i:1`.
2.  Repeat for `192.168.50.21:3389` with **Display → Full screen**, saved as `C:\RemoteAccess\Ubuntu-Login.rdp`. Add the `dynamic resolution` and `desktopscalefactor` lines in Notepad.
3.  Right-click the desktop → **New → Shortcut** → location `C:\Windows\System32\mstsc.exe "C:\RemoteAccess\Ubuntu-Screen.rdp"` → name **Ubuntu Screen**.
4.  **Properties → Change Icon… → Browse** → `C:\RemoteAccess\ubuntu.ico`. Repeat for **Ubuntu Login** with `C:\RemoteAccess\Ubuntu-Login.rdp`.
5.  If the icons don’t appear, press **F5** on the desktop. If an icon stays stale, run `ie4uinit.exe -show`.

## Appendix C: Ubuntu vs Kali at a glance

[Section titled “Appendix C: Ubuntu vs Kali at a glance”](#appendix-c-ubuntu-vs-kali-at-a-glance)

| Item | Kali (Kali tutorial) | Ubuntu (this tutorial) |
| --- | --- | --- |
| Address | 192.168.50.20 static | 192.168.50.21 static |
| SSH alias / key | kali-lab / kali_lab | ubuntu-lab (+ lab-ubuntu, lab-Ubuntu) / ubuntu_lab |
| Shell | zsh (needs ~/.zshenv for Mode B) | bash |
| Hermes | Mode A (kali-lab) + Mode B (Kali Lab gateway) | Mode A only (ubuntu-lab) |
| Separate desktop | XRDP + Xfce on 3389, login with the Linux password; coexists with a local login (private D-Bus fix) | GNOME Remote Login on 3389, RDP password, then the Linux password at GDM; ends a local login if one exists |
| Real screen | x11vnc + TigerVNC via SSH tunnel (Open-KaliScreen.ps1) | GNOME Desktop Sharing on 3390 via mstsc |
| Scaling fix | x11vnc -scale fit (TigerVNC is DPI-unaware) | smart sizing (Screen); dynamic resolution + desktopscalefactor (Login) |
| Desktop shortcuts | Kali Screen | Ubuntu Screen, Ubuntu Login |

## Final checklist

[Section titled “Final checklist”](#final-checklist)

-    Ubuntu keeps its static address (`192.168.50.21`, connection `HomeWiFi`, method `manual`) after reboot; a Kali laptop keeps its own.
-    `ssh -o BatchMode=yes ubuntu-lab "hostname"` → `lab-Ubuntu` (also via `lab-Ubuntu` and the IP).
-    Ubuntu’s `authorized_keys` has exactly one `ubuntu_lab` line, mode 600.
-    `hermes -p ubuntu-lab config get terminal.backend` → `ssh`; the one-shot test returns `lab-Ubuntu` / `labuser`.
-    Hermes Desktop: gateway **This device** + profile **ubuntu-lab** answers from Ubuntu; connection mode Local if another SSH laptop is off.
-    Ubuntu ports: 22, **3389** (Remote Login), **3390** (Desktop Sharing) listening.
-    **Ubuntu Screen** (3390) shows the live screen. Login: `labuser` + RDP password.
-    **Ubuntu Login** (3389) shows GDM, then a separate session at full screen. Login: `labuser` + RDP password, then the Linux password.
-    Both RDP modes use the same login (or Remember me is off for one); no password appears in screenshots or chats.
-    Automatic suspend is off; nothing is port-forwarded on the router.

The setup is complete when three things work:

1.  `ssh ubuntu-lab` works without prompts.
2.  Profile `ubuntu-lab` returns `lab-Ubuntu` / `labuser`.
3.  Both **Ubuntu Screen** and **Ubuntu Login** connect with the RDP login.

## Official documentation

[Section titled “Official documentation”](#official-documentation)

-   [Ubuntu: Share your desktop remotely](https://ubuntu.com/desktop/docs/en/latest/how-to/share-your-desktop-remotely/): Desktop Sharing vs Remote Login, credentials, ports 3389/3390, firewall.
-   [Ubuntu: Access a remote desktop](https://ubuntu.com/desktop/docs/en/latest/how-to/access-a-remote-desktop/)
-   [GNOME Remote Desktop project](https://gitlab.gnome.org/GNOME/gnome-remote-desktop): `grdctl`, headless/system daemon.
-   [Launchpad bug 2141992](https://bugs.launchpad.net/bugs/2141992): a related Remote Login handover issue on 25.10 (GNOME 49), and the source of the `grdctl --system` workaround.
-   [Hermes Agent configuration: SSH backend](https://hermes-agent.nousresearch.com/docs/user-guide/configuration)
-   [Hermes Agent profiles](https://hermes-agent.nousresearch.com/docs/user-guide/profiles)
-   [Microsoft: Supported RDP properties](https://learn.microsoft.com/azure/virtual-desktop/rdp-properties): `smart sizing`, `dynamic resolution`, `desktopscalefactor`, `screen mode id`, `username`.
-   Companion tutorial: [Control a Kali Linux Laptop from Windows 11](/tutorials/kali-linux-remote-access-from-windows).