Skip to content

feat(dgw): encrypt in-memory credentials at rest with ChaCha20-Poly1305#1689

Merged
Benoît Cortier (CBenoit) merged 17 commits intomasterfrom
DGW-326
Mar 27, 2026
Merged

feat(dgw): encrypt in-memory credentials at rest with ChaCha20-Poly1305#1689
Benoît Cortier (CBenoit) merged 17 commits intomasterfrom
DGW-326

Conversation

@CBenoit
Copy link
Copy Markdown
Member

@CBenoit Benoît Cortier (CBenoit) commented Feb 26, 2026

Add ChaCha20-Poly1305 encryption for credentials stored in the credential
store. Passwords are encrypted at rest with a randomly generated 256-bit
master key held in a protected page.

The page protection hardening is performed using the best available OS
hardening in a best-effort basis:

  • Windows: VirtualLock, guard pages (PAGE_NOACCESS), PAGE_READONLY after
    write, WerRegisterExcludedMemoryBlock for WER crash report exclusion.
  • Linux: mlock, guard pages (PROT_NONE), PROT_READ after write,
    madvise(MADV_DONTDUMP) for core dump exclusion.
  • All: zeroize-before-free on drop; plain heap fallback with
    zeroize-on-drop on unsupported platforms.

In concrete terms: this protects users from leaking important secrets in
the event of a memory dump captured for debugging purposes.

Issue: DGW-326

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Adds in-memory encryption for credentials stored by Devolutions Gateway to reduce plaintext exposure in dumps/swap, aligning with DGW-326.

Changes:

  • Introduces ChaCha20-Poly1305 encryption for stored passwords with a lazily initialized process-wide master key.
  • Updates credential ingestion to accept cleartext (API-only) types and encrypt on insert; decrypts on-demand for CredSSP flows.
  • Switches various password-like fields to secrecy::SecretString and updates serialization/usage accordingly.

Reviewed changes

Copilot reviewed 8 out of 9 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
devolutions-gateway/src/rdp_proxy.rs Decrypts stored credentials on-demand before CredSSP; updates Kerberos injection to use ExposeSecret.
devolutions-gateway/src/rd_clean_path.rs Updates Kerberos credential injection to pass password via ExposeSecret.
devolutions-gateway/src/credential/mod.rs Adds encrypted vs cleartext credential types; encrypts on insert and provides decrypt_password().
devolutions-gateway/src/credential/crypto.rs New module implementing ChaCha20-Poly1305 encryption and master-key handling (mlock/mprotect via secrets).
devolutions-gateway/src/config.rs Replaces several password fields with SecretString and adjusts (de)serialization and default checks.
devolutions-gateway/src/api/webapp.rs Uses ExposeSecret when verifying stored password hashes.
devolutions-gateway/src/api/preflight.rs Updates provisioning DTO to cleartext mapping type; changes error mapping for insert failures.
devolutions-gateway/Cargo.toml Adds crypto/secret-handling dependencies (chacha20poly1305, secrets, secrecy, rand).
Cargo.lock Locks new transitive dependencies for added crypto/secret crates.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@CBenoit
Copy link
Copy Markdown
Member Author

Copilot open a new pull request to apply changes based on the comments in this thread

We accept the panic in the LazyLock though. It’s not really expected to fail under normal conditions. Put the comment about libsodium in the README.md instead.

Copy link
Copy Markdown
Contributor

Copilot AI commented Feb 26, 2026

Benoît Cortier (@CBenoit) I've opened a new pull request, #1690, to work on those changes. Once the pull request is ready, I'll request review from you.

@CBenoit
Copy link
Copy Markdown
Member Author

Copilot Please make secrets (libsodium) an optional dependency with a feature flag. When secrets is not available, use secrecy for similar features (minus mlock). Ensure secrets is enabled for production builds. Log a warning on startup when a release build does not include the mlock security with libsodium. Ensure we build with secrets in the CI production builds.

Copy link
Copy Markdown
Contributor

Copilot AI commented Feb 26, 2026

Benoît Cortier (@CBenoit) I've opened a new pull request, #1691, to work on those changes. Once the pull request is ready, I'll request review from you.

Copy link
Copy Markdown
Contributor

Copilot AI commented Feb 26, 2026

Benoît Cortier (@CBenoit) I've opened a new pull request, #1692, to work on those changes. Once the pull request is ready, I'll request review from you.

Copy link
Copy Markdown
Contributor

Copilot AI commented Feb 26, 2026

Benoît Cortier (@CBenoit) I've opened a new pull request, #1693, to work on those changes. Once the pull request is ready, I'll request review from you.

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.

Looks good to me 👍

@CBenoit Benoît Cortier (CBenoit) changed the title feat(dgw): encrypt in-memory credentials feat(dgw): encrypt in-memory credentials with optional mlock protection Feb 27, 2026
@CBenoit Benoît Cortier (CBenoit) enabled auto-merge (squash) March 19, 2026 04:44
@CBenoit
Copy link
Copy Markdown
Member Author

I updated the CI so we get libsodium support for the ARM64 builds

@mamoreau-devolutions
Copy link
Copy Markdown
Contributor

We're going to use libsodium now, a C dependency? I see this pull request is already weeks in the making, so I trust you've already handled the additional complexity that comes with it for cross-compilation. There was really no other way?

Add ChaCha20-Poly1305 encryption for credentials stored in the
credential store. Passwords are encrypted at rest with a randomly
generated 256-bit master key that is zeroized on drop.

When built with the `mlock` cargo feature, the master key is
additionally protected via libsodium's mlock/mprotect facilities,
preventing exposure in memory dumps or swap. A startup warning is
emitted in release builds when mlock is not enabled.

Issue: DGW-326
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 17 out of 18 changed files in this pull request and generated 3 comments.

Comments suppressed due to low confidence (1)

devolutions-gateway/src/credential/mod.rs:76

  • #[derive(Deserialize)] is used in this module but serde::Deserialize is no longer imported (the previous serde::{de, ser} import was removed). This will fail to compile unless you either import serde::Deserialize or qualify the derive as serde::Deserialize.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 23 out of 24 changed files in this pull request and generated 3 comments.

Comments suppressed due to low confidence (1)

devolutions-gateway/src/credential/mod.rs:77

  • #[derive(Deserialize)] is used here, but this module has no use serde::Deserialize; (nor use serde::{Deserialize, Serialize};). Unless Deserialize is being referenced with a full path, this will fail to compile with “cannot find derive macro Deserialize in this scope”. Import the derive macros (e.g., use serde::Deserialize;) or switch the derives to fully-qualified serde::Deserialize/serde::Serialize.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 22 out of 23 changed files in this pull request and generated 3 comments.

Comments suppressed due to low confidence (1)

devolutions-gateway/src/credential/mod.rs:39

  • InsertError implements std::error::Error but doesn’t expose the wrapped anyhow::Error as a source, which can drop error-chain information for callers using source()/Error::cause-style introspection. Consider implementing source() (or using thiserror) so the underlying error is preserved.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 22 out of 23 changed files in this pull request and generated 2 comments.

Comments suppressed due to low confidence (1)

devolutions-gateway/src/credential/mod.rs:77

  • #[derive(Deserialize)] is used in this module, but serde::Deserialize is not imported into scope. This will not compile because derive macros require the trait name to resolve (e.g., use serde::Deserialize; or use serde::{Deserialize, Serialize};).

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@CBenoit Benoît Cortier (CBenoit) changed the title feat(dgw): encrypt in-memory credentials with optional mlock protection feat(dgw): encrypt in-memory credentials with ChaCha20-Poly1305 Mar 27, 2026
@CBenoit Benoît Cortier (CBenoit) changed the title feat(dgw): encrypt in-memory credentials with ChaCha20-Poly1305 feat(dgw): encrypt in-memory credentials at rest with ChaCha20-Poly1305 Mar 27, 2026
@CBenoit Benoît Cortier (CBenoit) enabled auto-merge (squash) March 27, 2026 02:51
@CBenoit Benoît Cortier (CBenoit) merged commit 8677514 into master Mar 27, 2026
41 checks passed
@CBenoit Benoît Cortier (CBenoit) deleted the DGW-326 branch March 27, 2026 02:55
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.

5 participants