You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@guacamole.apache.org by "subbareddyalamur (via GitHub)" <gi...@apache.org> on 2025/12/30 03:58:16 UTC
[PR] OpenBao Vault Integration Extension [guacamole-client]
subbareddyalamur opened a new pull request, #1143:
URL: https://github.com/apache/guacamole-client/pull/1143
# Pull Request: OpenBao Vault Integration Extension
## Summary
This PR adds a new vault extension for integrating Apache Guacamole with OpenBao, an open-source secrets management platform (fork of HashiCorp Vault).
## JIRA Issue
**GUACAMOLE-XXXX** (To be created at: https://issues.apache.org/jira/browse/GUACAMOLE/)
## Motivation
OpenBao is a community-driven fork of HashiCorp Vault, maintained under the Linux Foundation. As organizations adopt OpenBao for secrets management, there is a need for native Guacamole integration to automatically retrieve connection credentials from OpenBao vaults. This extension enables:
1. **Automatic Credential Retrieval**: Users no longer need to manually manage or update connection passwords in Guacamole
2. **Centralized Secrets Management**: Credentials are stored and managed in OpenBao, not in Guacamole's database
3. **Enhanced Security**: Passwords are retrieved on-demand and never stored in Guacamole
4. **User-Based Secret Mapping**: Each Guacamole user can have their own set of credentials in OpenBao
## Implementation Details
### Architecture
The extension follows the same architectural pattern as the existing Keeper Secrets Manager (KSM) vault extension, utilizing the `guacamole-vault-base` framework. Key components:
1. **OpenBaoAuthenticationProvider**: Main extension entry point extending `VaultAuthenticationProvider`
2. **OpenBaoSecretService**: Implements `VaultSecretService` for token resolution and secret retrieval
3. **OpenBaoClient**: HTTP client for communicating with OpenBao REST API using Apache HttpClient 5
4. **OpenBaoConfigurationService**: Reads configuration from `guacamole.properties`
5. **OpenBaoDirectoryService**: Pass-through implementation (no directory management needed)
6. **OpenBaoAttributeService**: Minimal implementation (no custom attributes needed)
### Token System
The extension supports two token patterns:
- **`${OPENBAO_SECRET}`**: Replaced with the password retrieved from OpenBao
- **`${GUAC_USERNAME}`**: Replaced with the logged-in Guacamole username
### Secret Path Mapping
The extension maps Guacamole usernames directly to OpenBao KV v2 secret paths:
```
Guacamole username: "john"
OpenBao path: /v1/rdp-creds/data/john
```
Example secret structure in OpenBao:
```json
{
"data": {
"data": {
"username": "john",
"password": "SecurePassword123"
}
}
}
```
### Configuration
The extension requires minimal configuration in `guacamole.properties`:
```properties
# REQUIRED
openbao-server-url: http://openbao.example.com:8200
openbao-token: s.YourAuthTokenHere
# OPTIONAL (defaults shown)
openbao-mount-path: rdp-creds
```
Hardcoded defaults:
- KV version: `2` (KV v2 secrets engine)
- Connection timeout: `5000ms`
- Request timeout: `10000ms`
### Files Added
```
extensions/guacamole-vault/modules/guacamole-vault-openbao/
├── .ratignore
├── README.md
├── pom.xml
└── src/main/
├── java/org/apache/guacamole/vault/openbao/
│ ├── OpenBaoAuthenticationProvider.java
│ ├── OpenBaoAuthenticationProviderModule.java
│ ├── conf/
│ │ ├── OpenBaoConfig.java
│ │ └── OpenBaoConfigurationService.java
│ ├── secret/
│ │ ├── OpenBaoClient.java
│ │ └── OpenBaoSecretService.java
│ └── user/
│ ├── OpenBaoAttributeService.java
│ └── OpenBaoDirectoryService.java
└── resource-templates/
└── guac-manifest.json
```
### Files Modified
- `extensions/guacamole-vault/pom.xml`: Added `guacamole-vault-openbao` module
## Dependencies
New dependencies added for the OpenBao module:
- `org.apache.httpcomponents.client5:httpclient5:5.2.1` - HTTP client for REST API communication
- `com.google.code.gson:gson:2.10.1` - JSON parsing
Both dependencies are shaded into the final JAR.
## Testing
### Build Testing
The module builds successfully with Maven:
```bash
cd extensions/guacamole-vault
mvn clean install -DskipTests
```
Build output:
- JAR: `target/guacamole-vault-openbao-1.6.1.jar`
- All Apache RAT license checks pass
- No compilation warnings or errors
- Successfully integrates with guacamole-vault-dist module
### Functional Testing
Tested with:
- **Guacamole**: 1.6.0
- **OpenBao**: v2.4.4
- **Protocol**: RDP
- **Environment**: Docker containers
Test scenario:
1. User "subba" logs into Guacamole
2. User connects to RDP connection configured with:
- Username: `${GUAC_USERNAME}`
- Password: `${OPENBAO_SECRET}`
3. Extension retrieves password from OpenBao at `/v1/rdp-creds/data/subba`
4. RDP connection succeeds with retrieved credentials
**Result**: ✅ All tests passed successfully
## Compatibility
- **Guacamole Version**: 1.6.x
- **Java Version**: 8+
- **OpenBao Versions**: Tested with v2.4.4, should work with v2.0.0+
- **Supported Protocols**: All Guacamole protocols (RDP, VNC, SSH, etc.)
## Security Considerations
1. **Token Storage**: OpenBao tokens are stored in `guacamole.properties`. Administrators should:
- Use read-only tokens with minimal permissions
- Restrict file permissions on `guacamole.properties`
- Rotate tokens regularly
2. **TLS**: Production deployments should use HTTPS:
```properties
openbao-server-url: https://openbao.example.com:8200
```
3. **Network Security**: OpenBao should only be accessible from Guacamole servers
4. **Audit Logging**: Enable OpenBao audit logging to track credential access
## Documentation
- **README.md**: Comprehensive documentation including:
- Overview and features
- Configuration guide
- Installation instructions
- Security best practices
- Troubleshooting guide
- Example deployment
## Breaking Changes
None. This is a new extension module that does not modify any existing code.
## Checklist
- [x] Code builds successfully
- [x] Apache license headers present on all files
- [x] Apache RAT check passes
- [x] README documentation provided
- [x] Follows existing code patterns (modeled after KSM extension)
- [x] Tested functionally with OpenBao server
- [ ] JIRA issue created (pending)
- [ ] Update GUACAMOLE-XXXX in commit message after JIRA issue creation
## Questions for Reviewers
1. Should we add support for OpenBao namespaces in the configuration?
2. Should we add support for configurable KV version (v1/v2)?
3. Should connection groups be supported with group-level OpenBao configuration?
4. Should we add support for multiple OpenBao mount paths?
## Future Enhancements
Potential future improvements (not in this PR):
1. **Multiple Mount Paths**: Support different OpenBao mount paths for different connection groups
2. **Token Rotation**: Automatic token renewal/rotation
3. **AppRole Authentication**: Support AppRole in addition to token-based auth
4. **Connection Group Configuration**: Allow OpenBao settings per connection group
5. **Secret Caching**: Optional caching to reduce API calls
6. **Namespace Support**: Support for OpenBao Enterprise namespaces
## Related Work
This extension is inspired by and follows the same architectural patterns as:
- Keeper Secrets Manager (KSM) vault extension (`guacamole-vault-ksm`)
## Additional Notes
OpenBao project: https://openbao.org/
OpenBao GitHub: https://github.com/openbao/openbao
---
**Ready for Review**: This PR is ready for initial review. Will update JIRA issue number once created.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: dev-unsubscribe@guacamole.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] GUACAMOLE-2196: OpenBao Vault Integration Extension [guacamole-client]
Posted by "subbareddyalamur (via GitHub)" <gi...@apache.org>.
subbareddyalamur commented on PR #1143:
URL: https://github.com/apache/guacamole-client/pull/1143#issuecomment-3912313991
> @subbareddyalamur Please:
>
> * Update your commit messages to include `GUACAMOLE-2196:`
> * Make sure your commits have [meaningful and unique messages](https://www.codelord.net/2015/03/16/bad-commit-messages-hall-of-shame/), or squash them together.
@necouchman Done.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: dev-unsubscribe@guacamole.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] GUACAMOLE-2196: OpenBao Vault Integration Extension [guacamole-client]
Posted by "corentin-soriano (via GitHub)" <gi...@apache.org>.
corentin-soriano commented on code in PR #1143:
URL: https://github.com/apache/guacamole-client/pull/1143#discussion_r2912430299
##########
extensions/guacamole-vault/modules/guacamole-vault-openbao/src/main/java/org/apache/guacamole/vault/openbao/secret/OpenBaoClient.java:
##########
@@ -0,0 +1,164 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.guacamole.vault.openbao.secret;
+
+import com.google.gson.Gson;
+import com.google.gson.JsonObject;
+import com.google.inject.Inject;
+import org.apache.guacamole.GuacamoleException;
+import org.apache.guacamole.GuacamoleServerException;
+import org.apache.guacamole.vault.openbao.conf.OpenBaoConfigurationService;
+import org.apache.hc.client5.http.classic.methods.HttpGet;
+import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
+import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse;
+import org.apache.hc.client5.http.impl.classic.HttpClients;
+import org.apache.hc.core5.http.io.entity.EntityUtils;
+import org.apache.hc.core5.util.Timeout;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+
+/**
+ * Client for communicating with OpenBao REST API.
+ */
+public class OpenBaoClient {
+
+ /**
+ * Logger for this class.
+ */
+ private static final Logger logger = LoggerFactory.getLogger(OpenBaoClient.class);
+
+ /**
+ * Service for retrieving OpenBao configuration.
+ */
+ @Inject
+ private OpenBaoConfigurationService configService;
+
+ /**
+ * Gson instance for JSON parsing.
+ */
+ private final Gson gson = new Gson();
+
+ /**
+ * Retrieves a secret from OpenBao by username.
+ *
+ * @param username
+ * The Guacamole username to look up in OpenBao.
+ *
+ * @return
+ * The JSON response from OpenBao.
+ *
+ * @throws GuacamoleException
+ * If the secret cannot be retrieved from OpenBao.
+ */
+ public JsonObject getSecret(String username) throws GuacamoleException {
+
+ String serverUrl = configService.getServerUrl();
+ String token = configService.getToken();
+ String mountPath = configService.getMountPath();
+ String kvVersion = configService.getKvVersion();
+
+ // Build the API path based on KV version
+ // KV v2: /v1/{mount-path}/data/{username}
+ // KV v1: /v1/{mount-path}/{username}
+ String apiPath;
+ if ("2".equals(kvVersion)) {
+ apiPath = String.format("/v1/%s/data/%s", mountPath, username);
+ } else {
+ apiPath = String.format("/v1/%s/%s", mountPath, username);
+ }
+
+ String fullUrl = serverUrl + apiPath;
+
+ logger.info("Fetching secret from OpenBao: {}", fullUrl);
+
+ try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
+
+ HttpGet httpGet = new HttpGet(fullUrl);
+ httpGet.setHeader("X-Vault-Token", token);
+ httpGet.setHeader("Accept", "application/json");
+
+ // Set timeouts
+ httpGet.setConfig(org.apache.hc.client5.http.config.RequestConfig.custom()
+ .setConnectionRequestTimeout(Timeout.ofMilliseconds(configService.getConnectionTimeout()))
+ .setResponseTimeout(Timeout.ofMilliseconds(configService.getRequestTimeout()))
+ .build());
+
+ org.apache.hc.core5.http.ClassicHttpResponse response = httpClient.executeOpen(null, httpGet, null);
+ try {
+ int statusCode = response.getCode();
+ String responseBody = EntityUtils.toString(response.getEntity());
+
+ if (statusCode == 200) {
+ logger.info("OpenBao response status: {} - successfully retrieved password for {}", statusCode, username);
+ JsonObject jsonResponse = gson.fromJson(responseBody, JsonObject.class);
+ return jsonResponse;
+ } else if (statusCode == 404) {
+ logger.warn("Secret not found in OpenBao for username: {}", username);
+ throw new GuacamoleServerException("Secret not found in OpenBao for username: " + username);
+ } else if (statusCode == 403) {
+ logger.error("Permission denied accessing OpenBao. Check token permissions.");
+ throw new GuacamoleServerException("Permission denied accessing OpenBao. Check token permissions.");
+ } else {
+ logger.error("OpenBao returned error status {}: {}", statusCode, responseBody);
+ throw new GuacamoleServerException("OpenBao error (HTTP " + statusCode + "): " + responseBody);
+ }
+ } finally {
+ response.close();
+ }
+
+ } catch (IOException | org.apache.hc.core5.http.ParseException e) {
+ logger.error("Failed to communicate with OpenBao at {}: {}", fullUrl, e.getMessage());
+ throw new GuacamoleServerException("Failed to communicate with OpenBao", e);
+ }
+ }
+
+ /**
+ * Extracts the password field from an OpenBao KV v2 response.
+ *
+ * @param response
+ * The JSON response from OpenBao.
+ *
+ * @return
+ * The password string, or null if not found.
+ */
+ public String extractPassword(JsonObject response) {
+ try {
+ // For KV v2: response.data.data.password
+ if (response.has("data")) {
+ JsonObject data = response.getAsJsonObject("data");
+ if (data.has("data")) {
+ JsonObject innerData = data.getAsJsonObject("data");
+ if (innerData.has("password")) {
+ return innerData.get("password").getAsString();
+ }
+ }
+ }
+
+ logger.warn("Password field not found in OpenBao response");
+ return null;
+
+ } catch (Exception e) {
Review Comment:
Could we catch a more specific exception?
##########
extensions/guacamole-vault/modules/guacamole-vault-openbao/src/main/java/org/apache/guacamole/vault/openbao/secret/OpenBaoClient.java:
##########
@@ -0,0 +1,164 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.guacamole.vault.openbao.secret;
+
+import com.google.gson.Gson;
+import com.google.gson.JsonObject;
+import com.google.inject.Inject;
+import org.apache.guacamole.GuacamoleException;
+import org.apache.guacamole.GuacamoleServerException;
+import org.apache.guacamole.vault.openbao.conf.OpenBaoConfigurationService;
+import org.apache.hc.client5.http.classic.methods.HttpGet;
+import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
+import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse;
+import org.apache.hc.client5.http.impl.classic.HttpClients;
+import org.apache.hc.core5.http.io.entity.EntityUtils;
+import org.apache.hc.core5.util.Timeout;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+
+/**
+ * Client for communicating with OpenBao REST API.
+ */
+public class OpenBaoClient {
+
+ /**
+ * Logger for this class.
+ */
+ private static final Logger logger = LoggerFactory.getLogger(OpenBaoClient.class);
+
+ /**
+ * Service for retrieving OpenBao configuration.
+ */
+ @Inject
+ private OpenBaoConfigurationService configService;
+
+ /**
+ * Gson instance for JSON parsing.
+ */
+ private final Gson gson = new Gson();
+
+ /**
+ * Retrieves a secret from OpenBao by username.
+ *
+ * @param username
+ * The Guacamole username to look up in OpenBao.
+ *
+ * @return
+ * The JSON response from OpenBao.
+ *
+ * @throws GuacamoleException
+ * If the secret cannot be retrieved from OpenBao.
+ */
+ public JsonObject getSecret(String username) throws GuacamoleException {
+
+ String serverUrl = configService.getServerUrl();
+ String token = configService.getToken();
+ String mountPath = configService.getMountPath();
+ String kvVersion = configService.getKvVersion();
+
+ // Build the API path based on KV version
+ // KV v2: /v1/{mount-path}/data/{username}
+ // KV v1: /v1/{mount-path}/{username}
+ String apiPath;
+ if ("2".equals(kvVersion)) {
+ apiPath = String.format("/v1/%s/data/%s", mountPath, username);
+ } else {
+ apiPath = String.format("/v1/%s/%s", mountPath, username);
+ }
+
+ String fullUrl = serverUrl + apiPath;
+
+ logger.info("Fetching secret from OpenBao: {}", fullUrl);
+
+ try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
Review Comment:
Is there a specific reason to re-instantiate an HTTP client for each request?
##########
extensions/guacamole-vault/modules/guacamole-vault-openbao/src/main/java/org/apache/guacamole/vault/openbao/secret/OpenBaoClient.java:
##########
@@ -0,0 +1,164 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.guacamole.vault.openbao.secret;
+
+import com.google.gson.Gson;
+import com.google.gson.JsonObject;
+import com.google.inject.Inject;
+import org.apache.guacamole.GuacamoleException;
+import org.apache.guacamole.GuacamoleServerException;
+import org.apache.guacamole.vault.openbao.conf.OpenBaoConfigurationService;
+import org.apache.hc.client5.http.classic.methods.HttpGet;
+import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
+import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse;
+import org.apache.hc.client5.http.impl.classic.HttpClients;
+import org.apache.hc.core5.http.io.entity.EntityUtils;
+import org.apache.hc.core5.util.Timeout;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+
+/**
+ * Client for communicating with OpenBao REST API.
+ */
+public class OpenBaoClient {
+
+ /**
+ * Logger for this class.
+ */
+ private static final Logger logger = LoggerFactory.getLogger(OpenBaoClient.class);
+
+ /**
+ * Service for retrieving OpenBao configuration.
+ */
+ @Inject
+ private OpenBaoConfigurationService configService;
+
+ /**
+ * Gson instance for JSON parsing.
+ */
+ private final Gson gson = new Gson();
+
+ /**
+ * Retrieves a secret from OpenBao by username.
+ *
+ * @param username
+ * The Guacamole username to look up in OpenBao.
+ *
+ * @return
+ * The JSON response from OpenBao.
+ *
+ * @throws GuacamoleException
+ * If the secret cannot be retrieved from OpenBao.
+ */
+ public JsonObject getSecret(String username) throws GuacamoleException {
+
+ String serverUrl = configService.getServerUrl();
+ String token = configService.getToken();
+ String mountPath = configService.getMountPath();
+ String kvVersion = configService.getKvVersion();
Review Comment:
What will happen if the URL, path, or token is not defined or is empty?
##########
extensions/guacamole-vault/modules/guacamole-vault-openbao/src/main/java/org/apache/guacamole/vault/openbao/secret/OpenBaoSecretService.java:
##########
@@ -0,0 +1,172 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.guacamole.vault.openbao.secret;
+
+import com.google.gson.JsonObject;
+import com.google.inject.Inject;
+import org.apache.guacamole.GuacamoleException;
+import org.apache.guacamole.net.auth.Connectable;
+import org.apache.guacamole.net.auth.UserContext;
+import org.apache.guacamole.protocol.GuacamoleConfiguration;
+import org.apache.guacamole.token.TokenFilter;
+import org.apache.guacamole.vault.secret.VaultSecretService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Map;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.Future;
+
+/**
+ * OpenBao implementation of VaultSecretService.
+ * Retrieves RDP passwords from OpenBao based on the logged-in Guacamole username.
+ */
+public class OpenBaoSecretService implements VaultSecretService {
+
+ /**
+ * Logger for this class.
+ */
+ private static final Logger logger = LoggerFactory.getLogger(OpenBaoSecretService.class);
+
+ /**
+ * Client for communicating with OpenBao.
+ */
+ @Inject
+ private OpenBaoClient openBaoClient;
+
+ /**
+ * Constructor that logs when the service is created.
+ */
+ public OpenBaoSecretService() {
+ logger.info("OpenBaoSecretService initialized");
+ }
+
+ /**
+ * The token pattern for OpenBao secrets: ${OPENBAO_SECRET}
+ */
+ public static final String OPENBAO_SECRET_TOKEN = "${OPENBAO_SECRET}";
+
+ /**
+ * The token pattern for Guacamole username: ${GUAC_USERNAME}
+ */
+ public static final String GUAC_USERNAME_TOKEN = "${GUAC_USERNAME}";
+
+ @Override
+ public String canonicalize(String token) {
+ // Return the canonical form for tokens we recognize
+ if (token == null)
+ return null;
+
+ // Remove ${} wrapper and return just the token name
+ if (OPENBAO_SECRET_TOKEN.equals(token)) {
+ return "OPENBAO_SECRET";
+ }
+
+ if (GUAC_USERNAME_TOKEN.equals(token)) {
+ return "GUAC_USERNAME";
+ }
+
+ // Not our token
+ return null;
+ }
+
+ @Override
+ public Future<String> getValue(String token) throws GuacamoleException {
+ // This method is called for simple token lookups without user context
+ logger.warn("getValue(String) called without user context - cannot determine username");
+ return CompletableFuture.completedFuture(null);
+ }
+
+ @Override
+ public Future<String> getValue(UserContext userContext, Connectable connectable, String token)
+ throws GuacamoleException {
+
+ logger.info("getValue() called with token: {}", token);
+
+ // Get the logged-in Guacamole username
+ String username = userContext.self().getIdentifier();
+
+ // Handle GUAC_USERNAME token - return the Guacamole username
+ if ("GUAC_USERNAME".equals(token)) {
+ logger.info("getValue() returning username: '{}'", username);
+ return CompletableFuture.completedFuture(username);
+ }
+
+ // Handle OPENBAO_SECRET token - fetch password from OpenBao
+ if ("OPENBAO_SECRET".equals(token)) {
+ logger.info("Retrieving OpenBao secret for username: {}", username);
+
+ try {
+ // Fetch the secret from OpenBao using the username
+ JsonObject response = openBaoClient.getSecret(username);
+
+ // Extract the password field
+ String password = openBaoClient.extractPassword(response);
+
+ if (password != null) {
+ logger.info("Successfully retrieved password from OpenBao for user: {} (length: {})", username, password.length());
+ return CompletableFuture.completedFuture(password);
+ } else {
+ logger.warn("Password field not found in OpenBao for user: {}", username);
+ return CompletableFuture.completedFuture(null);
+ }
+
+ } catch (GuacamoleException e) {
+ logger.error("Failed to retrieve secret from OpenBao for user: {}", username, e);
+ // Return null instead of throwing to allow connection attempt with empty password
+ return CompletableFuture.completedFuture(null);
+ }
+ }
+
+ // Not a recognized token
+ logger.warn("Token '{}' not recognized, returning null", token);
+ return CompletableFuture.completedFuture(null);
+ }
+
+ @Override
+ public Map<String, Future<String>> getTokens(UserContext userContext,
+ Connectable connectable,
+ GuacamoleConfiguration config,
+ TokenFilter tokenFilter) throws GuacamoleException {
+
+ Map<String, Future<String>> tokens = new java.util.HashMap<>();
+ String username = userContext.self().getIdentifier();
+
+ // Add GUAC_USERNAME token (always available)
+ tokens.put("GUAC_USERNAME", CompletableFuture.completedFuture(username));
+
+ // Add OPENBAO_SECRET token (fetch from OpenBao)
+ try {
+ JsonObject response = openBaoClient.getSecret(username);
+ String password = openBaoClient.extractPassword(response);
+ if (password != null) {
+ tokens.put("OPENBAO_SECRET", CompletableFuture.completedFuture(password));
+ logger.info("Added token OPENBAO_SECRET with password from OpenBao (length: {})", password.length());
+ } else {
+ logger.warn("Password not found in OpenBao for user: {}", username);
+ }
+ } catch (Exception e) {
Review Comment:
Could we catch a more specific exception?
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: dev-unsubscribe@guacamole.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] GUACAMOLE-2196: OpenBao Vault Integration Extension [guacamole-client]
Posted by "adb014 (via GitHub)" <gi...@apache.org>.
adb014 commented on code in PR #1143:
URL: https://github.com/apache/guacamole-client/pull/1143#discussion_r3092887198
##########
extensions/guacamole-vault/modules/guacamole-vault-openbao/src/main/java/org/apache/guacamole/vault/openbao/conf/OpenBaoConfigurationService.java:
##########
@@ -0,0 +1,122 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.guacamole.vault.openbao.conf;
+
+import com.google.inject.Inject;
+import org.apache.guacamole.GuacamoleException;
+import org.apache.guacamole.environment.Environment;
+import org.apache.guacamole.vault.conf.VaultConfigurationService;
+
+/**
+ * Service for retrieving OpenBao configuration from guacamole.properties.
+ */
+public class OpenBaoConfigurationService extends VaultConfigurationService {
+
+ /**
+ * The Guacamole server environment.
+ */
+ @Inject
+ private Environment environment;
+
+ /**
+ * Creates a new OpenBaoConfigurationService.
+ */
+ public OpenBaoConfigurationService() {
+ super("openbao-token-mapping.yml", "guacamole.properties.openbao");
+ }
+
+ /**
+ * Returns the OpenBao server URL.
+ *
+ * @return The OpenBao server URL (e.g., "http://localhost:8200").
+ * @throws GuacamoleException
+ * If the property is not defined in guacamole.properties.
+ */
+ public String getServerUrl() throws GuacamoleException {
+ return environment.getRequiredProperty(OpenBaoConfig.OPENBAO_SERVER_URL);
+ }
+
+ /**
+ * Returns the OpenBao authentication token.
+ *
+ * @return The OpenBao authentication token.
+ * @throws GuacamoleException
+ * If the property is not defined in guacamole.properties.
+ */
+ public String getToken() throws GuacamoleException {
Review Comment:
If you do then code in my proposed spring implementation is changed to.
```java
this.vaultTemplate = new VaultTemplate(endpoint,
new AppRoleAuthentication(confService.getRoleID(),
new AppRoleAuthenticationOptions()
.setSecretId(confService.getSecretID())));
```
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: dev-unsubscribe@guacamole.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] GUACAMOLE-2196: OpenBao Vault Integration Extension [guacamole-client]
Posted by "subbareddyalamur (via GitHub)" <gi...@apache.org>.
subbareddyalamur commented on code in PR #1143:
URL: https://github.com/apache/guacamole-client/pull/1143#discussion_r3108738897
##########
extensions/guacamole-vault/modules/guacamole-vault-openbao/src/main/java/org/apache/guacamole/vault/openbao/secret/OpenBaoSecretService.java:
##########
@@ -0,0 +1,172 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.guacamole.vault.openbao.secret;
+
+import com.google.gson.JsonObject;
+import com.google.inject.Inject;
+import org.apache.guacamole.GuacamoleException;
+import org.apache.guacamole.net.auth.Connectable;
+import org.apache.guacamole.net.auth.UserContext;
+import org.apache.guacamole.protocol.GuacamoleConfiguration;
+import org.apache.guacamole.token.TokenFilter;
+import org.apache.guacamole.vault.secret.VaultSecretService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Map;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.Future;
+
+/**
+ * OpenBao implementation of VaultSecretService.
+ * Retrieves RDP passwords from OpenBao based on the logged-in Guacamole username.
+ */
+public class OpenBaoSecretService implements VaultSecretService {
+
+ /**
+ * Logger for this class.
+ */
+ private static final Logger logger = LoggerFactory.getLogger(OpenBaoSecretService.class);
+
+ /**
+ * Client for communicating with OpenBao.
+ */
+ @Inject
+ private OpenBaoClient openBaoClient;
+
+ /**
+ * Constructor that logs when the service is created.
+ */
+ public OpenBaoSecretService() {
+ logger.info("OpenBaoSecretService initialized");
+ }
+
+ /**
+ * The token pattern for OpenBao secrets: ${OPENBAO_SECRET}
+ */
+ public static final String OPENBAO_SECRET_TOKEN = "${OPENBAO_SECRET}";
+
+ /**
+ * The token pattern for Guacamole username: ${GUAC_USERNAME}
+ */
+ public static final String GUAC_USERNAME_TOKEN = "${GUAC_USERNAME}";
+
+ @Override
+ public String canonicalize(String token) {
+ // Return the canonical form for tokens we recognize
+ if (token == null)
+ return null;
+
+ // Remove ${} wrapper and return just the token name
+ if (OPENBAO_SECRET_TOKEN.equals(token)) {
+ return "OPENBAO_SECRET";
+ }
+
+ if (GUAC_USERNAME_TOKEN.equals(token)) {
+ return "GUAC_USERNAME";
+ }
+
+ // Not our token
+ return null;
+ }
+
+ @Override
+ public Future<String> getValue(String token) throws GuacamoleException {
+ // This method is called for simple token lookups without user context
+ logger.warn("getValue(String) called without user context - cannot determine username");
+ return CompletableFuture.completedFuture(null);
+ }
+
+ @Override
+ public Future<String> getValue(UserContext userContext, Connectable connectable, String token)
+ throws GuacamoleException {
+
+ logger.info("getValue() called with token: {}", token);
+
+ // Get the logged-in Guacamole username
+ String username = userContext.self().getIdentifier();
+
+ // Handle GUAC_USERNAME token - return the Guacamole username
+ if ("GUAC_USERNAME".equals(token)) {
+ logger.info("getValue() returning username: '{}'", username);
+ return CompletableFuture.completedFuture(username);
+ }
+
+ // Handle OPENBAO_SECRET token - fetch password from OpenBao
+ if ("OPENBAO_SECRET".equals(token)) {
Review Comment:
I've implemented that additively in this new commit, so arbitrary secret paths are now supported alongside the existing per-user lookup.
I'd like to avoid renaming ${OPENBAO_SECRET} in this PR to "${OPENBAO_PASSWORD}"
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: dev-unsubscribe@guacamole.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] GUACAMOLE-2196: OpenBao Vault Integration Extension [guacamole-client]
Posted by "adb014 (via GitHub)" <gi...@apache.org>.
adb014 commented on PR #1143:
URL: https://github.com/apache/guacamole-client/pull/1143#issuecomment-4315037386
I won't make a pull request of this yet, because it is very much a work in progress, but the ["openbao" branch of my fork](https://github.com/adb014/guacamole-client/tree/openbao) has what I think this module should look like.. At this point it compiles, but its completely untested (I haven't even run it once). But I wanted to get it out there for discussion with you.
My version can use Guacamole tokens of the form `vault://<MOUNT PATH>/<ROLE>/<SECRET>`, where `<MOUNT PATH>` is a valid mount path on the vault server using one of 4 possible secret engines
- Key-Values : The engine you are using
- LDAP : The secret engine for ldap managed accounts, allowing password rotation by OpenBao
- SSH : The ssh engine, allowing one-time password with a helper function of signed certificate access
- Database: Allowing the password and account name that guacamole itself uses to access its database to be stored and rotated by the Vault
The type of secret engine is automatically detected by reading "sys/mounts" from the vault. So the role assigned to Guacamole needs read access in the vault to "sys/mounts". This also means it can support both KV_1 and KV_2 key-values stores intrinsically and is fully Hashicorp/OpenBao compatible via its use of org.springframework.vault
Two authentication methods are supported
- Token Authentication : The token can be a standard Base62 encoded service token of the Vault or a token in a file. If the token is in a file the life cycle management of the token taken into account and the file is reread on renewal. This allows this file to be a sink of a VaultAgent allowing AuthRole login, etc to be implemented with the VaultAgent as the broker running locally on the same machine as the guacamole client
- Username/Password : as you'd expect
I've attempted to write the document for guacamole-manual included in the "doc/" sub-directory. This still needs lots of work and lots of testing. But I think its more complete than your code or the pull request #1116. I'll do some testing for my use cases, but I'd love to collaborate on getting this to work.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: dev-unsubscribe@guacamole.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] GUACAMOLE-2196: OpenBao Vault Integration Extension [guacamole-client]
Posted by "adb014 (via GitHub)" <gi...@apache.org>.
adb014 commented on code in PR #1143:
URL: https://github.com/apache/guacamole-client/pull/1143#discussion_r3101119792
##########
extensions/guacamole-vault/modules/guacamole-vault-openbao/src/main/java/org/apache/guacamole/vault/openbao/secret/OpenBaoSecretService.java:
##########
@@ -0,0 +1,172 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.guacamole.vault.openbao.secret;
+
+import com.google.gson.JsonObject;
+import com.google.inject.Inject;
+import org.apache.guacamole.GuacamoleException;
+import org.apache.guacamole.net.auth.Connectable;
+import org.apache.guacamole.net.auth.UserContext;
+import org.apache.guacamole.protocol.GuacamoleConfiguration;
+import org.apache.guacamole.token.TokenFilter;
+import org.apache.guacamole.vault.secret.VaultSecretService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Map;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.Future;
+
+/**
+ * OpenBao implementation of VaultSecretService.
+ * Retrieves RDP passwords from OpenBao based on the logged-in Guacamole username.
+ */
+public class OpenBaoSecretService implements VaultSecretService {
+
+ /**
+ * Logger for this class.
+ */
+ private static final Logger logger = LoggerFactory.getLogger(OpenBaoSecretService.class);
+
+ /**
+ * Client for communicating with OpenBao.
+ */
+ @Inject
+ private OpenBaoClient openBaoClient;
+
+ /**
+ * Constructor that logs when the service is created.
+ */
+ public OpenBaoSecretService() {
+ logger.info("OpenBaoSecretService initialized");
+ }
+
+ /**
+ * The token pattern for OpenBao secrets: ${OPENBAO_SECRET}
+ */
+ public static final String OPENBAO_SECRET_TOKEN = "${OPENBAO_SECRET}";
+
+ /**
+ * The token pattern for Guacamole username: ${GUAC_USERNAME}
+ */
+ public static final String GUAC_USERNAME_TOKEN = "${GUAC_USERNAME}";
+
+ @Override
+ public String canonicalize(String token) {
+ // Return the canonical form for tokens we recognize
+ if (token == null)
+ return null;
+
+ // Remove ${} wrapper and return just the token name
+ if (OPENBAO_SECRET_TOKEN.equals(token)) {
+ return "OPENBAO_SECRET";
+ }
+
+ if (GUAC_USERNAME_TOKEN.equals(token)) {
+ return "GUAC_USERNAME";
+ }
+
+ // Not our token
+ return null;
+ }
+
+ @Override
+ public Future<String> getValue(String token) throws GuacamoleException {
+ // This method is called for simple token lookups without user context
+ logger.warn("getValue(String) called without user context - cannot determine username");
+ return CompletableFuture.completedFuture(null);
+ }
+
+ @Override
+ public Future<String> getValue(UserContext userContext, Connectable connectable, String token)
+ throws GuacamoleException {
+
+ logger.info("getValue() called with token: {}", token);
+
+ // Get the logged-in Guacamole username
+ String username = userContext.self().getIdentifier();
+
+ // Handle GUAC_USERNAME token - return the Guacamole username
+ if ("GUAC_USERNAME".equals(token)) {
+ logger.info("getValue() returning username: '{}'", username);
+ return CompletableFuture.completedFuture(username);
+ }
+
+ // Handle OPENBAO_SECRET token - fetch password from OpenBao
+ if ("OPENBAO_SECRET".equals(token)) {
Review Comment:
Only handles '${GUAC_USERNAME}" and "${OPENBAO_SECRET}" so there is only a single key/value pair allowed . This should do something like
```java
static String user_password_subpath = "userpasswords/";
String key;
if (token.toLowerCase().startsWith("openbao:") || ){
key = token.subString(8);
}
else if token.equals("OPENBAO_SECRET") {
key = user_password_subpath + username;
}
if (key) {
...
JsonObject response = openBaoClient.getSecret(key);
```
This would also allow passwords like "${openbao:/path/to/secret}" where the path is under the mount path used. "${OPENBAO_SECRET}" should probably be "${OPENBAO_PASSWORD}" for consistency with "${GAUC_PASSWORD}"
Also not sure we get here with " ${GUAC_USERNAME}" value, or if we do we should respond with an empty string and allow Guacamole itself to replace the value
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: dev-unsubscribe@guacamole.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] GUACAMOLE-2196: OpenBao Vault Integration Extension [guacamole-client]
Posted by "adb014 (via GitHub)" <gi...@apache.org>.
adb014 commented on code in PR #1143:
URL: https://github.com/apache/guacamole-client/pull/1143#discussion_r3092887198
##########
extensions/guacamole-vault/modules/guacamole-vault-openbao/src/main/java/org/apache/guacamole/vault/openbao/conf/OpenBaoConfigurationService.java:
##########
@@ -0,0 +1,122 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.guacamole.vault.openbao.conf;
+
+import com.google.inject.Inject;
+import org.apache.guacamole.GuacamoleException;
+import org.apache.guacamole.environment.Environment;
+import org.apache.guacamole.vault.conf.VaultConfigurationService;
+
+/**
+ * Service for retrieving OpenBao configuration from guacamole.properties.
+ */
+public class OpenBaoConfigurationService extends VaultConfigurationService {
+
+ /**
+ * The Guacamole server environment.
+ */
+ @Inject
+ private Environment environment;
+
+ /**
+ * Creates a new OpenBaoConfigurationService.
+ */
+ public OpenBaoConfigurationService() {
+ super("openbao-token-mapping.yml", "guacamole.properties.openbao");
+ }
+
+ /**
+ * Returns the OpenBao server URL.
+ *
+ * @return The OpenBao server URL (e.g., "http://localhost:8200").
+ * @throws GuacamoleException
+ * If the property is not defined in guacamole.properties.
+ */
+ public String getServerUrl() throws GuacamoleException {
+ return environment.getRequiredProperty(OpenBaoConfig.OPENBAO_SERVER_URL);
+ }
+
+ /**
+ * Returns the OpenBao authentication token.
+ *
+ * @return The OpenBao authentication token.
+ * @throws GuacamoleException
+ * If the property is not defined in guacamole.properties.
+ */
+ public String getToken() throws GuacamoleException {
Review Comment:
If you do then code in my proposed spring implementation is changed to.
this.vaultTemplate = new VaultTemplate(
endpoint,
new AppRoleAuthentication(
confService.getRoleID(),
new AppRoleAuthenticationOptions()
.setSecretId(confService.getSecretID())
)
);
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: dev-unsubscribe@guacamole.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] GUACAMOLE-2196: OpenBao Vault Integration Extension [guacamole-client]
Posted by "necouchman (via GitHub)" <gi...@apache.org>.
necouchman commented on PR #1143:
URL: https://github.com/apache/guacamole-client/pull/1143#issuecomment-3797158160
@subbareddyalamur Please:
* Update your commit messages to include `GUACAMOLE-2196:`
* Make sure your commits have [meaningful and unique messages](https://www.codelord.net/2015/03/16/bad-commit-messages-hall-of-shame/), or squash them together.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: dev-unsubscribe@guacamole.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] GUACAMOLE-2196: OpenBao Vault Integration Extension [guacamole-client]
Posted by "adb014 (via GitHub)" <gi...@apache.org>.
adb014 commented on code in PR #1143:
URL: https://github.com/apache/guacamole-client/pull/1143#discussion_r3092222590
##########
extensions/guacamole-vault/modules/guacamole-vault-openbao/src/main/java/org/apache/guacamole/vault/openbao/secret/OpenBaoClient.java:
##########
@@ -0,0 +1,164 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.guacamole.vault.openbao.secret;
+
+import com.google.gson.Gson;
+import com.google.gson.JsonObject;
+import com.google.inject.Inject;
+import org.apache.guacamole.GuacamoleException;
+import org.apache.guacamole.GuacamoleServerException;
+import org.apache.guacamole.vault.openbao.conf.OpenBaoConfigurationService;
+import org.apache.hc.client5.http.classic.methods.HttpGet;
+import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
+import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse;
+import org.apache.hc.client5.http.impl.classic.HttpClients;
+import org.apache.hc.core5.http.io.entity.EntityUtils;
+import org.apache.hc.core5.util.Timeout;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+
+/**
+ * Client for communicating with OpenBao REST API.
+ */
+public class OpenBaoClient {
+
+ /**
+ * Logger for this class.
+ */
+ private static final Logger logger = LoggerFactory.getLogger(OpenBaoClient.class);
+
+ /**
+ * Service for retrieving OpenBao configuration.
+ */
+ @Inject
+ private OpenBaoConfigurationService configService;
+
+ /**
+ * Gson instance for JSON parsing.
+ */
+ private final Gson gson = new Gson();
Review Comment:
SHoudn't this be static..
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: dev-unsubscribe@guacamole.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] GUACAMOLE-2196: OpenBao Vault Integration Extension [guacamole-client]
Posted by "subbareddyalamur (via GitHub)" <gi...@apache.org>.
subbareddyalamur commented on code in PR #1143:
URL: https://github.com/apache/guacamole-client/pull/1143#discussion_r3108821513
##########
extensions/guacamole-vault/modules/guacamole-vault-openbao/src/main/java/org/apache/guacamole/vault/openbao/secret/OpenBaoClient.java:
##########
@@ -0,0 +1,164 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.guacamole.vault.openbao.secret;
+
+import com.google.gson.Gson;
+import com.google.gson.JsonObject;
+import com.google.inject.Inject;
+import org.apache.guacamole.GuacamoleException;
+import org.apache.guacamole.GuacamoleServerException;
+import org.apache.guacamole.vault.openbao.conf.OpenBaoConfigurationService;
+import org.apache.hc.client5.http.classic.methods.HttpGet;
+import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
+import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse;
+import org.apache.hc.client5.http.impl.classic.HttpClients;
+import org.apache.hc.core5.http.io.entity.EntityUtils;
+import org.apache.hc.core5.util.Timeout;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+
+/**
+ * Client for communicating with OpenBao REST API.
+ */
+public class OpenBaoClient {
+
+ /**
+ * Logger for this class.
+ */
+ private static final Logger logger = LoggerFactory.getLogger(OpenBaoClient.class);
+
+ /**
+ * Service for retrieving OpenBao configuration.
+ */
+ @Inject
+ private OpenBaoConfigurationService configService;
+
+ /**
+ * Gson instance for JSON parsing.
+ */
+ private final Gson gson = new Gson();
+
+ /**
+ * Retrieves a secret from OpenBao by username.
+ *
+ * @param username
+ * The Guacamole username to look up in OpenBao.
+ *
+ * @return
+ * The JSON response from OpenBao.
+ *
+ * @throws GuacamoleException
+ * If the secret cannot be retrieved from OpenBao.
+ */
+ public JsonObject getSecret(String username) throws GuacamoleException {
+
+ String serverUrl = configService.getServerUrl();
+ String token = configService.getToken();
+ String mountPath = configService.getMountPath();
+ String kvVersion = configService.getKvVersion();
+
+ // Build the API path based on KV version
+ // KV v2: /v1/{mount-path}/data/{username}
+ // KV v1: /v1/{mount-path}/{username}
+ String apiPath;
+ if ("2".equals(kvVersion)) {
+ apiPath = String.format("/v1/%s/data/%s", mountPath, username);
+ } else {
+ apiPath = String.format("/v1/%s/%s", mountPath, username);
+ }
+
+ String fullUrl = serverUrl + apiPath;
+
+ logger.info("Fetching secret from OpenBao: {}", fullUrl);
+
+ try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
+
+ HttpGet httpGet = new HttpGet(fullUrl);
+ httpGet.setHeader("X-Vault-Token", token);
+ httpGet.setHeader("Accept", "application/json");
+
+ // Set timeouts
+ httpGet.setConfig(org.apache.hc.client5.http.config.RequestConfig.custom()
+ .setConnectionRequestTimeout(Timeout.ofMilliseconds(configService.getConnectionTimeout()))
+ .setResponseTimeout(Timeout.ofMilliseconds(configService.getRequestTimeout()))
+ .build());
+
+ org.apache.hc.core5.http.ClassicHttpResponse response = httpClient.executeOpen(null, httpGet, null);
+ try {
+ int statusCode = response.getCode();
+ String responseBody = EntityUtils.toString(response.getEntity());
+
+ if (statusCode == 200) {
+ logger.info("OpenBao response status: {} - successfully retrieved password for {}", statusCode, username);
+ JsonObject jsonResponse = gson.fromJson(responseBody, JsonObject.class);
+ return jsonResponse;
+ } else if (statusCode == 404) {
+ logger.warn("Secret not found in OpenBao for username: {}", username);
+ throw new GuacamoleServerException("Secret not found in OpenBao for username: " + username);
+ } else if (statusCode == 403) {
+ logger.error("Permission denied accessing OpenBao. Check token permissions.");
+ throw new GuacamoleServerException("Permission denied accessing OpenBao. Check token permissions.");
+ } else {
+ logger.error("OpenBao returned error status {}: {}", statusCode, responseBody);
+ throw new GuacamoleServerException("OpenBao error (HTTP " + statusCode + "): " + responseBody);
+ }
+ } finally {
+ response.close();
+ }
+
+ } catch (IOException | org.apache.hc.core5.http.ParseException e) {
+ logger.error("Failed to communicate with OpenBao at {}: {}", fullUrl, e.getMessage());
+ throw new GuacamoleServerException("Failed to communicate with OpenBao", e);
+ }
+ }
+
+ /**
+ * Extracts the password field from an OpenBao KV v2 response.
+ *
+ * @param response
+ * The JSON response from OpenBao.
+ *
+ * @return
+ * The password string, or null if not found.
+ */
+ public String extractPassword(JsonObject response) {
+ try {
+ // For KV v2: response.data.data.password
+ if (response.has("data")) {
+ JsonObject data = response.getAsJsonObject("data");
+ if (data.has("data")) {
+ JsonObject innerData = data.getAsJsonObject("data");
+ if (innerData.has("password")) {
+ return innerData.get("password").getAsString();
+ }
+ }
+ }
+
+ logger.warn("Password field not found in OpenBao response");
+ return null;
+
+ } catch (Exception e) {
Review Comment:
Pushed a new commit @corentin-soriano
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: dev-unsubscribe@guacamole.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] GUACAMOLE-2196: OpenBao Vault Integration Extension [guacamole-client]
Posted by "adb014 (via GitHub)" <gi...@apache.org>.
adb014 commented on code in PR #1143:
URL: https://github.com/apache/guacamole-client/pull/1143#discussion_r3101193585
##########
extensions/guacamole-vault/modules/guacamole-vault-openbao/src/main/java/org/apache/guacamole/vault/openbao/secret/OpenBaoSecretService.java:
##########
@@ -0,0 +1,172 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.guacamole.vault.openbao.secret;
+
+import com.google.gson.JsonObject;
+import com.google.inject.Inject;
+import org.apache.guacamole.GuacamoleException;
+import org.apache.guacamole.net.auth.Connectable;
+import org.apache.guacamole.net.auth.UserContext;
+import org.apache.guacamole.protocol.GuacamoleConfiguration;
+import org.apache.guacamole.token.TokenFilter;
+import org.apache.guacamole.vault.secret.VaultSecretService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Map;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.Future;
+
+/**
+ * OpenBao implementation of VaultSecretService.
+ * Retrieves RDP passwords from OpenBao based on the logged-in Guacamole username.
+ */
+public class OpenBaoSecretService implements VaultSecretService {
+
+ /**
+ * Logger for this class.
+ */
+ private static final Logger logger = LoggerFactory.getLogger(OpenBaoSecretService.class);
+
+ /**
+ * Client for communicating with OpenBao.
+ */
+ @Inject
+ private OpenBaoClient openBaoClient;
+
+ /**
+ * Constructor that logs when the service is created.
+ */
+ public OpenBaoSecretService() {
+ logger.info("OpenBaoSecretService initialized");
+ }
+
+ /**
+ * The token pattern for OpenBao secrets: ${OPENBAO_SECRET}
+ */
+ public static final String OPENBAO_SECRET_TOKEN = "${OPENBAO_SECRET}";
+
+ /**
+ * The token pattern for Guacamole username: ${GUAC_USERNAME}
+ */
+ public static final String GUAC_USERNAME_TOKEN = "${GUAC_USERNAME}";
+
+ @Override
+ public String canonicalize(String token) {
+ // Return the canonical form for tokens we recognize
+ if (token == null)
+ return null;
+
+ // Remove ${} wrapper and return just the token name
+ if (OPENBAO_SECRET_TOKEN.equals(token)) {
+ return "OPENBAO_SECRET";
+ }
+
+ if (GUAC_USERNAME_TOKEN.equals(token)) {
+ return "GUAC_USERNAME";
+ }
+
+ // Not our token
+ return null;
+ }
+
+ @Override
+ public Future<String> getValue(String token) throws GuacamoleException {
+ // This method is called for simple token lookups without user context
+ logger.warn("getValue(String) called without user context - cannot determine username");
+ return CompletableFuture.completedFuture(null);
+ }
+
+ @Override
+ public Future<String> getValue(UserContext userContext, Connectable connectable, String token)
+ throws GuacamoleException {
+
+ logger.info("getValue() called with token: {}", token);
+
+ // Get the logged-in Guacamole username
+ String username = userContext.self().getIdentifier();
+
+ // Handle GUAC_USERNAME token - return the Guacamole username
+ if ("GUAC_USERNAME".equals(token)) {
+ logger.info("getValue() returning username: '{}'", username);
+ return CompletableFuture.completedFuture(username);
+ }
+
+ // Handle OPENBAO_SECRET token - fetch password from OpenBao
+ if ("OPENBAO_SECRET".equals(token)) {
+ logger.info("Retrieving OpenBao secret for username: {}", username);
+
+ try {
+ // Fetch the secret from OpenBao using the username
+ JsonObject response = openBaoClient.getSecret(username);
+
+ // Extract the password field
+ String password = openBaoClient.extractPassword(response);
+
+ if (password != null) {
+ logger.info("Successfully retrieved password from OpenBao for user: {} (length: {})", username, password.length());
+ return CompletableFuture.completedFuture(password);
+ } else {
+ logger.warn("Password field not found in OpenBao for user: {}", username);
+ return CompletableFuture.completedFuture(null);
+ }
+
+ } catch (GuacamoleException e) {
+ logger.error("Failed to retrieve secret from OpenBao for user: {}", username, e);
+ // Return null instead of throwing to allow connection attempt with empty password
+ return CompletableFuture.completedFuture(null);
+ }
+ }
+
+ // Not a recognized token
+ logger.warn("Token '{}' not recognized, returning null", token);
+ return CompletableFuture.completedFuture(null);
+ }
+
+ @Override
+ public Map<String, Future<String>> getTokens(UserContext userContext,
+ Connectable connectable,
+ GuacamoleConfiguration config,
+ TokenFilter tokenFilter) throws GuacamoleException {
+
+ Map<String, Future<String>> tokens = new java.util.HashMap<>();
+ String username = userContext.self().getIdentifier();
+
+ // Add GUAC_USERNAME token (always available)
+ tokens.put("GUAC_USERNAME", CompletableFuture.completedFuture(username));
+
+ // Add OPENBAO_SECRET token (fetch from OpenBao)
Review Comment:
If you are using org.springramework.vault this should use the VaultKeyValueOperations.list function to first obtain a list of all of the keys under a specific mount point and then obtain a Map<String,String> of the keys like
```java
Map<String, String> secrets = new HashMap<>();
for (String key : keys) {
VaultKeyValueOperationsSupport.VaultResponse response =
kv.get("myapp/" + key);
if (response != null && response.getData() != null) {
Object value = response.getData().get("value");
if (value != null) {
secrets.put(key, value.toString());
}
}
}
```
where keys and the lists keys and "kv" is your VaultKeyValueOperations instantiated with your server uri, token and mount path
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: dev-unsubscribe@guacamole.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] GUACAMOLE-2196: OpenBao Vault Integration Extension [guacamole-client]
Posted by "adb014 (via GitHub)" <gi...@apache.org>.
adb014 commented on code in PR #1143:
URL: https://github.com/apache/guacamole-client/pull/1143#discussion_r3101193585
##########
extensions/guacamole-vault/modules/guacamole-vault-openbao/src/main/java/org/apache/guacamole/vault/openbao/secret/OpenBaoSecretService.java:
##########
@@ -0,0 +1,172 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.guacamole.vault.openbao.secret;
+
+import com.google.gson.JsonObject;
+import com.google.inject.Inject;
+import org.apache.guacamole.GuacamoleException;
+import org.apache.guacamole.net.auth.Connectable;
+import org.apache.guacamole.net.auth.UserContext;
+import org.apache.guacamole.protocol.GuacamoleConfiguration;
+import org.apache.guacamole.token.TokenFilter;
+import org.apache.guacamole.vault.secret.VaultSecretService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Map;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.Future;
+
+/**
+ * OpenBao implementation of VaultSecretService.
+ * Retrieves RDP passwords from OpenBao based on the logged-in Guacamole username.
+ */
+public class OpenBaoSecretService implements VaultSecretService {
+
+ /**
+ * Logger for this class.
+ */
+ private static final Logger logger = LoggerFactory.getLogger(OpenBaoSecretService.class);
+
+ /**
+ * Client for communicating with OpenBao.
+ */
+ @Inject
+ private OpenBaoClient openBaoClient;
+
+ /**
+ * Constructor that logs when the service is created.
+ */
+ public OpenBaoSecretService() {
+ logger.info("OpenBaoSecretService initialized");
+ }
+
+ /**
+ * The token pattern for OpenBao secrets: ${OPENBAO_SECRET}
+ */
+ public static final String OPENBAO_SECRET_TOKEN = "${OPENBAO_SECRET}";
+
+ /**
+ * The token pattern for Guacamole username: ${GUAC_USERNAME}
+ */
+ public static final String GUAC_USERNAME_TOKEN = "${GUAC_USERNAME}";
+
+ @Override
+ public String canonicalize(String token) {
+ // Return the canonical form for tokens we recognize
+ if (token == null)
+ return null;
+
+ // Remove ${} wrapper and return just the token name
+ if (OPENBAO_SECRET_TOKEN.equals(token)) {
+ return "OPENBAO_SECRET";
+ }
+
+ if (GUAC_USERNAME_TOKEN.equals(token)) {
+ return "GUAC_USERNAME";
+ }
+
+ // Not our token
+ return null;
+ }
+
+ @Override
+ public Future<String> getValue(String token) throws GuacamoleException {
+ // This method is called for simple token lookups without user context
+ logger.warn("getValue(String) called without user context - cannot determine username");
+ return CompletableFuture.completedFuture(null);
+ }
+
+ @Override
+ public Future<String> getValue(UserContext userContext, Connectable connectable, String token)
+ throws GuacamoleException {
+
+ logger.info("getValue() called with token: {}", token);
+
+ // Get the logged-in Guacamole username
+ String username = userContext.self().getIdentifier();
+
+ // Handle GUAC_USERNAME token - return the Guacamole username
+ if ("GUAC_USERNAME".equals(token)) {
+ logger.info("getValue() returning username: '{}'", username);
+ return CompletableFuture.completedFuture(username);
+ }
+
+ // Handle OPENBAO_SECRET token - fetch password from OpenBao
+ if ("OPENBAO_SECRET".equals(token)) {
+ logger.info("Retrieving OpenBao secret for username: {}", username);
+
+ try {
+ // Fetch the secret from OpenBao using the username
+ JsonObject response = openBaoClient.getSecret(username);
+
+ // Extract the password field
+ String password = openBaoClient.extractPassword(response);
+
+ if (password != null) {
+ logger.info("Successfully retrieved password from OpenBao for user: {} (length: {})", username, password.length());
+ return CompletableFuture.completedFuture(password);
+ } else {
+ logger.warn("Password field not found in OpenBao for user: {}", username);
+ return CompletableFuture.completedFuture(null);
+ }
+
+ } catch (GuacamoleException e) {
+ logger.error("Failed to retrieve secret from OpenBao for user: {}", username, e);
+ // Return null instead of throwing to allow connection attempt with empty password
+ return CompletableFuture.completedFuture(null);
+ }
+ }
+
+ // Not a recognized token
+ logger.warn("Token '{}' not recognized, returning null", token);
+ return CompletableFuture.completedFuture(null);
+ }
+
+ @Override
+ public Map<String, Future<String>> getTokens(UserContext userContext,
+ Connectable connectable,
+ GuacamoleConfiguration config,
+ TokenFilter tokenFilter) throws GuacamoleException {
+
+ Map<String, Future<String>> tokens = new java.util.HashMap<>();
+ String username = userContext.self().getIdentifier();
+
+ // Add GUAC_USERNAME token (always available)
+ tokens.put("GUAC_USERNAME", CompletableFuture.completedFuture(username));
+
+ // Add OPENBAO_SECRET token (fetch from OpenBao)
Review Comment:
If you are using org.springramework.vault this should use the VaultKeyValueOperations.list function to first obtain a list of all of the keys under a specific mount point and then obtain a Map<String,String> of the keys. If you add a method
```java
public List<String> listKeys(String path) {
return kvOperations.list(path);
}
```
to my proposed replacement for OpenBaoClient, then you could do something like
like
```java
List<Strings> keys = OpenBaoClient.listKeys("");
Map<String, String> secrets = new HashMap<>();
for (String key : keys) {
Map<String,Object> response = openBaoClient.getSecret(key);
String password = openBaoClient.extractPassword(response);
if (response != null && response.getData() != null) {
Object value = response.getData().get("value");
if (value != null) {
secrets.put(key, value.toString());
}
}
}
```
where keys and the lists keys
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: dev-unsubscribe@guacamole.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] GUACAMOLE-2196: OpenBao Vault Integration Extension [guacamole-client]
Posted by "adb014 (via GitHub)" <gi...@apache.org>.
adb014 commented on code in PR #1143:
URL: https://github.com/apache/guacamole-client/pull/1143#discussion_r3092850782
##########
extensions/guacamole-vault/modules/guacamole-vault-openbao/src/main/java/org/apache/guacamole/vault/openbao/conf/OpenBaoConfigurationService.java:
##########
@@ -0,0 +1,122 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.guacamole.vault.openbao.conf;
+
+import com.google.inject.Inject;
+import org.apache.guacamole.GuacamoleException;
+import org.apache.guacamole.environment.Environment;
+import org.apache.guacamole.vault.conf.VaultConfigurationService;
+
+/**
+ * Service for retrieving OpenBao configuration from guacamole.properties.
+ */
+public class OpenBaoConfigurationService extends VaultConfigurationService {
+
+ /**
+ * The Guacamole server environment.
+ */
+ @Inject
+ private Environment environment;
+
+ /**
+ * Creates a new OpenBaoConfigurationService.
+ */
+ public OpenBaoConfigurationService() {
+ super("openbao-token-mapping.yml", "guacamole.properties.openbao");
+ }
+
+ /**
+ * Returns the OpenBao server URL.
+ *
+ * @return The OpenBao server URL (e.g., "http://localhost:8200").
+ * @throws GuacamoleException
+ * If the property is not defined in guacamole.properties.
+ */
+ public String getServerUrl() throws GuacamoleException {
+ return environment.getRequiredProperty(OpenBaoConfig.OPENBAO_SERVER_URL);
+ }
+
+ /**
+ * Returns the OpenBao authentication token.
+ *
+ * @return The OpenBao authentication token.
+ * @throws GuacamoleException
+ * If the property is not defined in guacamole.properties.
+ */
+ public String getToken() throws GuacamoleException {
Review Comment:
Wouldn’t it be better to use an openbao roleID and secretID to obtain the token rather than supply it directly? The token typically has a shorter lifetime
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: dev-unsubscribe@guacamole.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] GUACAMOLE-2196: OpenBao Vault Integration Extension [guacamole-client]
Posted by "subbareddyalamur (via GitHub)" <gi...@apache.org>.
subbareddyalamur commented on code in PR #1143:
URL: https://github.com/apache/guacamole-client/pull/1143#discussion_r3108726467
##########
extensions/guacamole-vault/modules/guacamole-vault-openbao/src/main/java/org/apache/guacamole/vault/openbao/secret/OpenBaoClient.java:
##########
@@ -0,0 +1,164 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.guacamole.vault.openbao.secret;
+
+import com.google.gson.Gson;
+import com.google.gson.JsonObject;
+import com.google.inject.Inject;
+import org.apache.guacamole.GuacamoleException;
+import org.apache.guacamole.GuacamoleServerException;
+import org.apache.guacamole.vault.openbao.conf.OpenBaoConfigurationService;
+import org.apache.hc.client5.http.classic.methods.HttpGet;
+import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
+import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse;
+import org.apache.hc.client5.http.impl.classic.HttpClients;
+import org.apache.hc.core5.http.io.entity.EntityUtils;
+import org.apache.hc.core5.util.Timeout;
Review Comment:
Thanks for the detailed proposal — the Spring Vault version is definitely cleaner for future feature work like AppRole or KV listing.
For this initial contribution I'd like to keep the footprint minimal: the current hand-rolled client only needs httpclient5 + gson (both already familiar dependencies in the Guacamole tree) and doesn't pull in the Spring runtime. Moving to org.springframework.vault is a larger decision that touches dependency policy, so I'd rather propose it as a follow-up PR where it can be discussed on its own merits.
I have, however, addressed the underlying concerns that motivated the suggestion: the HttpClient is now constructed once and reused, exceptions are caught more narrowly, and AppRole authentication is supported.
@adb014
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: dev-unsubscribe@guacamole.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] GUACAMOLE-2196: OpenBao Vault Integration Extension [guacamole-client]
Posted by "adb014 (via GitHub)" <gi...@apache.org>.
adb014 commented on code in PR #1143:
URL: https://github.com/apache/guacamole-client/pull/1143#discussion_r3092105851
##########
extensions/guacamole-vault/modules/guacamole-vault-openbao/src/main/java/org/apache/guacamole/vault/openbao/conf/OpenBaoConfig.java:
##########
@@ -0,0 +1,64 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.guacamole.vault.openbao.conf;
+
+import org.apache.guacamole.properties.StringGuacamoleProperty;
+
+/**
+ * Configuration properties for OpenBao vault integration.
+ */
+public class OpenBaoConfig {
+
+ /**
+ * OpenBao server URL (e.g., "http://localhost:8200").
+ * This property is REQUIRED and must be configured in guacamole.properties.
+ */
+ public static final StringGuacamoleProperty OPENBAO_SERVER_URL =
+ new StringGuacamoleProperty() {
Review Comment:
Shouldn't this return a URIGuacamoleProperty
##########
extensions/guacamole-vault/modules/guacamole-vault-openbao/src/main/java/org/apache/guacamole/vault/openbao/secret/OpenBaoClient.java:
##########
@@ -0,0 +1,164 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.guacamole.vault.openbao.secret;
+
+import com.google.gson.Gson;
+import com.google.gson.JsonObject;
+import com.google.inject.Inject;
+import org.apache.guacamole.GuacamoleException;
+import org.apache.guacamole.GuacamoleServerException;
+import org.apache.guacamole.vault.openbao.conf.OpenBaoConfigurationService;
+import org.apache.hc.client5.http.classic.methods.HttpGet;
+import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
+import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse;
+import org.apache.hc.client5.http.impl.classic.HttpClients;
+import org.apache.hc.core5.http.io.entity.EntityUtils;
+import org.apache.hc.core5.util.Timeout;
Review Comment:
Wouldn't it be better to use "org.springframework.vault" than writing all the code to communicate with openbao yourself ?
##########
extensions/guacamole-vault/modules/guacamole-vault-openbao/src/main/java/org/apache/guacamole/vault/openbao/conf/OpenBaoConfigurationService.java:
##########
@@ -0,0 +1,122 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.guacamole.vault.openbao.conf;
+
+import com.google.inject.Inject;
+import org.apache.guacamole.GuacamoleException;
+import org.apache.guacamole.environment.Environment;
+import org.apache.guacamole.vault.conf.VaultConfigurationService;
+
+/**
+ * Service for retrieving OpenBao configuration from guacamole.properties.
+ */
+public class OpenBaoConfigurationService extends VaultConfigurationService {
+
+ /**
+ * The Guacamole server environment.
+ */
+ @Inject
+ private Environment environment;
+
+ /**
+ * Creates a new OpenBaoConfigurationService.
+ */
+ public OpenBaoConfigurationService() {
+ super("openbao-token-mapping.yml", "guacamole.properties.openbao");
+ }
+
+ /**
+ * Returns the OpenBao server URL.
+ *
+ * @return The OpenBao server URL (e.g., "http://localhost:8200").
+ * @throws GuacamoleException
+ * If the property is not defined in guacamole.properties.
+ */
+ public String getServerUrl() throws GuacamoleException {
Review Comment:
Shouldn't this return a "java.net.URI"
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: dev-unsubscribe@guacamole.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] GUACAMOLE-2196: OpenBao Vault Integration Extension [guacamole-client]
Posted by "adb014 (via GitHub)" <gi...@apache.org>.
adb014 commented on code in PR #1143:
URL: https://github.com/apache/guacamole-client/pull/1143#discussion_r3092583947
##########
extensions/guacamole-vault/modules/guacamole-vault-openbao/src/main/java/org/apache/guacamole/vault/openbao/secret/OpenBaoClient.java:
##########
@@ -0,0 +1,164 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.guacamole.vault.openbao.secret;
+
+import com.google.gson.Gson;
+import com.google.gson.JsonObject;
+import com.google.inject.Inject;
+import org.apache.guacamole.GuacamoleException;
+import org.apache.guacamole.GuacamoleServerException;
+import org.apache.guacamole.vault.openbao.conf.OpenBaoConfigurationService;
+import org.apache.hc.client5.http.classic.methods.HttpGet;
+import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
+import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse;
+import org.apache.hc.client5.http.impl.classic.HttpClients;
+import org.apache.hc.core5.http.io.entity.EntityUtils;
+import org.apache.hc.core5.util.Timeout;
Review Comment:
Something like
```java
package org.apache.guacamole.vault.openbao.secret;
import javax.inject.Inject;
import javax.inject.Singleton;
import java.net.URI;
import java.time.Duration;
import java.util.Map;
import org.apache.guacamole.GuacamoleException;
import org.apache.guacamole.GuacamoleServerException;
import org.apache.guacamole.vault.openbao.conf.OpenBaoConfigurationService;
import org.springframework.vault.VaultException;
import org.springframework.vault.authentication.TokenAuthentication;
import org.springframework.vault.client.VaultEndpoint;
import org.springframework.vault.core.VaultKeyValueOperations;
import org.springframework.vault.core.VaultTemplate;
import org.springframework.vault.support.VaultResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class OpenBaoClient {
/**
* Logger for this class.
*/
private static final Logger logger = LoggerFactory.getLogger(OpenBaoClient.class);
/**
* Service for retrieving OpenBao configuration.
*/
@Inject
private OpenBaoConfigurationService configService;
/**
* Vault Key/Value object to retrieve values based on their key
*/
private final VaultKeyValueOperations kvOperations;
/**
* KV.1 ou KV.2 template of teh mount mount
*/
private final VaultTemplate vaultTemplate;
/**
* Complete the instantiantion of the class after injection of confService
*/
@Inject
public init() {
VaultEndpoint endpoint = VaultEndpoint.from(configService.getServerUrl())
);
endpoint.setConnectionTimeout(
Duration.ofMillis(configService.getConnectionTimeout()));
endpoint.setReadTimeout(
Duration.ofMillis(configService.getRequestTimeout()));
this.vaultTemplate = new VaultTemplate(
endpoint,
new TokenAuthentication(configService.getToken())
);
String mountPath = configService.getMountPath();
String kvVersion = configService.getKvVersion();
if ("2".equals(kvVersion)) {
this.kvOperations = vaultTemplate.opsForKeyValue(
mountPath,
VaultKeyValueOperations.KeyValueBackend.KV_2);
}
else {
this.kvOperations = vaultTemplate.opsForKeyValue(
mountPath,
VaultKeyValueOperations.KeyValueBackend.KV_1);
}
}
/**
* Retrieves a secret from OpenBao by username.
*
* @param username
* The Guacamole username to look up in OpenBao.
*
* @return
* The JSON response from OpenBao in the form of a Map.
*
* @throws GuacamoleException
* If the secret cannot be retrieved from OpenBao.
*/
public Map<String, Object> getSecret(String username)
throws GuacamoleException {
try {
logger.info("Fetching secret from OpenBao: key={}", username);
VaultResponse response = kvOperations.get(username);
if (response == null || response.getData() == null) {
throw new GuacamoleServerException(
"Secret not found in OpenBao for username: " + username);
}
return response.getData();
} catch (VaultException e) {
logger.error("Failed to retrieve secret from OpenBao", e);
throw new GuacamoleServerException(
"Failed to retrieve secret from OpenBao", e);
}
}
/**
* Extracts the password field from an OpenBao KV v2 response.
*
* @param response
* The JSON response from OpenBao in the form of a Map<String,Object>.
*
* @return
* The password string, or null if not found.
*/
public String extractPassword(Map<String, Object> secretData) {
if (secretData == null) {
return null;
}
Object password = secretData.get("password");
if (password instanceof String) {
return (String) password;
}
logger.warn("Password field not found in OpenBao secret");
return null;
}
}
```
but completely untested could reimplement your getSecret and extractPassword methods using a Map rather than a JsonObject
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: dev-unsubscribe@guacamole.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] GUACAMOLE-2196: OpenBao Vault Integration Extension [guacamole-client]
Posted by "adb014 (via GitHub)" <gi...@apache.org>.
adb014 commented on code in PR #1143:
URL: https://github.com/apache/guacamole-client/pull/1143#discussion_r3092583947
##########
extensions/guacamole-vault/modules/guacamole-vault-openbao/src/main/java/org/apache/guacamole/vault/openbao/secret/OpenBaoClient.java:
##########
@@ -0,0 +1,164 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.guacamole.vault.openbao.secret;
+
+import com.google.gson.Gson;
+import com.google.gson.JsonObject;
+import com.google.inject.Inject;
+import org.apache.guacamole.GuacamoleException;
+import org.apache.guacamole.GuacamoleServerException;
+import org.apache.guacamole.vault.openbao.conf.OpenBaoConfigurationService;
+import org.apache.hc.client5.http.classic.methods.HttpGet;
+import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
+import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse;
+import org.apache.hc.client5.http.impl.classic.HttpClients;
+import org.apache.hc.core5.http.io.entity.EntityUtils;
+import org.apache.hc.core5.util.Timeout;
Review Comment:
Something like
```java
package org.apache.guacamole.vault.openbao.secret;
import javax.inject.Inject;
import javax.inject.Singleton;
import java.net.URI;
import java.time.Duration;
import java.util.Map;
import org.apache.guacamole.GuacamoleException;
import org.apache.guacamole.GuacamoleServerException;
import org.apache.guacamole.vault.openbao.conf.OpenBaoConfigurationService;
import org.springframework.vault.VaultException;
import org.springframework.vault.authentication.TokenAuthentication;
import org.springframework.vault.client.VaultEndpoint;
import org.springframework.vault.core.VaultKeyValueOperations;
import org.springframework.vault.core.VaultTemplate;
import org.springframework.vault.support.VaultResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class OpenBaoClient {
/**
* Logger for this class.
*/
private static final Logger logger = LoggerFactory.getLogger(OpenBaoClient.class);
/**
* Service for retrieving OpenBao configuration.
*/
@Inject
private OpenBaoConfigurationService configService;
/**
* Vault Key/Value object to retrieve values based on their key
*/
private final VaultKeyValueOperations kvOperations;
/**
* KV.1 ou KV.2 template of teh mount mount
*/
private final VaultTemplate vaultTemplate;
/**
* Complete the instantiantion of the class after injection of confService
*/
@Inject
public init() {
VaultEndpoint endpoint = VaultEndpoint.from(configService.getServerUrl())
);
endpoint.setConnectionTimeout(
Duration.ofMillis(configService.getConnectionTimeout()));
endpoint.setReadTimeout(
Duration.ofMillis(configService.getRequestTimeout()));
this.vaultTemplate = new VaultTemplate(
endpoint,
new TokenAuthentication(configService.getToken())
);
String mountPath = configService.getMountPath();
String kvVersion = configService.getKvVersion();
if ("2".equals(kvVersion)) {
this.kvOperations = vaultTemplate.opsForKeyValue(
mountPath,
VaultKeyValueOperations.KeyValueBackend.KV_2);
}
else {
this.kvOperations = vaultTemplate.opsForKeyValue(
mountPath,
VaultKeyValueOperations.KeyValueBackend.KV_1);
}
}
/**
* Retrieves a secret from OpenBao by username.
*
* @param username
* The Guacamole username to look up in OpenBao.
*
* @return
* The JSON response from OpenBao in the form of a Map.
*
* @throws GuacamoleException
* If the secret cannot be retrieved from OpenBao.
*/
public Map<String, Object> getSecret(String username)
throws GuacamoleException {
try {
logger.info("Fetching secret from OpenBao: key={}", username);
VaultResponse response = kvOperations.get(username);
if (response == null || response.getData() == null) {
throw new GuacamoleServerException(
"Secret not found in OpenBao for username: " + username);
}
return response.getData();
} catch (VaultException e) {
logger.error("Failed to retrieve secret from OpenBao", e);
throw new GuacamoleServerException(
"Failed to retrieve secret from OpenBao", e);
}
}
/**
* Extracts the password field from an OpenBao KV v2 response.
*
* @param response
* The JSON response from OpenBao in the form of a Map<String,Object>.
*
* @return
* The password string, or null if not found.
*/
public String extractPassword(Map<String, Object> sresponse) {
if (response == null) {
return null;
}
Object password = response.get("password");
if (password instanceof String) {
return (String) password;
}
logger.warn("Password field not found in OpenBao secret");
return null;
}
}
```
but completely untested could reimplement your getSecret and extractPassword methods using a Map rather than a JsonObject
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: dev-unsubscribe@guacamole.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] GUACAMOLE-2196: OpenBao Vault Integration Extension [guacamole-client]
Posted by "adb014 (via GitHub)" <gi...@apache.org>.
adb014 commented on code in PR #1143:
URL: https://github.com/apache/guacamole-client/pull/1143#discussion_r3092583947
##########
extensions/guacamole-vault/modules/guacamole-vault-openbao/src/main/java/org/apache/guacamole/vault/openbao/secret/OpenBaoClient.java:
##########
@@ -0,0 +1,164 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.guacamole.vault.openbao.secret;
+
+import com.google.gson.Gson;
+import com.google.gson.JsonObject;
+import com.google.inject.Inject;
+import org.apache.guacamole.GuacamoleException;
+import org.apache.guacamole.GuacamoleServerException;
+import org.apache.guacamole.vault.openbao.conf.OpenBaoConfigurationService;
+import org.apache.hc.client5.http.classic.methods.HttpGet;
+import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
+import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse;
+import org.apache.hc.client5.http.impl.classic.HttpClients;
+import org.apache.hc.core5.http.io.entity.EntityUtils;
+import org.apache.hc.core5.util.Timeout;
Review Comment:
Something like
```java
package org.apache.guacamole.vault.openbao.secret;
import java.io.IOException;
import java.util.Map;
import org.springframework.vault.authentication.ClientAuthentication;
import org.springframework.vault.authentication.TokenAuthentication;
import org.springframework.vault.core.VaultTemplate;
import org.springframework.vault.core.VaultKeyValueOperations;
import org.springframework.vault.core.VaultKeyValueOperationsSupport.KeyValueBackend;
import org.springframework.web.client.RestTemplate;
import org.springframework.vault.client.VaultEndpoint;
import org.springframework.web.client.RestTemplate;
import org.springframework.vault.authentication.ClientAuthentication;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Client for communicating with OpenBao REST API.
*/
public class OpenBaoClient {
/**
* Logger for this class.
*/
private static final Logger logger = LoggerFactory.getLogger(OpenBaoClient.class);
/**
* Service for retrieving OpenBao configuration.
*/
@Inject
private OpenBaoConfigurationService configService;
/**
* Vault Key/Value object to retrieve values based on their key
*/
private final VaultKeyValueOperations kv;
/**
* On instantiation connect to the OpenBao server and create a
* ValutKeyValueOperation
*/
public OpenBaoClient() {
// VaultEndpoint.from can take a java.net.URI or string
VaultEndpoint endpoint = VaultEndpoint.from(config.getServerUri());
RestTemplate rest = new RestTemplate();
ClientAuthentication auth = new TokenAuthentication(config.getToken());
VaultTemplate template = new VaultTemplate(endpoint, rest, auth);
this.kv = template.opsForKeyValue(
config.getMountPath(),
KeyValueBackend.KV_2
);
}
public Map<String, Object> getSecret(String path) {
return kv.get(path).getData();
}
}
```
but completely untested could reimplement your getSecret method returning a Map rather than a JsonObject
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: dev-unsubscribe@guacamole.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] GUACAMOLE-2196: OpenBao Vault Integration Extension [guacamole-client]
Posted by "adb014 (via GitHub)" <gi...@apache.org>.
adb014 commented on PR #1143:
URL: https://github.com/apache/guacamole-client/pull/1143#issuecomment-4259965302
Shouldn't This merge with the PR #2137 as Hashicorp and OpenBao use essentially the same API
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: dev-unsubscribe@guacamole.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] GUACAMOLE-2196: OpenBao Vault Integration Extension [guacamole-client]
Posted by "adb014 (via GitHub)" <gi...@apache.org>.
adb014 commented on code in PR #1143:
URL: https://github.com/apache/guacamole-client/pull/1143#discussion_r3101193585
##########
extensions/guacamole-vault/modules/guacamole-vault-openbao/src/main/java/org/apache/guacamole/vault/openbao/secret/OpenBaoSecretService.java:
##########
@@ -0,0 +1,172 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.guacamole.vault.openbao.secret;
+
+import com.google.gson.JsonObject;
+import com.google.inject.Inject;
+import org.apache.guacamole.GuacamoleException;
+import org.apache.guacamole.net.auth.Connectable;
+import org.apache.guacamole.net.auth.UserContext;
+import org.apache.guacamole.protocol.GuacamoleConfiguration;
+import org.apache.guacamole.token.TokenFilter;
+import org.apache.guacamole.vault.secret.VaultSecretService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Map;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.Future;
+
+/**
+ * OpenBao implementation of VaultSecretService.
+ * Retrieves RDP passwords from OpenBao based on the logged-in Guacamole username.
+ */
+public class OpenBaoSecretService implements VaultSecretService {
+
+ /**
+ * Logger for this class.
+ */
+ private static final Logger logger = LoggerFactory.getLogger(OpenBaoSecretService.class);
+
+ /**
+ * Client for communicating with OpenBao.
+ */
+ @Inject
+ private OpenBaoClient openBaoClient;
+
+ /**
+ * Constructor that logs when the service is created.
+ */
+ public OpenBaoSecretService() {
+ logger.info("OpenBaoSecretService initialized");
+ }
+
+ /**
+ * The token pattern for OpenBao secrets: ${OPENBAO_SECRET}
+ */
+ public static final String OPENBAO_SECRET_TOKEN = "${OPENBAO_SECRET}";
+
+ /**
+ * The token pattern for Guacamole username: ${GUAC_USERNAME}
+ */
+ public static final String GUAC_USERNAME_TOKEN = "${GUAC_USERNAME}";
+
+ @Override
+ public String canonicalize(String token) {
+ // Return the canonical form for tokens we recognize
+ if (token == null)
+ return null;
+
+ // Remove ${} wrapper and return just the token name
+ if (OPENBAO_SECRET_TOKEN.equals(token)) {
+ return "OPENBAO_SECRET";
+ }
+
+ if (GUAC_USERNAME_TOKEN.equals(token)) {
+ return "GUAC_USERNAME";
+ }
+
+ // Not our token
+ return null;
+ }
+
+ @Override
+ public Future<String> getValue(String token) throws GuacamoleException {
+ // This method is called for simple token lookups without user context
+ logger.warn("getValue(String) called without user context - cannot determine username");
+ return CompletableFuture.completedFuture(null);
+ }
+
+ @Override
+ public Future<String> getValue(UserContext userContext, Connectable connectable, String token)
+ throws GuacamoleException {
+
+ logger.info("getValue() called with token: {}", token);
+
+ // Get the logged-in Guacamole username
+ String username = userContext.self().getIdentifier();
+
+ // Handle GUAC_USERNAME token - return the Guacamole username
+ if ("GUAC_USERNAME".equals(token)) {
+ logger.info("getValue() returning username: '{}'", username);
+ return CompletableFuture.completedFuture(username);
+ }
+
+ // Handle OPENBAO_SECRET token - fetch password from OpenBao
+ if ("OPENBAO_SECRET".equals(token)) {
+ logger.info("Retrieving OpenBao secret for username: {}", username);
+
+ try {
+ // Fetch the secret from OpenBao using the username
+ JsonObject response = openBaoClient.getSecret(username);
+
+ // Extract the password field
+ String password = openBaoClient.extractPassword(response);
+
+ if (password != null) {
+ logger.info("Successfully retrieved password from OpenBao for user: {} (length: {})", username, password.length());
+ return CompletableFuture.completedFuture(password);
+ } else {
+ logger.warn("Password field not found in OpenBao for user: {}", username);
+ return CompletableFuture.completedFuture(null);
+ }
+
+ } catch (GuacamoleException e) {
+ logger.error("Failed to retrieve secret from OpenBao for user: {}", username, e);
+ // Return null instead of throwing to allow connection attempt with empty password
+ return CompletableFuture.completedFuture(null);
+ }
+ }
+
+ // Not a recognized token
+ logger.warn("Token '{}' not recognized, returning null", token);
+ return CompletableFuture.completedFuture(null);
+ }
+
+ @Override
+ public Map<String, Future<String>> getTokens(UserContext userContext,
+ Connectable connectable,
+ GuacamoleConfiguration config,
+ TokenFilter tokenFilter) throws GuacamoleException {
+
+ Map<String, Future<String>> tokens = new java.util.HashMap<>();
+ String username = userContext.self().getIdentifier();
+
+ // Add GUAC_USERNAME token (always available)
+ tokens.put("GUAC_USERNAME", CompletableFuture.completedFuture(username));
+
+ // Add OPENBAO_SECRET token (fetch from OpenBao)
Review Comment:
If you are using org.springramework.vault this should use the VaultKeyValueOperations.list function to first obtain a list of all of the keys under a specific mount point and then obtain a Map<String,String> of the keys like
```java
Map<String, String> secrets = new HashMap<>();
for (String key : keys) {
Map<String,Object> response = openBaoClient.getSecret(key);
String password = openBaoClient.extractPassword(response);
if (response != null && response.getData() != null) {
Object value = response.getData().get("value");
if (value != null) {
secrets.put(key, value.toString());
}
}
}
```
where keys and the lists keys
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: dev-unsubscribe@guacamole.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org