Skip to content

[PSU-1337] Add PSU agent device enrollment - #2066

Open
Adam Driscoll (adamdriscoll) wants to merge 2 commits into
masterfrom
adamdriscoll-psu-agent-device-enrollment
Open

Adam Driscoll (adamdriscoll) wants to merge 2 commits into
masterfrom
adamdriscoll-psu-agent-device-enrollment

Conversation

@adamdriscoll

@adamdriscoll Adam Driscoll (adamdriscoll) commented Oct 9, 2026 •

Copy link
Copy Markdown
Contributor

Adds opt-in PowerShell Universal gRPC device enrollment with pinned-root server-proof verification and locally generated P-256 certificate requests. Protected per-server state and fresh signed Device assertions support tokenless restart and certificate renewal, with CLI commands and persistent-volume Docker configuration. Legacy Bearer authentication is unchanged; device mode never automatically downgrades or re-enrolls.

Companion to Devolutions/powershell-universal#1307. Requires Devolutions/powershell-universal#1482, stacked on that PR, which includes the actual issuing chain in the signed bootstrap proof and prevents reuse of disposed cryptographic providers.

Validation passed: formatting, workspace Clippy with -D warnings, Windows build, 13 new mock integration tests, and 11 legacy connector tests. The final agent commit and binary passed real-PSU pinned bootstrap, enrollment, Device connection, tokenless restart, renewal, renewed connection, and licensed PowerShell job execution against PSU commit 49ed14c74a63144cac376047b68d24e2d6f113de; the bounded enrollment token remained at one use of one. Full workspace tests are not green: three unchanged package-broker symlink tests fail with Windows OS error 1314 because the required privilege is unavailable.

QA and replay instructions: package/AgentWindowsManaged/README.md (standalone Windows configuration and lifecycle smoke test) and package/AgentLinux/README.md (exact-source container build, device settings, and persistent volume).

Issue: PSU-1337

Allow explicit pinned-root enrollment with durable device credentials and
fresh signed control-stream proofs. Preserve legacy app-token and
PSU-managed container authentication without downgrade or re-enrollment.

Add approval retries, certificate renewal, isolated smoke commands, and
mocked enrollment lifecycle coverage.

Issue: PSU-1337

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot AI balanced review requested due to automatic review settings October 9, 2026 14:38
@adamdriscoll

Copy link
Copy Markdown
Contributor Author

Implementation notes:

  • Extends the existing devolutions.psu.agent.poc.v1 connector, not the separate generic agent-identity proposal. Renew returns the same EnrollResponse as enrollment.
  • Device mode verifies the out-of-band root fingerprint, full signed serverAuth chain, ES256 proof, nonce, audience, and lifetime before sending the enrollment token. Normal HTTPS validation remains enabled; local h2c proofs do not provide encryption or active-relay resistance.
  • A locally generated P-256 key signs DER CSRs and fresh one-minute Device assertions with UUID subject, unpredictable single-use jti, leaf x5c, and JOSE signature encoding. Registration uses the server-issued UUID.
  • Restricted per-server state stores key, certificate, chain, pin, hardware identifier, UUID, and expiry together. Atomic replacement and an exclusive process lock prevent partial credentials and concurrent access. Pending approval retains credentials; ambiguous enrollment responses require operator reconciliation rather than another token use.
  • Renewal occurs ahead of expiry and before an expired identity reconnects. Failed renewal preserves previous state. Revocation/reset never triggers automatic re-enrollment or Bearer fallback. Legacy and PSU-managed container authentication remain opt-out defaults.
  • The psu-device enroll, renew, and status commands support isolated foreground smoke tests without installing a service. The Docker entrypoint supports explicit enrollment settings and a private persistent identity volume.

Validation includes 13 mock lifecycle/security tests, 11 existing connector tests, workspace Clippy with warnings denied, formatting, the Windows build, and isolated container-entrypoint configuration checks. The final executable also passed actual PSU enrollment, tokenless restart, forced renewal, reconnect, and a licensed PowerShell job while consuming the bounded token exactly once. Three unchanged package-broker symlink tests prevent a fully green workspace test run on this Windows host because OS error 1314 indicates missing privileges.

Note

Human-tuned, LLM-assisted content.

@github-actions

github-actions Bot commented Oct 9, 2026

Copy link
Copy Markdown

Let maintainers know that an action is required on their side

  • Add the label release-required Please cut a new release (Devolutions Gateway, Devolutions Agent, Jetsocat, PowerShell module) when you request a maintainer to cut a new release (Devolutions Gateway, Devolutions Agent, Jetsocat, PowerShell module)

  • Add the label release-blocker Follow-up is required before cutting a new release if a follow-up is required before cutting a new release

  • Add the label publish-required Please publish libraries (`Devolutions.Gateway.Utils`, OpenAPI clients, etc) when you request a maintainer to publish libraries (Devolutions.Gateway.Utils, OpenAPI clients, etc.)

  • Add the label publish-blocker Follow-up is required before publishing libraries if a follow-up is required before publishing libraries

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Windows silent upgrades currently reject valid device-enrollment configurations because the MSI still requires an app token.

1 open finding
What changed in this PR

Adds opt-in PSU device enrollment with certificate-based authentication, renewal, persisted identity state, CLI support, and container configuration.

Changes:

  • Implements pinned-root enrollment and signed device authentication.
  • Adds enrollment lifecycle tests and CLI commands.
  • Documents Windows and Linux setup and recovery workflows.
File Description
Cargo.lock Locks new cryptography and test dependencies.
crates/​devolutions-agent-shared/​src/​lib.rs Exports restricted filesystem helpers.
crates/​devolutions-agent-shared/​src/​restricted_file.rs Adds protected directories and atomic writes.
devolutions-agent/​Cargo.toml Adds certificate-verification dependencies.
devolutions-agent/​docker/​devolutions-agent-entrypoint.sh Generates device-mode container configuration.
devolutions-agent/​proto/​psu_agent.proto Defines enrollment and renewal RPCs.
devolutions-agent/​src/​config.rs Adds device-enrollment configuration.
devolutions-agent/​src/​main.rs Adds enrollment, renewal, and status commands.
devolutions-agent/​src/​psu_agent/​device.rs Implements device identity lifecycle and validation.
devolutions-agent/​src/​psu_agent/​mod.rs Integrates device authentication and renewal.
package/​AgentLinux/​README.md Documents container enrollment.
package/​AgentWindowsManaged/​README.md Documents standalone Windows testing.
testsuite/​Cargo.toml Adds integration-test dependencies.
testsuite/​tests/​main.rs Registers the new test module.
testsuite/​tests/​psu_device.rs Tests enrollment, renewal, recovery, and validation.

🧠 Review effort: Balanced


💡 Configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment on lines +793 to +794
#[serde(default, skip_serializing_if = "Option::is_none")]
pub device_enrollment: Option<PsuDeviceEnrollmentConf>,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Implemented in 2a2a0e8: silent-upgrade preservation now accepts a non-empty ServerUrl and DeviceEnrollment.RootThumbprint when AppToken is absent, with tests for both authentication modes.

Note

Human-tuned, LLM-assisted content.

Co-authored-by: adamdriscoll <1256531+adamdriscoll@users.noreply.github.com>
@CBenoit

Copy link
Copy Markdown
Member

Thank you Adam

I’m worried this may be overlapping a lot with the Agent Identity enrollment system I am working on, we may need to align before merging this

@adamdriscoll

Copy link
Copy Markdown
Contributor Author

Benoît Cortier (@CBenoit) That's fine. No need to bring this in if you already have something in flight. I was just looking at this PR from the PSU side and this is what copilot had to do to get it working over here.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

4 participants