Skip to content

GUACAMOLE-2196: Openbao/Hashicorp Vault extension - #1214

Closed
adb014 wants to merge 16 commits into
apache:mainfrom
adb014:openbao
Closed

GUACAMOLE-2196: Openbao/Hashicorp Vault extension #1214
adb014 wants to merge 16 commits into
apache:mainfrom
adb014:openbao

Conversation

@adb014

@adb014 adb014 commented May 21, 2026

Copy link
Copy Markdown

This PR was first based on #1143 but its moved a fair way on since then, I have however stolen their JIRA ticket. The main differences are

  • It supports both OpenBao and Hashicorp Vault, so this PR also address GUACAMOLE-2137: Introduce HashiCorp Vault token handler (based on the KSM module) #1116
  • It uses the same idea as GUACAMOLE-2137: Introduce HashiCorp Vault token handler (based on the KSM module) #1116 to have the tokens represented by their paths in the Vault
  • This extension requires read access of /sys/mounts in the Vault to obtan the list of secret engine mount paths and their types. This allows Guacamole to know how to treat the mount path in the token without additional user input.
  • Uses the spring-vault modules to talk to the Vault
  • It adds support for both KV_1 and KV_2 Key/Values secret engines
  • It adds support of the LDAP secret engine for static, dynamic and service accounts
  • It adds support of the SSH secret engine including both SSH one-time passwords and signed User certificates
  • It adds supports for the databse secret engine, allowing Guacamole itself to obtain its username, password and if the database id configured with additional static values, the URI of the database server and the database itself
  • Adds the possiblity of including sub-tokens with the Vault tokens (ex: vault://kv1/users{GUAC_USERNAME}/password)
  • Prepare an addition to guacamole-manual curently included in this commit in the docs/ sub-directory of the extension

As this PR is fully documented in the markdown/jinja I won't do in to details of the working of this extension, but rather discuss the problems I encountered

Simplified token names:

The KSM extension uses token names like KEEPER_SERVER_USERNAME as only a single Keeper secret record with relatively well controls key values is used. THis extension allows basically any mount path of teh secret engine and path to the secret record within the Vault and so multiple secret records. Its therefore difficult to see how the KSM token naming method could be projected on to a Vault.

As in #1116 I chose to expose the Vault paths directly in the tokens, allowing the complete freedom

Token paths:
In most cases the token name to use will be the path to the Vault secret record post-pending with the secret value to extract. This has two issues

  1. The Vault itself doesn't create the SSH certificates so the secret names "unsigned", "public" and "private" are imposed by Guacamole
  2. The LDAP service accounts are checked out via and endpoint "/check-out". I chose to drop the "/check-out" in the token path in the extension
  3. Certain secret keys are added for consistency with other endpoints. For example the password for SSH OTP is returned in the value "key". This value is kept but copied to "password". LDAP service account usernames and returned in "service_account_name" which is copied to "username"

Secret caching:

When obtaining a secret record from the Vault, the secrets in the record are associated between themselves. For exemple a dynamic LDAP account has a single use username and password. The structure of the base vault extension assumes a function getValue that returns a single secret value. We therefore need to cache the values for a single record to keep the association between common values.

In the getTokens method the VaultSecretService, this is not a problem as all tokens for a single connection are resolved at once and we can set a UUID as a key in the cache to ensure common values are kept together. It also prevents session stealing by timing issues in concurrent connections. For the secrets looked up for guacamole.propertes.vlt that basically only concern secrets without any user context this is no problem. The issue is with tokens looked up in vault-token-mapping.yml. These secrets are looked up individually, but they have a user context. To avoid secret stealing and try to keep common secrets together I used a cache key combining GUAC_USERNAME, USERNAME and the path to the Vault secret record.

By the nature of Vault tokens where the password rotation at each usage, this cache should only be very short term, in the order of a few seconds. I used caffiene for the cache

Spring-Vault :
Due to the Tomcat 9 dependency of Guacamole I was forced to use a version of spring-vault compatible with Java-11, so I'm left using version 2.3.4 of spring-vault, when spring-vault is currently working on version 4.x. This came with many issues, the main one being that the LifecycleAwareSessionManager class of spring-vault in version 2.3.4 is pretty much broken, so I had to reimplement my own SessionManager. The use of spring-vault in this pull-request is therefore more of a promise to be able to simplify the extension when Guacamole migrates to a newer Tomcat

Spring-vault also pulled in a number of other dependencies (spring-framework, micrometre, jspecify) that you might not want to pull in.

SSH ed25519:
The SSH certificate signing reqires Guacamole to be able to generate temporty SSH certificates. I used apach SSHD for this. The Java 11 versions of apache SSHD don't include ed25519 directly and I had to include i2p as a crypto provider for ed25519

LDAP Service accounts:
The LDAP service accounts in the Vault must be checked out and checked back in again after use. This presented two problems for this extension.

  1. With the extension the GuacamoleTunnel is not yet opened, and so I have no unique connection ID for the extension. I addressed this by assuming that the TunnelConnectEvent will be very rapidly after the call to getTokens in the extension, and setting the connection UUID in the TunnelConnectEvent.
  2. The TunnelCloseEvent is a bit random when and if it is called. I wrote the code to check-in the service account if a TunnelCloseEvent is recieved, but as a back-up I fixed the TTL of the checked out accounts to 2 hours to ensure the Vault automatically checks in these accounts. In Guacamole the service accounts check in information is just dropped after 2 hours. If we can get more reliable TunnelCloseEvents on the termination of the sessions, the code as is should just work

Non renewable or expired token
Even in recent spring-vault implementations of LifecycleAwareSessionManager, an expired or non renewable token will cause the SessionManager to just stop. I'd like to use VaultAgent for complex authentification methods via a token sink file. This essentially means that a reauthentication via a call to the function CLientAuthentication.login() of spring-vault should be used if the token is expired or non-renewable. I implemented this in my SessionManager

@corentin-soriano corentin-soriano left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Thank you for this contribution!

I don't have time to read it in detail right now, but I already have some comments.

Comment thread extensions/guacamole-vault/modules/guacamole-vault-openbao/docs/openbao.md.j2 Outdated
@adb014

adb014 commented May 21, 2026

Copy link
Copy Markdown
Author

PS: The build failure for this PR is due to the changes in generate-license-files.sh a few months ago. Basically just have to rename the README files to LICENSE, remove the version from the directory names and reformat the LICENSE files to be the same as the other LICENSE files...

I'll rebase this PR, so it builds with the latest Guacamole

@adb014

adb014 commented May 21, 2026

Copy link
Copy Markdown
Author

Latest commit removes the need from Guacamole to have access to the Vault path "/sys/mounts" and uses the path-help API instead. The path "sys/mounts" give information and metadata on all secret engine mount paths in the Vault, which is possibly a security risk depending on your threat model, whereas the path-help functionality only responses for paths that Guacamole has the permission to access.

David Bateman and others added 11 commits May 22, 2026 09:36
…he start of a testing script and documentation. The documentation will be transfered to guacamole-manual in the future
 - Gets ed25519 ssh to actually work
 - Replace spring-vault sesion manager with our own implement as broken
   in spring-vault version 2.3 and doesn't treat non renewable tokens by
   re-authentication in later versions of spring-vault
 - Reorganisation vault specific code in to a seperate sub-directory
 - Fully test everything but the LDAP and database secret engines
 - Update the documentation
…single point, resolve sub-tokens before inclusion, update documentation and remove old README.md
…ifference in creation of ldap static credentials between OpenBao and Hashicorp is only in the role creation, not the use by Guacamole
- Typos (review comment)
- Don't print secret values even for debug logging (review comment)
- Remove the need for access to sys/mounts that might be a security risk
  and use the Vault path-help API to get the mount-path and type
- Fix missing SSH OTP token secrets after simplification of OpenBaoClient
- Some documentation corrections
David Bateman added 2 commits May 22, 2026 13:46
- Use TokenFilter pattern matcher directly to detect arbitrary token by matching explicitly the allowed pattern modifiers
- Ignore pattern modifies in openbao extension
@adb014

adb014 commented May 23, 2026

Copy link
Copy Markdown
Author

@corentin-soriano can you hold off on reviewing this PR? Looking again at #1116 I kind of understand why the author wrote the code as he did, and there is one clear avantage of the code in #1116 for the concurrency in the http requests. Basically I see two possibilities

  1. We only allow a single vault configured at boot time as in this PR. In that case the class responsible for talking to the vault is a singleton as here. I’d still like to cherry pick the concurrency code from the vault client of GUACAMOLE-2137: Introduce HashiCorp Vault token handler (based on the KSM module) #1116 in that case to add to GUACAMOLE-2196: Openbao/Hashicorp Vault extension  #1214

  2. We want to allow per connection group vaults. I initially thought this was a bad idea, particularly on a per user basis, and as Guacamole has access to all of the configured vaults, it’s not a security issue from threats of compromission of guacamole-client itself. However, if the global vault has very limited permissions on the vault (or not configured at all) and the permissions of the vault role used for each connection group is limited to a different set of secrets. Doing this reduces the chance of exposing secrets from one connection group to another accidentally, so there is an advantage

What I’m thinking of doing, starting from #1116 is

  1. remove the user vault configuration as I really can’t see a use case and in case of a typo in a connection token by an administrator, it could be used to connect to a host outside the allowed connection group. For example imagine the connection group had the token vault://kv1/sevrer/user/hostname, where server is incorrectly spelt and won’t be found in the global vault. The user could then inject their own value for this.
  2. Replace the base64 encoded json configuration with real individual configuration parameters
  3. Replace the token naming scheme with the one I used here as closer to a standard URL format
  4. Use my cache keying scheme to limit risk of secret stealing and allow paired tokens (think public/private ssh certificates that go together) to be kept together
  5. Take my OpenBaoClient class and SessionManager, and a few other ancillary classes and use them to replace the only the http client in vault client of GUACAMOLE-2137: Introduce HashiCorp Vault token handler (based on the KSM module) #1116 keeping all the concurrency code.
  6. Rebase against latest main to ensure build still works
  7. Do two new PRs for guacamole-client and guacamole-manual

This should end up with a PR functionally equivalent to #1214 but allowing per connection group vaults, with a minimum of work. At that point up to you and the other maintainers to decide if per connection group vaults are a good idea or not.

@adb014

adb014 commented May 28, 2026

Copy link
Copy Markdown
Author

@corentin-soriano can you hold off on reviewing this PR? Looking again at #1116 I kind of understand why the author wrote the code as he did, and there is one clear avantage of the code in #1116 for the concurrency in the http requests. Basically I see two possibilities

  1. We only allow a single vault configured at boot time as in this PR. In that case the class responsible for talking to the vault is a singleton as here. I’d still like to cherry pick the concurrency code from the vault client of GUACAMOLE-2137: Introduce HashiCorp Vault token handler (based on the KSM module) #1116 in that case to add to GUACAMOLE-2196: Openbao/Hashicorp Vault extension  #1214
  2. We want to allow per connection group vaults. I initially thought this was a bad idea, particularly on a per user basis, and as Guacamole has access to all of the configured vaults, it’s not a security issue from threats of compromission of guacamole-client itself. However, if the global vault has very limited permissions on the vault (or not configured at all) and the permissions of the vault role used for each connection group is limited to a different set of secrets. Doing this reduces the chance of exposing secrets from one connection group to another accidentally, so there is an advantage

What I’m thinking of doing, starting from #1116 is

  1. remove the user vault configuration as I really can’t see a use case and in case of a typo in a connection token by an administrator, it could be used to connect to a host outside the allowed connection group. For example imagine the connection group had the token vault://kv1/sevrer/user/hostname, where server is incorrectly spelt and won’t be found in the global vault. The user could then inject their own value for this.
  2. Replace the base64 encoded json configuration with real individual configuration parameters
  3. Replace the token naming scheme with the one I used here as closer to a standard URL format
  4. Use my cache keying scheme to limit risk of secret stealing and allow paired tokens (think public/private ssh certificates that go together) to be kept together
  5. Take my OpenBaoClient class and SessionManager, and a few other ancillary classes and use them to replace the only the http client in vault client of GUACAMOLE-2137: Introduce HashiCorp Vault token handler (based on the KSM module) #1116 keeping all the concurrency code.
  6. Rebase against latest main to ensure build still works
  7. Do two new PRs for guacamole-client and guacamole-manual

This should end up with a PR functionally equivalent to #1214 but allowing per connection group vaults, with a minimum of work. At that point up to you and the other maintainers to decide if per connection group vaults are a good idea or not.

@corentin-soriano Frankly after working with #1116, I'm going to close this pull request and create another pull request that combines #1116, #1147 and #1214. To be continued in my next pull request

@adb014 adb014 closed this May 28, 2026
adb014 pushed a commit to adb014/guacamole-client that referenced this pull request Jun 1, 2026
…apache#1214

- It supports both OpenBao and Hashicorp Vault
- Uses url like tokens of the form "vault://<mount>/<path>/<secret>"
- Uses the path-help function of the vault to determine the vault type
- Uses spring-vault to communicate with the vault
- Adds support for both KV_1 and KV_2 Key-Value secret engines
- Adds support for LDAP secret engine and static, dynamic and service accounts
- Adds support of the SSH secret engine including both SSH one-time passwords and signed user certificates
- Adds supports for the database secret engine, allowing Guacamole itself to obtain its username, password and if the database is configured with additional static values, the URI of the database server and the database itself
- Adds the possiblity of including sub-tokens with the Vault tokens (ex: vault://kv1/users{GUAC_USERNAME}/password)
- Gets the connectionGroup and User fallback functions in apache#1116 to actually work
- Doesn't use a Base64 configuration string, but real Guacamole configuration options
- Doesn't use a sanitize function on TextFields of the Form, but rather PasswordField types
adb014 pushed a commit to adb014/guacamole-client that referenced this pull request Jun 5, 2026
…apache#1214

- It supports both OpenBao and Hashicorp Vault
- Uses url like tokens of the form "vault://<mount>/<path>/<secret>"
- Uses the path-help function of the vault to determine the vault type
- Uses spring-vault to communicate with the vault
- Adds support for both KV_1 and KV_2 Key-Value secret engines
- Adds support for LDAP secret engine and static, dynamic and service accounts
- Adds support of the SSH secret engine including both SSH one-time passwords and signed user certificates
- Adds supports for the database secret engine, allowing Guacamole itself to obtain its username, password and if the database is configured with additional static values, the URI of the database server and the database itself
- Adds the possiblity of including sub-tokens with the Vault tokens (ex: vault://kv1/users{GUAC_USERNAME}/password)
- Gets the connectionGroup and User fallback functions in apache#1116 to actually work
- Doesn't use a Base64 configuration string, but real Guacamole configuration options
- Doesn't use a sanitize function on TextFields of the Form, but rather PasswordField types
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.

4 participants