Finish secure agent connection (cert pinning discoverability + vendored sync + MSI source) - #36
Merged
Merged
Conversation
…screen)
The server image bundles agent/ (Dockerfile: COPY agent /agent) and serves it via
/api/orgs/{org}/agent.zip, so the vendored copy must match the canonical agent in
mischa323/leuffen-rmm-agent. It had drifted: the vendored agent lacked TLS cert
pinning and the per-device-secret client, and screen.py lacked the login/lock
screen capture. This makes vendored == canonical so zip-installed agents get the
same secure-connection code as MSI-installed ones.
Files synced verbatim from leuffen-rmm-agent: agent.py, handlers.py, inventory.py,
screen.py, updater.py (Dockerfile, netscan.py, requirements.txt, tray.py already
matched).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Cert pinning shipped agent-side (RMM_SERVER_FINGERPRINT / server_fingerprint) but
the server never surfaced its own fingerprint, so admins had no way to know what to
pin. Add:
- tls.cert_fingerprint(): SHA-256 of the leaf cert DER — exactly what the agent
hashes from getpeercert(binary_form=True). Decodes PEM->DER via base64 (no extra
deps). Returns None when the cert isn't readable (e.g. proxy mode).
- GET /api/server-fingerprint (global admin): {fingerprint, tls_mode}.
- run.py logs the fingerprint on startup (non-proxy modes).
- Settings -> Security: read-only fingerprint card with copy button and pinning
instructions (lazy-loaded like the logs tab).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The Windows agent build moved to mischa323/leuffen-rmm-agent (which publishes the v2.x MSIs with the secure-connection code), but MSI_URL/GH_REPO still defaulted to Mischa323/Leuffenrrm — whose latest release is a stale v1.1.x agent. Unless prod set RMM_MSI_URL/RMM_GH_REPO, /install.msi and the version badge pointed at the wrong repo. Point the defaults at leuffen-rmm-agent (asset: leuffen-rmm-agent.msi, confirmed on v2.2.10). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…out) README: new "Secure connection" section covering cert pinning (how to obtain and pin the fingerprint), the per-device secret, the env-var table (SESSION_SECRET, RMM_SERVER_FINGERPRINT, RMM_REQUIRE_DEVICE_SECRET), and a safe rollout order for enabling enforcement. CHANGELOG: Unreleased entry. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Completes the secure-connection work that was started but never landed. The core
logic shipped earlier — agent cert pinning + per-device-secret client
(
leuffen-rmm-agent) and server-side device-secret issue/validate (PR #35) — butseveral loose ends meant it wasn't actually usable or consistent end to end. This
ties them off.
Pairs with leuffen-rmm-agent#4 (canonical agent gains config-file fingerprint);
merge that first so the vendored copy here stays in step.
What was unfinished
RMM_SERVER_FINGERPRINTbut the server never exposed its own fingerprint, so an admin had no way to know
what to pin.
agent/(COPY agent /agent) and serves it viaagent.zip, but that copy lacked cert pinning, thedevice-secret client, and the login/lock-screen capture — so zip-installed
agents differed from MSI ones. (The old
claude/sync-vendored-agentbranch onlyhalf-synced it and was never merged — this supersedes it.)
RMM_MSI_URL/RMM_GH_REPOstilldefaulted to this repo (stale v1.1.x agent) after the build moved to
leuffen-rmm-agent(v2.x with the secure code).SESSION_SECRET/RMM_SERVER_FINGERPRINT/RMM_REQUIRE_DEVICE_SECRETwere in the docs.Changes
tls.cert_fingerprint()— SHA-256 of the leaf cert DER (PEM→DER via base64,no new deps); matches what the agent hashes from
getpeercert(binary_form=True).Verified equal to
cryptography'sfingerprint(SHA256)and toopenssl.GET /api/server-fingerprint(global admin) →{fingerprint, tls_mode};null in proxy mode.
pinning instructions (lazy-loaded like the logs tab).
inventory.py, screen.py, updater.py).
Mischa323/leuffen-rmm-agent(assetleuffen-rmm-agent.msi, confirmed on v2.2.10).order) and CHANGELOG.
Verification
py_compileon all server + vendored-agent modules;node --checkonsettings.js.
cryptographyandopenssl(exact match;agent-side colon/upper normalisation round-trips).
Operational follow-ups (not code)
leuffen-rmm-agentso the MSI carries agent#4.RMM_REQUIRE_DEVICE_SECRET=1to enforce the device secret.claude/sync-vendored-agentbranch can be deleted (superseded here).🤖 Generated with Claude Code