Metadata-Version: 2.4
Name: lab-rdp-dashboard
Version: 1.0.0
Summary: A browser gateway to lab Windows PCs: reserve a machine, sign in, and use its desktop in a browser tab.
Author: Naveen Daniel Kennedy
Keywords: rdp,remote desktop,lab,gateway,windows
Classifier: Development Status :: 4 - Beta
Classifier: Environment :: Web Environment
Classifier: Intended Audience :: System Administrators
Classifier: Operating System :: Microsoft :: Windows
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Topic :: System :: Systems Administration
Requires-Python: >=3.11
Description-Content-Type: text/markdown
Requires-Dist: fastapi<1,>=0.115
Requires-Dist: uvicorn<1,>=0.32
Requires-Dist: jinja2<4,>=3.1
Requires-Dist: python-multipart>=0.0.12
Requires-Dist: itsdangerous<3,>=2.2
Requires-Dist: pydantic-settings<3,>=2.6
Requires-Dist: sqlalchemy<3,>=2.0.36
Requires-Dist: alembic<2,>=1.14
Requires-Dist: ldap3<3,>=2.9.1
Requires-Dist: pywinrm<1,>=0.5
Requires-Dist: icmplib<4,>=3.0.4
Requires-Dist: apscheduler<4,>=3.10.4
Requires-Dist: cryptography>=43
Requires-Dist: aardwolf<0.3,>=0.2.17
Requires-Dist: pillow>=10.1
Requires-Dist: websockets>=13
Requires-Dist: pywin32>=308; sys_platform == "win32"

# Lab RDP Dashboard

The single, mandatory gateway to lab Windows PCs. Users pick a machine in the dashboard and click Connect. A browser
tab asks for their Windows user name and password, then shows that PC's desktop. Nothing is installed or configured
on laptops. While someone is connected, nobody else can connect to that machine. Each lab PC's firewall can be locked
to accept RDP only from this server, so there is no other way in.

It runs as one Python process on one Windows 10/11 Pro PC, with no Linux, Docker or IIS.

**Security note.** The Windows password passes through this server's memory for the length of the session. It is never
written to disk or logged, but use HTTPS and keep the server locked down.

## Install

Install Python 3.11 or newer for all users, then:

```powershell
python -m venv C:\labrdp
C:\labrdp\Scripts\pip install lab-rdp-dashboard
```

## Try it with no lab

```powershell
C:\labrdp\Scripts\labrdp demo
```

Open http://127.0.0.1:8443 and sign in as `admin` / `admin`. The lab PCs and firewalls are simulated, and it keeps its
data apart from a real setup. Click Connect and sign in with any user name and password. `wrong` as the password shows
the failure message.

## Test it on one real lab PC

Use any Windows PC that can reach a lab PC with Remote Desktop turned on.

```powershell
C:\labrdp\Scripts\labrdp init
C:\labrdp\Scripts\labrdp run
```

`init` asks you to choose an admin password, then prints the address to open. The certificate is self-signed, so your
browser warns once: choose Advanced, then Proceed. Other PCs may need port 8443 allowed through this PC's firewall.

Go to **Manage machines → Add machine**, enter the lab PC's IP address and name, then go to **Machines** and click
**Connect**. Sign in with a Windows account that can use that PC. If sign-in fails, the page says why and
`C:\ProgramData\LabRDP\data\labrdp.log` has the detail.

## Deploy for the whole lab

Settings live in `C:\ProgramData\LabRDP\.env`, and every option is described in the `env.example` that `init` points to.
Run these from an elevated PowerShell.

1. Give the server a fixed IP, and a DNS name for users to reach it by.
2. Run `labrdp init` if you haven't. In `.env` set `AUTH_MODE=ldap` and the `LDAP_*` settings, `RDP_DEFAULT_DOMAIN`,
   and point `TLS_CERTFILE` and `TLS_KEYFILE` at a corporate CA certificate.
3. To lock lab PCs to this server, set `ENFORCEMENT_BACKEND=winrm` and the `WINRM_*` settings, then store the password:
   ```powershell
   C:\labrdp\Scripts\labrdp set-winrm-password
   ```
4. Prepare each lab PC once. `labrdp lab-scripts` shows where the scripts are. Run this on the lab PC, by hand or GPO:
   ```powershell
   Set-ExecutionPolicy -Scope Process -ExecutionPolicy Bypass -Force
   .\lab-pc-enable-winrm.ps1 -ServiceAccount 'CORP\svc-labrdp' -ServerIp 10.20.0.5
   ```
5. Restrict the server's own ports with `server-firewall.ps1` from the same folder, then install the service:
   ```powershell
   C:\labrdp\Scripts\labrdp service install
   C:\labrdp\Scripts\labrdp service start
   ```
   It starts at boot and restarts on a crash.
6. Enroll the first machine. Confirm its firewall badge turns to **Locked**, that direct `mstsc` from a laptop times
   out, and that **Connect** shows the desktop.
7. Bulk-enroll the rest from **Manage machines → Import CSV**. The template is on that page.

A backup share must be writable by the service's account, which is the server's computer account under LocalSystem.

To upgrade: `pip install -U lab-rdp-dashboard`, then `labrdp init` to update the database, then restart the service.

## Things to watch on real hardware

The desktop in the browser has only run against a simulated PC so far.

- **NTLM.** The browser desktop signs in with NTLM. A domain that blocks NTLM for Remote Desktop would refuse it.
- **Colour and speed.** `RDP_COLOR_DEPTH=16` is the fastest. Try 24 or 32 if colours look poor.
- **Not included yet:** copy and paste between the laptop and the lab PC, sound, and the remote mouse pointer's
  changing shape. Browsers also keep a few shortcuts for themselves, such as Ctrl+W. Full screen captures more of them.

## Adding and removing machines

All of this is on the **Manage machines** page, for admins. The **Machines** page is only for finding a PC and connecting.

- The grid shows every detail of every PC. **Click a cell to edit it**: Enter saves and moves down, Tab moves across,
  Esc cancels. Each change is validated and audited. **Columns** chooses what's shown, and clicking a heading sorts.
- **Add machine** opens a side panel. Enter the IP address and click **Detect** to fill in the name, MAC and Windows
  version from the PC over WinRM. **Save and add another** keeps the panel open for the next PC.
- **Import CSV**, in the same panel, enrolls a whole lab from one file. It's all or nothing, and it lists every bad row.
  **Export CSV** writes the same columns.
- Tick rows to act on many at once: change a field, check the firewall, push the rule, retire, put back, export or delete.
- **Delete** is the bin icon on each row. It's refused while a machine is in use.
- Deleting offers to **restore normal Remote Desktop** on the PC, so it isn't left locked to a server that has
  forgotten it. If the PC can't be reached it's kept, and you can untick the option to delete it anyway.
- Session history and the audit log survive a delete. **Retire** is the reversible alternative: it hides the
  machine and keeps its record.

## Behaviour worth knowing

- **Who holds a machine.** It's held from Connect until release, shown as *Reserved* until the user signs in.
- **After a dropped connection** the machine stays held for a 2-minute reconnect grace. Disconnecting or signing out
  of Windows frees it at once.
- **Signing in.** Three failed attempts release the machine, so a mistyped password can't lock the Windows account.
  A closed sign-in page releases the machine at once. Passwords are never stored, logged or audited.
- **The `mstsc` fallback.** With `RELAY_ENABLED=true`, the sign-in page links to a route for anyone who prefers the
  Remote Desktop app. It needs port 3389 free on the server.
- **Firewall compliance** covers more than our own rule: Windows Firewall being off on a profile, or any other
  enabled rule that admits RDP, also counts as drift. Auto-repair restores our rule and the built-in rules' disabled
  state, but it won't remove other rules. Those stay red until an admin deals with them.
- **Run one process only.** The machine locks live in memory, so don't add Uvicorn workers.
- **If the server is down**, `lab-pc-remove-dashboard-rule.ps1` on a lab PC restores normal RDP.

## Development

```powershell
python -m venv .venv
.venv\Scripts\pip install -r requirements-dev.txt
.venv\Scripts\python -m pytest
.venv\Scripts\labrdp demo
```

To release: raise `__version__` in `labrdp/__init__.py`, delete `dist`, then run `python -m build` and `python -m twine upload dist\*`.
