Hashicorp Vault
Configuring a Hashicorp Vault OAuth integration in Posit Connect allows content to access secrets stored in Vault without long-lived credentials. This integration uses Workload Identity Federation to authenticate content to Vault using JSON Web Tokens (JWTs) issued by Connect.
Configuring this integration involves two main steps, each performed by different administrators:
- Step 1: A Vault administrator configures JWT authentication and creates appropriate policies and roles.
- Step 2: A Posit Connect administrator creates an OAuth integration within Connect.
This guide walks through both steps of this process.
Step 1: Vault administrator
Understanding the authentication flow
When content runs on Connect with a Vault integration:
- Connect issues a JWT (content session token) to the running content
- The content exchanges this token with Connect for an OIDC-compliant JWT with a
vaultaudience - Connect-session (the content supervisor) authenticates to Vault using this JWT
- Vault validates the JWT against Connect’s OIDC discovery endpoint
- Vault issues a client token that the content can use to access secrets
Prerequisites
- Vault server (Community Edition, Enterprise, or HCP Vault)
- Administrative access to configure authentication methods and policies
- Network connectivity from Connect content processes to the Vault server
- Network connectivity from Vault to Connect’s OIDC discovery endpoints
Configure JWT authentication
First, enable a JWT authentication backend for Connect:
Terminal
# Set your Connect server address (include trailing slash)
export CONNECT_SERVER_ADDRESS="https://connect.example.org/"
# Name for the JWT auth backend path
export CONNECT_JWT_PATH="jwt-posit-connect"
# Enable JWT auth backend
vault auth enable -path=$CONNECT_JWT_PATH -description="Posit Connect" jwt
# Configure the JWT backend to trust Connect as an identity provider
vault write auth/${CONNECT_JWT_PATH}/config \
oidc_discovery_url="$CONNECT_SERVER_ADDRESS" \
bound_issuer="$CONNECT_SERVER_ADDRESS"Connect normalizes the issuer claim in its tokens to include a trailing slash on the server URL. Ensure CONNECT_SERVER_ADDRESS includes the trailing slash, otherwise Vault will encounter errors like error checking oidc discovery URL.
Create policies
Create a policy that defines what secrets the Connect integration can access:
Terminal
# Policy name - can be anything descriptive
export CONNECT_POLICY="posit-connect-secrets"
# Example: Allow read access to secrets under the "connect/" path in a KV v2 engine
vault policy write ${CONNECT_POLICY} - <<EOF
path "secret/data/connect/*" {
capabilities = ["read"]
}
EOFAdjust the policy paths and capabilities according to your security requirements.
Create a role
Create a role that maps Connect’s JWT claims to the policy:
Terminal
# Subject claim - must match the integration configuration
export CONNECT_SUBJECT="connect"
vault write auth/${CONNECT_JWT_PATH}/role/${CONNECT_SUBJECT} - <<EOF
{
"role_type": "jwt",
"token_policies": ["${CONNECT_POLICY}"],
"bound_audiences": ["vault"],
"bound_subject": "${CONNECT_SUBJECT}",
"user_claim": "sub",
"token_type": "service"
}
EOFThe bound_audiences must be set to vault, which is the standard audience Connect uses for Vault integrations.
Transfer information to Connect administrator
The Vault administrator shares the following information with the Posit Connect administrator:
| Field | Description |
|---|---|
vault_addr |
The URL of the Vault server (e.g., https://vault.example.com:8200). |
vault_namespace |
The Vault namespace (for HCP Vault or Vault Enterprise). Leave empty for Vault Community Edition. |
jwt_auth_path |
The path where the JWT auth backend is mounted (e.g., jwt-posit-connect). |
role |
The Vault role name to authenticate as (e.g., connect). |
subject |
The subject claim used in the role configuration. Defaults to connect. |
Step 2: Posit Connect administrator
Create OAuth integration in Posit Connect
Using the information from the Vault administrator, the Posit Connect administrator creates an integration through the dashboard’s System > Integrations settings. Once the OAuth integration has been created in Connect, it is available for use by all publishers.
This integration only supports the Workload Identity authentication type. Connect automatically handles token exchange and Vault authentication on behalf of content.
Alternatively, the example below shows how to create a Vault integration using curl and the Connect Server API.
Replace connect.example.org with the address of the Connect server.
Terminal
curl -H "Authorization: Key ${CONNECT_API_KEY}" \
-XPOST https://connect.example.org/__api__/v1/oauth/integrations \
--data '{
"template": "vault",
"name": "Hashicorp Vault",
"description": "Access secrets from Vault using workload identity.",
"config": {
"vault_addr": "https://vault.example.com:8200",
"vault_namespace": "admin",
"jwt_auth_path": "jwt-posit-connect",
"role": "connect",
"subject": "connect"
}
}'
# 200 OK
# {"guid": "<oauth-integration-guid>", ... }For Vault Community Edition, omit vault_namespace or set it to an empty string.
Environment variables available to content
When content runs with a Vault integration, the following environment variables are available:
| Variable | Description |
|---|---|
VAULT_ADDR |
The Vault server URL configured in the integration. |
VAULT_NAMESPACE |
The Vault namespace (if configured). |
VAULT_TOKEN |
A valid Vault client token for authenticating API requests. |
The VAULT_TOKEN is obtained once when content starts. For long-running content, ensure your Vault token TTL is configured appropriately.
Next steps
Once the integration is configured, publishers can use it in their content. The VAULT_TOKEN environment variable can be used with:
Example: Reading secrets with R
app.R
library(vaultr)
# Connect using VAULT_ADDR and VAULT_TOKEN from environment
client <- vault_client(login = "token", quiet = TRUE)
# Read a secret
secret <- client$read("secret/data/connect/database", field = "data")
db_password <- secret$passwordExample: Reading secrets with Python
app.py
import hvac
# Connect using VAULT_ADDR and VAULT_TOKEN from environment
client = hvac.Client()
# Read a secret
response = client.secrets.kv.v2.read_secret_version(path='connect/database')
db_password = response['data']['data']['password']