Security¶
LabLink protects the admin UI, client registration, and browser desktop sessions with different credentials. The deployment config and state can contain secrets; treat them as sensitive files. Choose an HTTPS exposure mode before inviting participants over the internet.
Change Default Passwords¶
The allocator reads app.admin_user, app.admin_password, and db.password
from its deployed config.yaml. It does not read ADMIN_PASSWORD or
DB_PASSWORD environment variables or fetch them from AWS Secrets Manager.
The handling depends on how you deploy:
- CLI on AWS:
lablink deployprompts for admin credentials on every run. It saves them in the deployment working copy under~/.lablink/deploy/, never in~/.lablink/config.yaml. It does not prompt for the database password: set a strongdb.passwordin~/.lablink/config.yamlyourself before deploying. - CLI with
provider: manual:deployuses values from the config or a previous rendered deployment first, then prompts if needed. It saves the result under~/.lablink/compose/<deployment_name>/config.yaml. - Template repository: keep
PLACEHOLDER_ADMIN_PASSWORDandPLACEHOLDER_DB_PASSWORDin the committed config.scripts/setup.shcreates GitHub repository secretsADMIN_PASSWORDandDB_PASSWORD; the deployment workflow substitutes them in its working copy.
Do not commit a config file containing real passwords. Use a strong unique admin password for any publicly reachable allocator. The validator rejects common weak passwords and passwords shorter than 12 characters for a Tailscale Funnel deployment.
Database Password¶
Set db.password through the deployment path above. PostgreSQL 17 runs
inside the allocator container, and the standard deployments do not publish
port 5432 to the host or the internet. The database password still protects
local and in-container access; replace the CLI default or template placeholder
before it reaches a running deployment.
Machine-to-Machine Authentication¶
- The allocator uses admin HTTP Basic authentication for admin pages and management APIs. Participant requests use their signed browser session, not the admin password.
- A BYO client registers with a deployment-wide register token. The allocator stores an argon2 hash of that token in PostgreSQL. The plaintext token appears in the allocator log so the CLI can pick it up; protect log access and avoid pasting the token into public channels.
- Each registered client receives its own secret. The allocator stores its argon2 hash and verifies it on client telemetry endpoints, including heartbeat, GPU health, session metrics, and overlay-hostname updates.
- The allocator also mints a separate
AGENT_TOKENfor its control calls to client agents. The client agent checks the bearer value with ordinary string equality, so this path does not claim constant-time comparison. The token is generated per allocator process. A manual-provider client container can outlive an allocator restart and retain an older token until it is re-registered or restarted with updated credentials.
Browser Desktop Sessions¶
On assignment, the allocator rotates the client's KasmVNC password and
creates a session ID and browser token. It signs the lablink_session cookie
with HMAC-SHA256; the cookie is HttpOnly and SameSite=Strict, with the
Secure flag when the request arrived over HTTPS. The browser receives a
303 redirect to /desktop.
For allocator-proxied desktops, nginx calls Flask's auth_request endpoint
before upgrading /proxy/<token> to a WebSocket. The signed cookie must
resolve to the VM named by the URL token and the VM must be running. nginx
then sends the rotated KasmVNC credential to the client; the browser does not
receive it. Reverse-tunnel attachment at /tun-* has its own bearer-token
auth_request check.
An admin can reserve a VM for peek or connect without giving it to a student. The allocator releases reservations older than 30 minutes in a background sweep, so closing a browser tab does not hold the seat indefinitely.
Network Security¶
The template's allocator security group allows TCP 80, 443, 5000, and 22
from 0.0.0.0/0. The application may bind port 5000 to loopback under an
HTTPS configuration, but the security-group rule itself is broad. The
client VM security group allows SSH (22) from the internet and KasmVNC
(6080) and its agent (7070) only from the allocator security group. Neither
security group exposes PostgreSQL 5432.
Review those rules for your deployment before making it public. OpenTofu resources are in the template repository and the allocator's bundled client OpenTofu files. For a manual-provider deployment, use the connectivity and exposure modes to control which network paths are reachable.
HTTP-Only Deployments (ssl.provider: "none")¶
HTTP sends the admin Basic-auth header, session cookie, bearer tokens, and
desktop traffic without transport encryption. Use HTTPS for public
deployments. The AWS host uses Caddy for letsencrypt, cloudflare, and
none; ACM terminates TLS at an ALB. A Cloudflare Tunnel terminates TLS at
Cloudflare's edge, so Cloudflare can read traffic there. Tailscale Funnel
offers a different public exposure path; see
Tailscale & Cloudflare Setup.
An HTTP viewer also cannot use browser WebCodecs H.264 streaming on an
insecure origin, so it falls back to JPEG/WebP. lablink doctor warns about
this when ssl.provider: none is selected.
GitHub Actions and State¶
The template's scripts/setup.sh creates a GitHub OIDC provider and the
github-actions-lablink IAM role, then stores the role ARN and region as
repository secrets. The trust policy is scoped to the configured repository
and the sts.amazonaws.com audience. See
AWS Setup for the
current managed-policy list.
OpenTofu state can contain sensitive values, including generated SSH keys.
The template uses an S3 backend and a DynamoDB lock table. setup.sh enables
S3 versioning on a new state bucket; it does not explicitly set a bucket
encryption policy or public-access block. Apply your organization's storage
controls and restrict access to the state bucket and workflow artifacts.
SSH Access¶
SSH Key Management¶
The deployment OpenTofu state contains a key for the allocator. On the
template path, the deploy workflow uploads it as a one-day artifact; a local
OpenTofu run can use tofu output -raw private_key_pem from the initialized
lablink-infrastructure/ directory. Restrict file
permissions before using that key.
Client VMs use a different keypair generated by the allocator's own OpenTofu
workspace. Its output is lablink_private_key_pem inside the allocator
container. The deployment key does not open client VMs. See
Troubleshooting for access steps and use the allocator
logs or lablink logs to diagnose a client before reaching for SSH.