Skip to content

feat: verify vault cluster identity - #33

Open
Raven-182 wants to merge 3 commits into
openstack-charmers:masterfrom
Raven-182:vault-cluster-identity
Open

Raven-182 wants to merge 3 commits into
openstack-charmers:masterfrom
Raven-182:vault-cluster-identity

Conversation

@Raven-182

@Raven-182 Raven-182 commented Sep 27, 2026 •

Copy link
Copy Markdown
Contributor

Summary

After a successful AppRole login, Vaultlocker reads the cluster ID. On the first run, it saves the ID beside the configuration file. On later runs, it compares the current ID with the saved one.

If the IDs differ, encrypt and enroll stop before changing a device or storing a key. decrypt logs a warning and tries to unlock using the vault connection if it finds a key. This also applies to boot-time unlocking.

Why

A Vault URL can change or point to a different cluster. New device keys should not be stored there while existing keys may still be in the original cluster.

Decrypting uses an existing key. If that key is available and unlocks the device, a cluster ID change should not prevent boot-time unlocking.

Changes

  • Authenticate to vault, then read and validate the cluster ID from Vault’s seal status.
  • Save one cluster ID per configuration file as <config-path>.cluster-id.
  • Stop encrypt and enroll when the cluster ID differs from the saved ID.
  • Warn and attempt decrypt on a mismatch.
  • Add unit and Vault-backed functional tests for pinning and mismatches.
  • Configure coverage reports to exclude tests and require full coverage of production code.

@Raven-182
Raven-182 force-pushed the vault-cluster-identity branch 2 times, most recently from 0cf28e3 to 57a3bf7 Compare September 29, 2026 15:03
@Raven-182
Raven-182 requested review from LucasAPayne and hmlanigan and removed request for LucasAPayne September 29, 2026 15:22
@Raven-182
Raven-182 force-pushed the vault-cluster-identity branch from 57a3bf7 to fc3efac Compare September 30, 2026 16:00
@Raven-182
Raven-182 marked this pull request as ready for review October 1, 2026 18:08
Read the cluster ID from Vault's seal status and reject missing, empty, non-string, or non-object responses with an error.

Assisted-by: OpenCode (DeepSeek v4.1 Flash)
Co-authored-by: Raven Kaur <raven.kaur@canonical.com>
Co-authored-by: Lucas Payne <lucas.payne@canonical.com>
Co-authored-by: Heather Lanigan <heather.lanigan@canonical.com>
Signed-off-by: Billy Olsen <billy.olsen@canonical.com>
Signed-off-by: Raven Kaur <raven.kaur@canonical.com>
@Raven-182
Raven-182 force-pushed the vault-cluster-identity branch 2 times, most recently from 13e2808 to 2b31f00 Compare October 1, 2026 18:36
Comment thread vaultlocker/shell.py
Comment on lines +694 to +700
_login_to_vault(client, config)
try:
_verify_cluster_identity(client, args.config)
except exceptions.ClusterIdentityMismatchError as mismatch_error:
if fail_on_cluster_mismatch:
raise
logger.warning('Cluster identity mismatch: %s', mismatch_error)

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.

suggestion: According to the hvac docs, read_seal_status() reads from an unauthenticated endpoint, so the login should not be necessary since that's what _verify_cluster_identity() calls. If login is needed for other actions, it can be moved after verifying the cluster's identity.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

So on the first run we create the cluster id pin. If we verify the cluster before authenticating, we could end up pinning a vault that we were never able to authenticate to. If login fails, we should not pin a Vault that these credentials could not access. I will add a comment to clarify this order.

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.

Thanks for clarifying

Comment thread vaultlocker/exceptions.py
Comment on lines +121 to +128
class ClusterIdentityMismatchError(ClusterIdentityError):
"""The connected Vault cluster does not match the saved cluster."""

def __init__(self, expected, actual):
super().__init__(
"Vault cluster identity mismatch: pinned cluster_id={}, "
"observed cluster_id={}".format(expected, actual)
)

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.

todo: A user may not be aware of the sidecar file used to verify the cluster ID. We should add remediation steps to this message. For example: "If this change is expected, remove {pin_path} and re-run."

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.

todo: In addition to suggesting how to correct the issue in this message, we should also add some documentation (in the README for now) of steps to take when migrating Vault.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I don't think we should suggest removing the pin. A mismatch can mean the original Vault still holds keys for devices we already manage. If we remove the pin, new operations could start using a different Vault without those keys being migrated. Vault migration is currently out of scope but it may be worth documenting this in the README.

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.

That makes sense, thanks. My primary concern was having some way to advertise that the pin file exists and what it's for.

Comment on lines +132 to +133
_luks_format.assert_not_called()
_boot_unlock.register.assert_not_called()

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.

question: Why are these functions being asserted to not be called? They seem unrelated to decryption, but I'm unsure if I'm missing something or if there are some extraneous asserts in some of the tests.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Thanks for catching, these assertions are stale, I will remove them

Comment on lines +108 to +110
def test_cluster_mismatch_warns_and_attempts_decrypts(
self, _luks_open, _luks_format, _boot_unlock,
_udevadm_rescan, _udevadm_settle):

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.

nit: Remove the extra "s" at the end of _decrypts

@Raven-182
Raven-182 force-pushed the vault-cluster-identity branch from 2b31f00 to ba32eef Compare October 2, 2026 14:37
@Raven-182
Raven-182 requested a review from LucasAPayne October 2, 2026 14:38
Comment thread README.rst Outdated
Comment on lines +92 to +100
After authentication, vaultlocker saves the Vault cluster ID in ``<config-path>.cluster_id``.
If the ID later changes, ``encrypt`` and ``enroll`` operations will fail with an error.
``decrypt`` will log a warning and try to find the key in the observed Vault cluster. If
that Vault does not have the key, vaultlocker cannot unlock the device.

If the change is unexpected, check the Vault url and restore the connection to
the original cluster. Deleting the pin does not move existing keys.
Vaultlocker does not support automatically migrating keys between clusters.

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.

Perfect, thanks!

@hemanthnakkina hemanthnakkina left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

minor nit on readme, otherwise LGTM

Comment thread README.rst Outdated
that a CIDR based ACL is in use to only allow permitted systems within the
Data Center to login and retrieve secrets from Vault.

After authentication, vaultlocker saves the Vault cluster ID in ``<config-path>.cluster_id``.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

<config-path>.cluster-id hyphen instead of underscore

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Updated, thanks

@hmlanigan hmlanigan 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.

Looks good once the small change is made.

Raven-182 and others added 2 commits October 6, 2026 14:07
Create one cluster pin beside each configuration file on first use. Authenticate with AppRole then verify the pin before starting a device operation.

Assisted-by: OpenCode (DeepSeek v4.1 Flash)
Co-authored-by: Billy Olsen <billy.olsen@canonical.com>
Co-authored-by: Lucas Payne <lucas.payne@canonical.com>
Co-authored-by: Heather Lanigan <heather.lanigan@canonical.com>
Signed-off-by: Billy Olsen <billy.olsen@canonical.com>
Signed-off-by: Raven Kaur <raven.kaur@canonical.com>
Require 90% line and branch coverage for production modules while excluding the test packages.

Assisted-by: OpenCode (DeepSeek v4.1 Flash)
Co-authored-by: Raven Kaur <raven.kaur@canonical.com>
Co-authored-by: Lucas Payne <lucas.payne@canonical.com>
Co-authored-by: Heather Lanigan <heather.lanigan@canonical.com>
Signed-off-by: Billy Olsen <billy.olsen@canonical.com>
Signed-off-by: Raven Kaur <raven.kaur@canonical.com>
@Raven-182
Raven-182 force-pushed the vault-cluster-identity branch from ba32eef to 063d320 Compare October 6, 2026 18:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants