# Vouch — Complete Documentation > Vouch is an open-source credential broker that replaces long-lived secrets (AWS keys, SSH keys, GitHub PATs, registry tokens) with short-lived credentials after FIDO2 hardware verification. Source: https://github.com/vouch-sh/vouch Website: https://vouch.sh Note: Throughout this documentation, references to `us.vouch.sh` refer to the Vouch US instance URL used for OIDC discovery, credential endpoints, and API calls. --- # Add Hardware-Backed Sign-In to Your Application Source: https://vouch.sh/docs/applications/ Building phishing-resistant authentication from scratch means implementing WebAuthn flows, managing attestation, and handling device lifecycle -- a significant engineering investment for a startup. If you already have an OIDC-compatible application, you can get hardware-backed sign-in without any of that. Vouch is a fully compliant [OpenID Connect](https://openid.net/connect/) (OIDC) provider. You add "Sign in with Vouch" to any web application, single-page app, or native CLI tool using standard OIDC libraries. Every login is backed by a hardware security key, giving your application phishing-resistant authentication without building it yourself. <a href="https://openid.net/certification/certified-fapi-2-0-op-security-profile-final-message-signing-final/" target="_blank" rel="noopener noreferrer"><img src="/img/openid-certified.png" alt="OpenID Certified" class="certification-badge-inline" /></a> ## Choose a guide <div class="journey-grid"> <div class="journey-card"> <h3>Server-rendered web apps</h3> <p>Use the authorization code flow with a backend session.</p> <p><a href="#server-side-frameworks">Server frameworks</a></p> </div> <div class="journey-card"> <h3>Single-page apps</h3> <p>Use authorization code with PKCE for browser-based clients.</p> <p><a href="#single-page-applications">SPA frameworks</a></p> </div> <div class="journey-card"> <h3>Native and CLI apps</h3> <p>Use the Device Authorization Grant for terminals and desktop apps.</p> <p><a href="#native--cli-applications">Native and CLI guides</a></p> </div> <div class="journey-card"> <h3>Agents and MCP servers</h3> <p>Protect tool servers and agent-to-agent calls with Vouch OIDC tokens.</p> <p><a href="#mcp-servers">MCP guides</a> · <a href="#agent-to-agent-a2a">A2A guides</a></p> </div> </div> ## What you'll build By following this guide, you will integrate Vouch as an OIDC identity provider into your application. Users will authenticate with their YubiKey through Vouch, and your application will receive verified identity information including: - A unique, stable user identifier (`sub` claim) in the ID token - The user's email address in the ID token - Hardware attestation claim (`hardware_verified`) in the access token This works with any framework or library that supports OpenID Connect or OAuth 2.0 authorization code flow. Beyond web applications, Vouch secures **MCP tool servers** with bearer token authentication and enables **agent-to-agent (A2A)** communication backed by hardware-verified identity. --- ## Prerequisites Before integrating Vouch into your application, you need: - A **registered OAuth application** at https://us.vouch.sh/applications - Your application's **client ID** (`client_id`) - Your application's **client secret** (`client_secret`) - A configured **redirect URI** (e.g., `https://your-app.example.com/auth/callback`) To register an application, navigate to https://us.vouch.sh/applications and click **New Application**: ![Register New Application form with application type, redirect URIs, and security profile options](/images/admin/applications-new.png) After creating the application, you will see your client ID and client secret. The client secret is only shown once — store it securely. ![Application Created page showing client ID and client secret](/images/admin/application-created.png) You can view and manage all your applications from the applications list: ![My Applications page showing registered applications with type, scope, and status](/images/admin/applications-list.png) --- ## Managing Applications After creating an application, you can view its full configuration — including client ID, type, access scope, redirect URIs, and client secrets — from the application detail page: ![Application detail page showing configuration, redirect URIs, and client secret management](/images/admin/application-detail.png) From this page you can edit settings, rotate client secrets, or delete the application. --- ## Access Scopes Vouch supports two access scopes that control what identity information is included in tokens: | Scope | Description | Claims Included | |---|---|---| | `openid` | **Required.** Identifies this as an OIDC authentication request. | `sub`, `iss`, `aud`, `exp`, `iat` | | `email` | User email address. | `email`, `email_verified` | Request scopes in the authorization request by including them in the `scope` parameter, space-separated: ``` scope=openid email ``` --- ## Rich Authorization Requests For fine-grained access control beyond what scopes can express, Vouch supports Rich Authorization Requests ([RFC 9396](https://datatracker.ietf.org/doc/html/rfc9396)). Instead of flat scope strings, your application can pass structured `authorization_details` objects that describe the type, actions, and resources being requested. ### Format The `authorization_details` parameter is a JSON array of objects. Each object must include a `type` field; all other fields are defined by your application: ```json [ { "type": "account_access", "actions": ["read", "transfer"], "locations": ["https://api.example.com/accounts"] } ] ``` ### Using with PAR Since Vouch uses Pushed Authorization Requests ([RFC 9126](https://datatracker.ietf.org/doc/html/rfc9126)), include `authorization_details` in the PAR request body alongside your other parameters: ```bash curl -X POST https://us.vouch.sh/oauth/par \ -d "client_id=your-client-id" \ -d "client_secret=your-client-secret" \ -d "response_type=code" \ -d "redirect_uri=https://your-app.example.com/auth/callback" \ -d "scope=openid email" \ -d 'authorization_details=[{"type":"account_access","actions":["read","transfer"]}]' ``` ### Token response The granted `authorization_details` are returned in the token response, so your application can confirm exactly what was authorized: ```json { "access_token": "eyJhbGciOiJFUzI1NiIs...", "token_type": "Bearer", "expires_in": 3600, "authorization_details": [ { "type": "account_access", "actions": ["read", "transfer"] } ] } ``` Rich Authorization Requests can be used alongside scopes — they are complementary, not mutually exclusive. --- ## Configuration Reference Use the following endpoints and values to configure your OIDC client library. All URLs are relative to your Vouch server instance. | Parameter | Value | |---|---| | **Discovery URL** | `https://us.vouch.sh/.well-known/openid-configuration` | | **Issuer** | `https://us.vouch.sh` | | **Authorization Endpoint** | `https://us.vouch.sh/oauth/authorize` | | **Token Endpoint** | `https://us.vouch.sh/oauth/token` | | **UserInfo Endpoint** | `https://us.vouch.sh/oauth/userinfo` | | **JWKS URI** | `https://us.vouch.sh/oauth/jwks` | | **Signing Algorithm** | `ES256` | | **Supported Scopes** | `openid`, `email` | | **Authorization Details** | Supported via `authorization_details` parameter ([RFC 9396](https://datatracker.ietf.org/doc/html/rfc9396)) | | **Device Authorization Endpoint** | `https://us.vouch.sh/oauth/device/code` | | **Device Verification URL** | `https://us.vouch.sh/oauth/device` | | **PAR Endpoint** | `https://us.vouch.sh/oauth/par` | | **Token Revocation Endpoint** | `https://us.vouch.sh/oauth/revoke` | | **Token Introspection Endpoint** | `https://us.vouch.sh/oauth/introspect` | | **Protected Resource Metadata** | `https://us.vouch.sh/.well-known/oauth-protected-resource` | Most OIDC libraries can auto-configure themselves from the Discovery URL alone. ### Additional capabilities The authorization endpoint supports several advanced features: - **`response_mode=form_post`** — The authorization code is delivered via an auto-submitting HTML form POST instead of a query-string redirect. Useful for server-side applications that want the code in the request body. - **`request_uri` by URL** — In addition to PAR (`urn:ietf:params:oauth:request_uri:...`) and inline `request` JWTs, the authorization endpoint accepts HTTPS URLs as `request_uri` values for hosting Request Objects externally (OIDC Core Section 6.2). - **Signed UserInfo** — Applications that register `userinfo_signed_response_alg` (ES256 or RS256) during client registration receive signed JWT responses from the UserInfo endpoint instead of plain JSON. - **Client branding** — Applications that register `logo_uri`, `policy_uri`, or `tos_uri` during client registration have these displayed on the Vouch login page. --- ## ID Token Claims Vouch ID tokens follow the standard OIDC specification. The payload contains: | Claim | Type | Description | |---|---|---| | `iss` | string | Issuer — your Vouch server URL | | `sub` | string | Subject — stable, unique user identifier | | `aud` | string | Audience — your application's `client_id` | | `exp` | number | Expiration time | | `iat` | number | Issued-at time | | `email` | string | User's email address (when `email` scope is requested) | | `email_verified` | boolean | Whether the email is verified (when `email` scope is requested) | | `amr` | array | Authentication methods used (e.g., `["hwk", "pin"]`) | | `acr` | string | Authentication context class (e.g., NIST AAL3 for hardware MFA) | | `cnf` | object | Confirmation claim containing key binding information per [RFC 7800](https://datatracker.ietf.org/doc/html/rfc7800). Includes a `kid` field referencing the specific credential used. | Example ID token payload: ```json { "iss": "https://us.vouch.sh", "sub": "user_abc123", "aud": "your-client-id", "exp": 1700000000, "iat": 1699996400, "email": "alice@example.com", "email_verified": true, "amr": ["hwk", "pin"], "acr": "urn:nist:authentication:assurance-level:aal3", "cnf": { "kid": "credential_xyz789" } } ``` ## Access Token Claims Vouch issues access tokens as JWTs ([RFC 9068](https://datatracker.ietf.org/doc/html/rfc9068)). These include a hardware attestation claim that your application can use to enforce security policies. This claim is **not** in the ID token or the UserInfo response. | Claim | Type | Description | |---|---|---| | `hardware_verified` | boolean | `true` if the authentication was performed using a verified hardware security key. Always `true` for Vouch-issued tokens from the authorization code flow. | To access this claim, decode the access token JWT payload. See the [examples repository](https://github.com/vouch-sh/examples) for working implementations in each framework. The `hardware_aaguid` claim (FIDO2 authenticator AAGUID identifying the key make and model) is available in cloud federation tokens (AWS STS, Kubernetes) but not in standard OIDC access tokens. --- ## Token Lifetime Vouch access tokens are short-lived (1 hour, as shown in the `expires_in` field above) and Vouch does not issue refresh tokens (by design -- every session requires hardware key interaction). Your application should handle token expiry gracefully: - **Server-side applications** -- Check token expiry before using it. If expired, redirect the user through the authorization flow again. - **Single-page applications** -- Check `user.expired` before making API calls. If the token has expired, redirect the user through the authorization flow again using `signinRedirect()`. - **Native CLI applications** -- Use the device authorization flow (RFC 8628) and prompt the user to re-authenticate when the token expires. --- ## Service-to-Service (M2M) Authentication For server-to-server communication where no human user is involved, Vouch supports **Token Exchange** (RFC 8693). A service with a valid Vouch token can exchange it for a new token scoped to a different audience, enabling secure service-to-service calls. ### Token Exchange Flow 1. **Service A** authenticates a user through the standard OIDC flow and obtains an access token. 2. **Service A** calls **Service B** and needs to pass along the user's identity. 3. **Service A** exchanges its token for a new token with **Service B's** audience by calling the token endpoint. ### Token Exchange Request ```bash curl -X POST https://us.vouch.sh/oauth/token \ -d "grant_type=urn:ietf:params:oauth:grant-type:token-exchange" \ -d "subject_token=<access-token>" \ -d "subject_token_type=urn:ietf:params:oauth:token-type:access_token" \ -d "audience=service-b-client-id" \ -d "client_id=service-a-client-id" \ -d "client_secret=service-a-client-secret" ``` ### Token Exchange Response ```json { "access_token": "eyJhbGciOiJFUzI1NiIs...", "issued_token_type": "urn:ietf:params:oauth:token-type:access_token", "token_type": "Bearer", "expires_in": 3600 } ``` The new token is scoped to Service B's audience and inherits the user's identity claims from the original token. Service B can validate this token by checking the `aud` claim and verifying the signature against the Vouch JWKS endpoint. --- ## Client Credentials (Machine-to-Machine) For automated systems that need to authenticate without any human interaction -- CI/CD pipelines, background services, or daemon processes -- Vouch supports the OAuth **client credentials** grant ([RFC 6749 Section 4.4](https://datatracker.ietf.org/doc/html/rfc6749#section-4.4)). Unlike the authorization code flow (which requires a browser and a YubiKey tap), the client credentials flow lets a service authenticate directly using its client ID and client secret. Services can also use Mutual TLS ([RFC 8705](https://datatracker.ietf.org/doc/html/rfc8705)) for client authentication (`tls_client_auth` or `self_signed_tls_client_auth`) and certificate-bound access tokens as an alternative to client secrets — see [Architecture](/docs/architecture/#mutual-tls-rfc-8705) for details. ### When to use client credentials - **CI/CD pipelines** that need Vouch tokens without a human in the loop - **Background services** or **daemon processes** that run unattended - **Automated tooling** that needs to call Vouch-protected APIs For deployments where you want a human to explicitly authorize each action (e.g., production deploys), use the [CI/CD human approval gate](/docs/cicd/) pattern instead. ### Request ```bash curl -X POST https://us.vouch.sh/oauth/token \ -d "grant_type=client_credentials" \ -d "client_id=your-client-id" \ -d "client_secret=your-client-secret" \ -d "scope=openid email" ``` ### Response ```json { "access_token": "eyJhbGciOiJFUzI1NiIs...", "token_type": "Bearer", "expires_in": 3600 } ``` The access token can be used as a Bearer token in API requests to any service that validates tokens against the Vouch JWKS endpoint. ### Setup 1. Register an application at `https://us.vouch.sh/applications` (or via the [Admin Dashboard](/docs/admin/)). 2. Note the **client ID** and **client secret**. 3. Store the client secret securely (e.g., as a CI/CD secret, not in source code). 4. Use the token endpoint with `grant_type=client_credentials` to obtain tokens. --- ## Troubleshooting ### Discovery endpoint not reachable ``` Error: unable to fetch OpenID configuration from https://us.vouch.sh/.well-known/openid-configuration ``` - Verify the Vouch server URL is correct and accessible from your application server. - Check that your application can reach the Vouch server (no firewall or network restrictions). - Ensure the URL does not have a trailing slash. ### Invalid client_id or client_secret ``` error: invalid_client ``` - Verify the `client_id` and `client_secret` match what was registered on the Vouch [applications page](https://us.vouch.sh/applications). - Check that the client credentials are not expired or revoked. - Ensure the credentials are being sent correctly (as form parameters for the token endpoint, not as JSON). ### Redirect URI mismatch ``` error: redirect_uri_mismatch ``` The redirect URI in your authorization request does not match any URI registered for this client. Ensure: - The redirect URI exactly matches what was registered (including protocol, host, port, and path). - There are no trailing slashes or query parameters that differ. - If using localhost for development, the port must match exactly. ### ID token validation fails ``` Error: ID token signature verification failed ``` - Ensure your library is configured to use the `ES256` signing algorithm. Vouch uses ECDSA with P-256, not RSA. - Verify the JWKS endpoint is reachable: `https://us.vouch.sh/oauth/jwks` - Check that the `iss` claim matches your configured issuer URL exactly. - Ensure your server's clock is synchronized (token validation is time-sensitive). ### CORS errors in browser applications ``` Access to fetch at 'https://us.vouch.sh/oauth/token' has been blocked by CORS policy ``` For single-page applications, ensure your application's origin is registered as an allowed origin on the Vouch [applications page](https://us.vouch.sh/applications). The Vouch server must include your origin in its CORS `Access-Control-Allow-Origin` response header. ### Device code expired ``` error: expired_token ``` The user did not complete authentication within the allowed time window. Request a new device code and display the new `user_code` to the user. The default expiration is 10 minutes. --- ## Rails (OmniAuth) Source: https://vouch.sh/docs/applications/rails/ See the [Applications overview](/docs/applications/) for prerequisites, configuration endpoints, and available scopes. [OmniAuth OpenID Connect](https://github.com/omniauth/omniauth_openid_connect) provides a standard OIDC strategy for Rails applications. Key configuration: - Install [`omniauth-openid-connect`](https://github.com/omniauth/omniauth_openid_connect) and [`omniauth-rails_csrf_protection`](https://github.com/cookpad/omniauth-rails_csrf_protection) gems - Enable PKCE in the OmniAuth provider configuration (`pkce: true`) - CSRF protection is required for the OmniAuth request phase - Callback URL: `/auth/vouch/callback` - The hardware attestation claim (`hardware_verified`) is in the access token JWT — decode the payload to read it ## Example **[web/rails-omniauth](https://github.com/vouch-sh/examples/tree/main/web/rails-omniauth)** — Complete working example with OmniAuth OIDC strategy, PKCE, and hardware claim extraction. --- ## Django (django-allauth) Source: https://vouch.sh/docs/applications/django/ See the [Applications overview](/docs/applications/) for prerequisites, configuration endpoints, and available scopes. [django-allauth](https://docs.allauth.org/) provides OpenID Connect support for Django. Key configuration: - Install [`django-allauth[openid_connect]`](https://docs.allauth.org/en/latest/socialaccount/providers/openid_connect.html) - Set `oauth_pkce_enabled: True` and `fetch_userinfo: True` in `SOCIALACCOUNT_PROVIDERS` - Use `server_url` (not `issuer`) in provider settings - Callback URL: `/accounts/oidc/vouch/login/callback/` - The hardware attestation claim (`hardware_verified`) is in the access token JWT — decode the payload to read it ## Example **[web/django-allauth](https://github.com/vouch-sh/examples/tree/main/web/django-allauth)** — Complete working example with django-allauth OIDC provider, PKCE, and hardware claim extraction. --- ## Express.js (openid-client) Source: https://vouch.sh/docs/applications/express/ See the [Applications overview](/docs/applications/) for prerequisites, configuration endpoints, and available scopes. [openid-client](https://github.com/panva/openid-client) provides a certified OpenID Connect client for Node.js. Key configuration: - Use `client.discovery()` for automatic issuer metadata, then `authorizationCodeGrant()` with PKCE - Manual state, nonce, and PKCE code verifier management - Store tokens in `express-session` with `saveUninitialized: false` - The hardware attestation claim (`hardware_verified`) is in the access token JWT ([RFC 9068](https://datatracker.ietf.org/doc/html/rfc9068)) — decode with `Buffer.from(token.split('.')[1], 'base64url')` ## Example **[web/express-openid](https://github.com/vouch-sh/examples/tree/main/web/express-openid)** — Complete working example with authorization code flow, PKCE, session management, and token introspection. --- ## Next.js (NextAuth.js) Source: https://vouch.sh/docs/applications/nextjs/ See the [Applications overview](/docs/applications/) for prerequisites, configuration endpoints, and available scopes. [NextAuth.js](https://next-auth.js.org/) provides drop-in authentication for Next.js with OIDC auto-discovery. Key configuration: - Configure a custom OAuth provider with `wellKnown` discovery URL - Set `id_token_signed_response_alg: 'ES256'` to match Vouch's signing algorithm - Enable PKCE with `checks: ['pkce', 'state']` and set `idToken: true` - The hardware attestation claim (`hardware_verified`) is in the access token JWT — decode in the `jwt` callback and propagate through the session callback - Requires `NEXTAUTH_SECRET` environment variable (generate with `openssl rand -base64 32`) ## Example **[web/nextjs-nextauth](https://github.com/vouch-sh/examples/tree/main/web/nextjs-nextauth)** — Complete working example with NextAuth.js provider, PKCE, and hardware claim propagation through JWT and session callbacks. --- ## Laravel (Socialite) Source: https://vouch.sh/docs/applications/laravel/ See the [Applications overview](/docs/applications/) for prerequisites, configuration endpoints, and available scopes. [Laravel Socialite](https://laravel.com/docs/socialite) with the OIDC driver provides OAuth/OIDC integration. Key configuration: - Install [`laravel/socialite`](https://laravel.com/docs/socialite), [`kovah/laravel-socialite-oidc`](https://github.com/Kovah/laravel-socialite-oidc), and [`socialiteproviders/manager`](https://github.com/SocialiteProviders/Manager) - Use the `'oidc'` driver name with `->enablePKCE()` for PKCE support - Register the Socialite service provider and event listener - The hardware attestation claim (`hardware_verified`) is in the access token JWT — decode the payload after base64url character replacement - Callback URL: `/auth/callback` ## Example **[web/laravel-socialite](https://github.com/vouch-sh/examples/tree/main/web/laravel-socialite)** — Complete working example with Socialite OIDC driver, PKCE, and hardware claim extraction. --- ## Flask (Authlib) Source: https://vouch.sh/docs/applications/flask/ See the [Applications overview](/docs/applications/) for prerequisites, configuration endpoints, and available scopes. [Authlib](https://authlib.org/) provides OAuth and OpenID Connect client support for Flask. Key configuration: - Install [`authlib`](https://authlib.org/), `flask`, and `requests` - Register the provider with `server_metadata_url` and `code_challenge_method='S256'` for PKCE - Set `client_kwargs={'scope': 'openid email'}` - The hardware attestation claim (`hardware_verified`) is in the access token JWT — decode the payload with base64url and padding adjustment - Callback URL: `/callback` ## Example **[web/flask-authlib](https://github.com/vouch-sh/examples/tree/main/web/flask-authlib)** — Complete working example with authorization code flow, PKCE, and hardware claim extraction. --- ## FastAPI (Authlib) Source: https://vouch.sh/docs/applications/fastapi/ See the [Applications overview](/docs/applications/) for prerequisites, configuration endpoints, and available scopes. [Authlib](https://authlib.org/) provides OAuth and OpenID Connect client support for Starlette-based applications. Key configuration: - Register the provider with `server_metadata_url` and `code_challenge_method='S256'` for PKCE - Add `SessionMiddleware` with a secret key before the auth middleware - The hardware attestation claim (`hardware_verified`) is in the access token JWT — decode the payload with base64url and padding adjustment - Callback URL: `/callback` ## Example **[web/fastapi-authlib](https://github.com/vouch-sh/examples/tree/main/web/fastapi-authlib)** — Complete working example with authorization code flow, PKCE, session middleware, and hardware claim extraction. --- ## Spring Boot (Spring Security) Source: https://vouch.sh/docs/applications/spring-boot/ See the [Applications overview](/docs/applications/) for prerequisites, configuration endpoints, and available scopes. [Spring Security OAuth2 Client](https://docs.spring.io/spring-security/reference/servlet/oauth2/client/index.html) provides OIDC auto-discovery. Key configuration: - Add [`spring-boot-starter-oauth2-client`](https://docs.spring.io/spring-boot/reference/web/spring-security.html#web.security.oauth2.client) dependency - Configure `spring.security.oauth2.client.provider.vouch.issuer-uri` for auto-discovery - Set `authorization-grant-type: authorization_code` and `scope: openid,email` - Enable PKCE with `OAuth2AuthorizationRequestCustomizers.withPkce()` - The hardware attestation claim (`hardware_verified`) is in the access token JWT — decode the payload to read it ## Example **[web/spring-boot](https://github.com/vouch-sh/examples/tree/main/web/spring-boot)** — Complete working example with Spring Security OIDC, PKCE, and hardware claim extraction. --- ## Axum (openidconnect-rs) Source: https://vouch.sh/docs/applications/axum/ See the [Applications overview](/docs/applications/) for prerequisites, configuration endpoints, and available scopes. [openidconnect-rs](https://crates.io/crates/openidconnect) provides a type-safe OpenID Connect client for Rust. Key configuration: - Use `CoreProviderMetadata::discover_async()` for OIDC auto-discovery - PKCE is automatic with `PkceCodeChallenge::new_random_sha256()` - Define a custom claims struct implementing `AdditionalClaims` for type-safe access to Vouch-specific fields - The hardware attestation claim (`hardware_verified`) is in the access token JWT — decode the payload after token exchange - Use tower-sessions for session management (use a persistent store in production) ## Example **[web/axum-openidconnect](https://github.com/vouch-sh/examples/tree/main/web/axum-openidconnect)** — Complete working example with type-safe claims, PKCE, and hardware claim extraction. --- ## Go (go-oidc) Source: https://vouch.sh/docs/applications/go/ See the [Applications overview](/docs/applications/) for prerequisites, configuration endpoints, and available scopes. [go-oidc](https://github.com/coreos/go-oidc) and the standard [oauth2](https://pkg.go.dev/golang.org/x/oauth2) package provide OIDC support for Go. Key configuration: - Initialize the provider with `oidc.NewProvider()` for auto-discovery - Generate PKCE verifier with `oauth2.GenerateVerifier()` and `oauth2.S256ChallengeOption()` - Manual state, nonce, and PKCE verifier management required - The hardware attestation claim (`hardware_verified`) is in the access token JWT — decode the payload into a struct after token exchange ## Example **[web/go-oidc](https://github.com/vouch-sh/examples/tree/main/web/go-oidc)** — Complete working example with OIDC discovery, PKCE, and hardware claim extraction. --- ## ASP.NET Core (OpenID Connect) Source: https://vouch.sh/docs/applications/aspnet-core/ See the [Applications overview](/docs/applications/) for prerequisites, configuration endpoints, and available scopes. ASP.NET Core includes built-in OpenID Connect middleware. Key configuration: - Requires .NET 10.0 or later with [`Microsoft.AspNetCore.Authentication.OpenIdConnect`](https://learn.microsoft.com/en-us/aspnet/core/security/authentication/configure-oidc-web-authentication) - Enable PKCE with `options.UsePkce = true` - Set `GetClaimsFromUserInfoEndpoint = true` and `SaveTokens = true` - The hardware attestation claim (`hardware_verified`) is in the access token JWT — decode with `JsonDocument.Parse()` after base64url decoding with padding adjustment ## Example **[web/aspnet-core](https://github.com/vouch-sh/examples/tree/main/web/aspnet-core)** — Complete working example with OIDC middleware, PKCE, and hardware claim extraction. --- ## React (react-oidc-context) Source: https://vouch.sh/docs/applications/react/ See the [Applications overview](/docs/applications/) for prerequisites, configuration endpoints, and available scopes. [react-oidc-context](https://github.com/authts/react-oidc-context) wraps [oidc-client-ts](https://github.com/authts/oidc-client-ts) in a React context provider. Key configuration: - No client secret needed (public client with PKCE, enabled by default) - Vouch does not issue refresh tokens — redirect the user to sign in again when the token expires - The hardware attestation claim (`hardware_verified`) is in the access token JWT — decode with `atob(token.split('.')[1])` after base64url character replacement - Access the token via `auth.user.access_token` ## Example **[spa/react](https://github.com/vouch-sh/examples/tree/main/spa/react)** — Complete working example with react-oidc-context, PKCE, and hardware claim extraction from the access token. --- ## Vue (oidc-client-ts) Source: https://vouch.sh/docs/applications/vue/ See the [Applications overview](/docs/applications/) for prerequisites, configuration endpoints, and available scopes. [oidc-client-ts](https://github.com/authts/oidc-client-ts) provides a `UserManager` for managing the OIDC lifecycle in Vue applications. Key configuration: - No client secret needed (public client with PKCE, enabled by default) - Vouch does not issue refresh tokens — redirect the user to sign in again when the token expires - Configure `UserManager` with `authority`, `client_id`, `redirect_uri`, and `scope` - The hardware attestation claim (`hardware_verified`) is in the access token JWT — decode with `atob(token.split('.')[1])` after base64url character replacement - State persistence uses `sessionStorage` by default via `WebStorageStateStore` ## Example **[spa/vue](https://github.com/vouch-sh/examples/tree/main/spa/vue)** — Complete working example with oidc-client-ts UserManager, PKCE, and hardware claim extraction. --- ## SvelteKit (oidc-client-ts) Source: https://vouch.sh/docs/applications/sveltekit/ See the [Applications overview](/docs/applications/) for prerequisites, configuration endpoints, and available scopes. [oidc-client-ts](https://github.com/authts/oidc-client-ts) integrates with SvelteKit using Svelte 5 reactivity (`$state` and `$derived`). Key configuration: - No client secret needed (public client with PKCE, enabled by default) - Vouch does not issue refresh tokens — redirect the user to sign in again when the token expires - Uses `sessionStorage` for state persistence - The hardware attestation claim (`hardware_verified`) is in the access token JWT — decode with `atob(token.split('.')[1])` after base64url character replacement ## Example **[spa/sveltekit](https://github.com/vouch-sh/examples/tree/main/spa/sveltekit)** — Complete working example with oidc-client-ts, Svelte 5 reactivity, PKCE, and hardware claim extraction. --- ## Angular (angular-auth-oidc-client) Source: https://vouch.sh/docs/applications/angular/ See the [Applications overview](/docs/applications/) for prerequisites, configuration endpoints, and available scopes. [angular-auth-oidc-client](https://github.com/damienbod/angular-auth-oidc-client) is a certified OpenID Connect library for Angular. Key configuration: - No client secret needed (public client with PKCE, enabled by default) - Vouch does not issue refresh tokens — redirect the user to sign in again when the token expires - Set `autoUserInfo: true` for automatic userinfo fetching - Use `OidcSecurityService.checkAuth()` to get `{ isAuthenticated, userData, accessToken }` - The hardware attestation claim (`hardware_verified`) is in the access token JWT — decode with `atob(token.split('.')[1])` after base64url character replacement ## Example **[spa/angular](https://github.com/vouch-sh/examples/tree/main/spa/angular)** — Complete working example with angular-auth-oidc-client, PKCE, and hardware claim extraction. --- ## Vanilla JavaScript Source: https://vouch.sh/docs/applications/vanilla-js/ See the [Applications overview](/docs/applications/) for prerequisites, configuration endpoints, and available scopes. [oidc-client-ts](https://github.com/authts/oidc-client-ts) works with plain JavaScript — no framework required. It can be loaded via CDN (`https://cdn.jsdelivr.net/npm/oidc-client-ts`). Key configuration: - No client secret needed (public client with PKCE, enabled by default) - Vouch does not issue refresh tokens — redirect the user to sign in again when the token expires - Initialize `UserManager` and call `signinRedirectCallback()` on the callback page - The hardware attestation claim (`hardware_verified`) is in the access token JWT — decode with `atob(token.split('.')[1])` after base64url character replacement ## Example **[spa/vanilla-js](https://github.com/vouch-sh/examples/tree/main/spa/vanilla-js)** — Complete working example with oidc-client-ts, PKCE, and hardware claim extraction. --- ## BFF Pattern (Express) Source: https://vouch.sh/docs/applications/bff-express/ See the [Applications overview](/docs/applications/) for prerequisites, configuration endpoints, and available scopes. The Backend-for-Frontend (BFF) pattern keeps all OAuth tokens on the server. The browser never sees access tokens or ID tokens — it authenticates via `HttpOnly`, `SameSite=Strict` session cookies instead. Key configuration: - Client secret required (confidential client, since tokens are server-side) - PKCE is automatic with [`openid-client`](https://github.com/panva/openid-client) - Set `httpOnly: true`, `sameSite: 'strict'`, and `secure: true` (in production) on session cookies - Proxy a `/api/me` endpoint so the frontend never handles tokens directly - The hardware attestation claim (`hardware_verified`) is decoded server-side from the access token JWT ## Example **[spa/bff-express](https://github.com/vouch-sh/examples/tree/main/spa/bff-express)** — Complete working example with Express BFF server, openid-client, PKCE, and proxied UserInfo endpoint. --- ## Device Authorization (CLI) Source: https://vouch.sh/docs/applications/device-authorization/ See the [Applications overview](/docs/applications/) for prerequisites, configuration endpoints, and available scopes. For native desktop applications and CLI tools that cannot open a browser redirect, use the [Device Authorization Grant](https://datatracker.ietf.org/doc/html/rfc8628) (RFC 8628). This flow displays a URL and code that the user enters in a browser on any device. ## How it works 1. Your application requests a device code from `POST /oauth/device/code`. 2. The user opens the verification URL in a browser and enters the displayed code. 3. The user authenticates with their YubiKey in the browser. 4. Your application polls `POST /oauth/token` with the `device_code` until the user completes authentication. 5. The token response includes an access token with the hardware attestation claim (`hardware_verified`). Key details: - Handle `authorization_pending` (keep polling), `slow_down` (increase interval), and `expired_token` (request a new code) responses - The default device code expiration is 10 minutes - No client secret is needed (public client) - [Rich Authorization Requests](/docs/applications/#rich-authorization-requests) are supported via `authorization_details` in the device code request ## Examples Working examples are available in the examples repository: - **[native/python](https://github.com/vouch-sh/examples/tree/main/native/python)** — Python device flow with polling and hardware claim extraction - **[native/node](https://github.com/vouch-sh/examples/tree/main/native/node)** — Node.js device flow with polling and hardware claim extraction - **[native/rust](https://github.com/vouch-sh/examples/tree/main/native/rust)** — Rust device flow with the [`openidconnect`](https://crates.io/crates/openidconnect) crate --- ## MCP Remote Server (TypeScript) Source: https://vouch.sh/docs/applications/mcp-server-ts/ See the [Applications overview](/docs/applications/) for prerequisites, configuration endpoints, and available scopes. The [Model Context Protocol](https://modelcontextprotocol.io/) (MCP) lets AI assistants call tools on remote servers. This guide covers building an MCP remote server that requires Vouch OIDC bearer tokens, using the official TypeScript SDK and [`jose`](https://github.com/panva/jose) for JWT verification. The server validates access tokens against the Vouch JWKS endpoint and extracts the hardware attestation claim (`hardware_verified`) from the JWT payload. Tools can gate sensitive operations on hardware key attestation. ## Example **[mcp/remote-server-ts](https://github.com/vouch-sh/examples/tree/main/mcp/remote-server-ts)** -- Complete working example with bearer token verification, [RFC 9728](https://datatracker.ietf.org/doc/html/rfc9728) Protected Resource Metadata, and per-user MCP server instances. --- ## MCP Remote Server (Python) Source: https://vouch.sh/docs/applications/mcp-server-py/ See the [Applications overview](/docs/applications/) for prerequisites, configuration endpoints, and available scopes. The [Model Context Protocol](https://modelcontextprotocol.io/) (MCP) lets AI assistants call tools on remote servers. [FastMCP](https://github.com/PrefectHQ/fastmcp) has built-in support for OAuth-based auth and [RFC 9728](https://datatracker.ietf.org/doc/html/rfc9728) Protected Resource Metadata. The server validates access tokens against the Vouch JWKS endpoint and extracts the hardware attestation claim (`hardware_verified`) from the JWT payload. Tools can gate sensitive operations on hardware key attestation. ## Example **[mcp/remote-server-py](https://github.com/vouch-sh/examples/tree/main/mcp/remote-server-py)** -- Complete working example with FastMCP, bearer token verification, and hardware claim extraction. --- ## MCP Credential Broker Source: https://vouch.sh/docs/applications/mcp-credential-broker/ See the [Applications overview](/docs/applications/) for prerequisites, configuration endpoints, and available scopes. An MCP credential broker lets AI assistants obtain temporary cloud credentials on behalf of authenticated users. The broker validates the caller's Vouch access token, then calls the Vouch credential APIs to get: - **AWS** -- Temporary STS credentials via `AssumeRoleWithWebIdentity` - **GitHub** -- Installation access tokens scoped to the user's organization - **SSH** -- Signed certificates tied to the user's identity All credentials are short-lived and trace back to a hardware-verified human identity. ## Example **[mcp/credential-broker](https://github.com/vouch-sh/examples/tree/main/mcp/credential-broker)** -- Complete working example extending the Python MCP remote server with AWS, GitHub, and SSH credential brokering tools. --- ## A2A Agent (Python) Source: https://vouch.sh/docs/applications/a2a-agent/ See the [Applications overview](/docs/applications/) for prerequisites, configuration endpoints, and available scopes. The [Agent-to-Agent (A2A)](https://a2a-protocol.org/) protocol defines how AI agents discover and communicate with each other. An A2A agent secured with Vouch requires bearer token authentication -- the Agent Card advertises the Vouch OIDC security scheme, and the agent validates access tokens against the Vouch JWKS endpoint. ## Example **[a2a/python-agent](https://github.com/vouch-sh/examples/tree/main/a2a/python-agent)** -- Complete working example with A2A Python SDK, Agent Card with Vouch security scheme, and bearer token verification middleware. --- ## Credential Brokering Agents Source: https://vouch.sh/docs/applications/credential-brokering-agents/ See the [Applications overview](/docs/applications/) for prerequisites, configuration endpoints, and available scopes. CLI agents and automation scripts can authenticate with Vouch using the [Device Authorization Grant](https://datatracker.ietf.org/doc/html/rfc8628) (no browser redirect needed), then use the Vouch credential brokering APIs to obtain temporary AWS credentials, GitHub tokens, or SSH certificates -- all tied to the user's hardware-backed identity. ## Examples Working examples are available in the examples repository: - **[native/python-agent-aws](https://github.com/vouch-sh/examples/tree/main/native/python-agent-aws)** -- CLI agent that brokers AWS STS credentials - **[native/python-agent-github](https://github.com/vouch-sh/examples/tree/main/native/python-agent-github)** -- CLI agent that brokers GitHub installation tokens - **[native/python-agent-multi](https://github.com/vouch-sh/examples/tree/main/native/python-agent-multi)** -- CLI agent that brokers AWS, GitHub, and SSH credentials --- # Replace AWS Access Keys with Short-Lived Credentials Source: https://vouch.sh/docs/aws/ Vouch eliminates static AWS access keys. You configure AWS to trust Vouch as an OIDC identity provider, and developers get temporary STS credentials -- valid for up to 1 hour -- after authenticating with their YubiKey. Every API call is tied to a verified human identity in CloudTrail. {{< tldr >}} - **Prerequisite:** [Getting Started](/docs/getting-started/) (CLI installed, YubiKey enrolled). - **Admin, once:** [register the OIDC provider](#step-1----register-the-vouch-oidc-provider) and [deploy a role](#step-2----deploy-a-role); share the role ARN. - **Each developer:** `vouch setup aws --role <ROLE_ARN>`, then verify with `aws sts get-caller-identity --profile vouch`. - Rolling this out to a whole team? Follow the [Team Rollout playbook](/docs/rollout/). {{< /tldr >}} ## Step 1 -- Register the Vouch OIDC provider {{< role admin >}} Vouch uses **exactly one OIDC provider per organization**. Register it once -- in your single AWS account, or in the **management account** if you use AWS Organizations. An administrator does this before any user can assume a role. > For background on OIDC identity providers in AWS, see [Creating OpenID Connect (OIDC) identity providers](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_providers_create_oidc.html) in the AWS documentation. #### AWS CLI ```bash aws iam create-open-id-connect-provider \ --url "https://us.vouch.sh" \ --client-id-list "https://us.vouch.sh" ``` #### CloudFormation ```yaml AWSTemplateFormatVersion: "2010-09-09" Description: Vouch OIDC Identity Provider Resources: VouchOIDCProvider: Type: "AWS::IAM::OIDCProvider" Properties: Url: "https://us.vouch.sh" ClientIdList: - "https://us.vouch.sh" ``` #### Terraform ```hcl resource "aws_iam_openid_connect_provider" "vouch" { url = "https://us.vouch.sh" client_id_list = ["https://us.vouch.sh"] } ``` > **Note:** AWS fetches the JWKS from `https://us.vouch.sh/oauth/jwks` at runtime to verify token signatures. A `ThumbprintList` is no longer required -- AWS obtains the root CA thumbprint automatically. --- ## Choose your setup {{< role admin >}} How your team accesses AWS decides what you deploy next. This is the same question the `vouch setup aws` wizard asks each developer -- **"How do you access AWS?"** -- so admins and developers stay aligned: | Your AWS layout | What to deploy | Guide | |---|---|---| | **Single account — one IAM role** | One role in this account | Continue to Step 2 below | | **Multiple accounts — a management role that assumes into member roles** | A hub role here, plus a spoke role per account | [Role chaining](/docs/aws-multi-account/) | | **Identity Center (SSO) — permission sets** | Register Vouch as a trusted token issuer | [Identity Center](/docs/aws-multi-account/#aws-iam-identity-center) | If you have an Organization, everything anchors in the management account. Single-account setup continues below. --- ## Step 2 -- Deploy a role {{< role admin >}} This is the role developers federate into with `vouch login`. Its **trust policy** is the same no matter what the role can do; only the **permissions policy** changes -- and *what the role is allowed to do is your team's decision.* Deploy the shared trust policy below, then attach a permissions policy one of two ways. The `*@example.com` condition limits role assumption to anyone with a verified email in your domain (see [Tips for restricting access](#tips-for-restricting-access) for narrower patterns). ### Shared trust policy ```json { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "Federated": "arn:aws:iam::ACCOUNT_ID:oidc-provider/us.vouch.sh" }, "Action": [ "sts:AssumeRoleWithWebIdentity", "sts:SetSourceIdentity", "sts:TagSession" ], "Condition": { "StringEquals": { "us.vouch.sh:aud": "https://us.vouch.sh" }, "StringLike": { "us.vouch.sh:sub": "*@example.com", "sts:RoleSessionName": "${us.vouch.sh:sub}" }, "Bool": { "sts:RoleAuthorizedByIdp": "true" } } } ] } ``` The `sub` and `sts:RoleSessionName` conditions bind the session to the authenticated user, and `sts:RoleAuthorizedByIdp` requires the token to be pinned to this role; see [Tips for restricting access](#tips-for-restricting-access) for what each does. ### Managed policy Attach an AWS-managed policy. Start with `ReadOnlyAccess` and broaden to exactly what your team needs -- don't reach for `PowerUserAccess` by default. See AWS's [job-function managed policies](https://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies_job-functions.html) for options. #### CloudFormation ```yaml Resources: VouchDeveloperRole: Type: AWS::IAM::Role Properties: RoleName: VouchDeveloper AssumeRolePolicyDocument: Version: "2012-10-17" Statement: - Effect: Allow Principal: Federated: !Sub "arn:${AWS::Partition}:iam::${AWS::AccountId}:oidc-provider/us.vouch.sh" Action: - "sts:AssumeRoleWithWebIdentity" - "sts:SetSourceIdentity" - "sts:TagSession" Condition: StringEquals: "us.vouch.sh:aud": "https://us.vouch.sh" StringLike: "us.vouch.sh:sub": "*@example.com" "sts:RoleSessionName": "${us.vouch.sh:sub}" Bool: "sts:RoleAuthorizedByIdp": "true" ManagedPolicyArns: # Start safe. Attach the permissions your team needs. - !Sub "arn:${AWS::Partition}:iam::aws:policy/ReadOnlyAccess" ``` #### Terraform ```hcl data "aws_caller_identity" "current" {} data "aws_partition" "current" {} locals { aws_partition = data.aws_partition.current.partition aws_account_id = data.aws_caller_identity.current.account_id } resource "aws_iam_role" "vouch_developer" { name = "VouchDeveloper" assume_role_policy = jsonencode({ Version = "2012-10-17" Statement = [ { Effect = "Allow" Principal = { Federated = "arn:${local.aws_partition}:iam::${local.aws_account_id}:oidc-provider/us.vouch.sh" } Action = [ "sts:AssumeRoleWithWebIdentity", "sts:SetSourceIdentity", "sts:TagSession", ] Condition = { StringEquals = { "us.vouch.sh:aud" = "https://us.vouch.sh" } StringLike = { "us.vouch.sh:sub" = "*@example.com" "sts:RoleSessionName" = "${us.vouch.sh:sub}" } Bool = { "sts:RoleAuthorizedByIdp" = "true" } } } ] }) # Start safe. Attach the permissions your team needs. managed_policy_arns = ["arn:${local.aws_partition}:iam::aws:policy/ReadOnlyAccess"] } ``` ### Explicit actions Attach a custom inline policy listing only the actions your team needs. Use the same trust policy and role definition as the managed-policy example; replace the `ManagedPolicyArns` block with an inline policy. Example -- read/write a specific S3 bucket and read CloudWatch logs: ```json { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "s3:GetObject", "s3:PutObject", "s3:ListBucket" ], "Resource": [ "arn:aws:s3:::my-app-data", "arn:aws:s3:::my-app-data/*" ] }, { "Effect": "Allow", "Action": [ "logs:GetLogEvents", "logs:FilterLogEvents", "logs:DescribeLogGroups", "logs:DescribeLogStreams" ], "Resource": "*" } ] } ``` In CloudFormation, use [`Policies`](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-iam-role.html#cfn-iam-role-policies) on the role; in Terraform, use [`aws_iam_role_policy`](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy). <div class="checkpoint"> <p><strong>You are done with role deployment when...</strong></p> <ul> <li>The role uses the shared trust policy with your email domain in the <code>sub</code> condition.</li> <li>You attached a permissions policy your team is comfortable with (start with <code>ReadOnlyAccess</code>).</li> <li>You have the role ARN written down -- developers need it in Step 3.</li> </ul> </div> --- ## Step 3 -- Configure the Vouch CLI {{< role developer >}} Each developer runs the wizard -- it asks the same **"How do you access AWS?"** question and writes the AWS profile for you: ```bash vouch setup aws ``` For single-account access you can skip the prompts by passing the role directly: ```bash vouch setup aws --role arn:aws:iam::123456789012:role/VouchDeveloper ``` This command accepts the following flags: - `--role` -- The ARN of the IAM role from Step 2. Omit all flags to launch the interactive wizard. - `--profile` -- The AWS profile name to write credentials to (default: `vouch`; additional profiles auto-name as `vouch-2`, `vouch-3`, etc.). For multi-account setups, `vouch setup aws` also accepts `--management-role`, `--identity-center-application`, `--region`, and `--discover` -- see [Multi-Account AWS](/docs/aws-multi-account/). The command writes a `credential_process` entry into `~/.aws/config` so that the AWS CLI and SDKs automatically call `vouch credential aws` whenever credentials are needed: ```ini [profile vouch] credential_process = vouch credential aws --role arn:aws:iam::123456789012:role/VouchDeveloper ``` > **Note:** If you need a specific region for this profile, add a `region` line manually (e.g., `region = us-east-1`). --- ## Step 4 -- Test {{< role developer >}} Verify that everything is working: ```bash # Make sure you are logged in vouch login # Check your identity aws sts get-caller-identity --profile vouch ``` Expected output: ```json { "UserId": "AROA...:alice@example.com", "Account": "123456789012", "Arn": "arn:aws:sts::123456789012:assumed-role/VouchDeveloper/alice@example.com" } ``` Try running a command against a real AWS service: ```bash aws s3 ls --profile vouch ``` <div class="checkpoint"> <p><strong>You are done when...</strong></p> <ul> <li><code>aws sts get-caller-identity --profile vouch</code> returns an assumed-role ARN for the expected AWS account.</li> <li>The role session name matches the authenticated Vouch user.</li> <li>A real AWS command succeeds without static credentials in <code>~/.aws/credentials</code>.</li> </ul> </div> --- ## Console access Open the AWS Management Console directly from the CLI without entering credentials in a browser: ```bash vouch aws console ``` This uses your active Vouch session to obtain temporary STS credentials, exchanges them for a federation sign-in token, and opens the console in your default browser. Pass `--role` to specify a role, or omit it to use the role from your configured AWS profile. For [IAM Identity Center](/docs/aws-multi-account/#aws-iam-identity-center) access, pass `--account <id> --permission-set <name>` (add `--idc-application <arn>` when more than one instance is configured); use `--via <management-role-arn>` to select a management role when multiple organizations are configured. --- ## Tips for restricting access ### Restrict by email address Limit role assumption to specific users by adding an email condition to the trust policy: ```json "Condition": { "StringEquals": { "us.vouch.sh:aud": "https://us.vouch.sh", "us.vouch.sh:sub": ["user@example.com"] }, "StringLike": { "sts:RoleSessionName": "${us.vouch.sh:sub}" }, "Bool": { "sts:RoleAuthorizedByIdp": "true" } } ``` ### Restrict by email domain Allow any user from a specific domain: ```json "Condition": { "StringEquals": { "us.vouch.sh:aud": "https://us.vouch.sh" }, "StringLike": { "us.vouch.sh:sub": "*@example.com", "sts:RoleSessionName": "${us.vouch.sh:sub}" }, "Bool": { "sts:RoleAuthorizedByIdp": "true" } } ``` ### Revoke access for a specific user There are two complementary techniques for cutting off a user. Use both together for full offboarding. **1. Immediate revocation via an explicit `Deny` on the role's permissions policy.** Permissions policies are evaluated on every AWS API call, and explicit `Deny` always wins. Because Vouch sets the `vouch:Email` session tag on every assumed-role session (see [Session tags](#session-tags)), you can block an offboarded user on the next API call -- including calls made with STS credentials that the Vouch agent has already cached: ```json { "Version": "2012-10-17", "Statement": [ { "Sid": "DenyOffboardedUsers", "Effect": "Deny", "Action": "*", "Resource": "*", "Condition": { "StringEquals": { "aws:PrincipalTag/vouch:Email": [ "former-employee@example.com" ] } } } ] } ``` Attach this as an inline policy on each Vouch role, or apply it as a [Service Control Policy](/docs/aws-multi-account/) in the management account to enforce it organization-wide. Because Vouch marks session tags as transitive, the `vouch:Email` tag propagates through role chains, so the same condition works in both the entry role and any spoke roles. **2. Block new role assumption via the trust policy.** Add a `StringNotEquals` clause to the trust policy listing the emails to deny. The next `AssumeRoleWithWebIdentity` call from that user will fail: ```json "Condition": { "StringEquals": { "us.vouch.sh:aud": "https://us.vouch.sh" }, "StringLike": { "us.vouch.sh:sub": "*@example.com", "sts:RoleSessionName": "${us.vouch.sh:sub}" }, "StringNotEquals": { "us.vouch.sh:sub": ["former-employee@example.com"] }, "Bool": { "sts:RoleAuthorizedByIdp": "true" } } ``` On its own, this only takes effect when the Vouch agent's cached STS credentials expire (up to 1 hour later), because the trust policy is evaluated at `AssumeRoleWithWebIdentity` time, not on every API call. Combined with the deny statement above, it provides a durable record of who is offboarded and prevents the role from being re-assumed even after the deny statement is later removed. **For full offboarding**, also deactivate the user in Vouch so they cannot start new sessions and existing non-AWS credentials (like SSH certificates) are cut off: - **With [SCIM](/docs/scim/) provisioning**, deactivating the user in your identity provider automatically revokes their active Vouch session and blocks future logins -- no manual step required. - **Without SCIM**, an administrator can use the **Deactivate** and **Revoke credentials** actions in the [admin console](/docs/admin/) to do the same thing. Note that revoking the Vouch session does not invalidate STS credentials already cached in the user's local Vouch agent -- the explicit `Deny` from step 1 is what blocks those on the AWS side. The Vouch-side action prevents new sessions and severs other credentials issued from the same session. ### Prevent session name spoofing The `RoleSessionName` is a client-provided STS API parameter. Without a trust policy condition, someone with a valid Vouch JWT could set it to another user's email, making CloudTrail session ARNs misleading. The `sts:RoleSessionName` condition binds the session name to the authenticated `sub` claim from the validated JWT: ```json "StringLike": { "sts:RoleSessionName": "${us.vouch.sh:sub}" } ``` All trust policy examples in this guide include this condition. AWS rejects any `AssumeRoleWithWebIdentity` call where the session name does not match the OIDC subject. > **Note:** The immutable `SourceIdentity` claim (set to the user's email) provides a second attribution anchor in CloudTrail that cannot be spoofed regardless of this condition. ### Require role pinning The Vouch CLI requests each OIDC token pinned to the role it is about to assume, and the Vouch server embeds that role ARN in the token's `https://aws.amazon.com/roles` claim. AWS STS enforces the claim: a pinned token can only be exchanged for the exact role it was minted for, so a leaked token cannot assume any other role that trusts the Vouch issuer. This happens automatically -- no configuration is required. The trust policy makes pinning mandatory with the [`sts:RoleAuthorizedByIdp`](https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_condition-keys.html#condition-keys-sts) condition key: ```json "Condition": { "Bool": { "sts:RoleAuthorizedByIdp": "true" } } ``` With this in place, AWS rejects any `AssumeRoleWithWebIdentity` call whose token does not name this role in its `roles` claim. All trust policy examples in this guide include this condition. Two caveats: - **Roll the CLI out first.** Older CLI versions do not request pinning, so their tokens carry no `roles` claim and fail the condition. If part of your team is on an older CLI, leave the condition out until everyone has upgraded. - **Web-identity trust statements only.** The condition key is defined for `AssumeRoleWithWebIdentity`. A role assumed by a plain SigV4 `sts:AssumeRole` call -- such as a [spoke role](/docs/aws-multi-account/) in a management-role chain -- has no OIDC token in the request, so a `Bool`-`true` condition there can never match. Matching is by exact role ARN; wildcards are not supported. ### Session tags Vouch sets the following session tags when assuming a role, which you can use in IAM policies for [attribute-based access control (ABAC)](https://docs.aws.amazon.com/IAM/latest/UserGuide/introduction_attribute-based-access-control.html): | Tag Key | Value | Example | |---------|-------|---------| | `vouch:Email` | The user's verified email | `alice@example.com` | | `vouch:Domain` | The user's organization domain (from the OIDC `hd` claim) | `example.com` | | `vouch:AccessType` | Set to `ai` when an AI coding agent is detected | `ai` | | `vouch:Agent` | The detected agent name (only present when an agent is detected) | `claude-code` | You can reference these tags in IAM policy conditions using `aws:PrincipalTag`: ```json { "Condition": { "StringEquals": { "aws:PrincipalTag/vouch:Domain": "example.com" } } } ``` Because Vouch marks all tags as [transitive](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_session-tags.html#id_session-tags_role-chaining), they automatically propagate through role chains. If a developer assumes a hub role via Vouch and then chains into a spoke role in another account, the spoke role's trust policy can still evaluate `aws:PrincipalTag/vouch:Email` and `aws:PrincipalTag/vouch:Domain` conditions. --- ## AI agent safety When `vouch credential aws` runs inside an AI coding agent, Vouch automatically restricts the returned credentials to read-only access. No configuration is required -- the CLI detects the agent environment and applies the restriction transparently. > **Note:** This read-only downscoping applies to the STS credential paths (`--role`, including role chaining). On the [IAM Identity Center](/docs/aws-multi-account/#aws-iam-identity-center) path (`--account`/`--permission-set`), Vouch **refuses to issue credentials to a detected agent** rather than downscoping them -- permission-set credentials cannot be constrained with a session policy, so there is no way to enforce read-only access. Agent workflows that need AWS access should use the STS role-chaining model. ### How it works The CLI checks for environment variables set by popular AI coding agents. When one is detected: 1. The [`ReadOnlyAccess`](https://docs.aws.amazon.com/aws-managed-policy/latest/reference/ReadOnlyAccess.html) AWS managed policy is attached as a [session policy](https://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies.html#policies_session), which limits the effective permissions to the intersection of the role's policies and `ReadOnlyAccess` -- regardless of what the role itself allows. 2. The `vouch:AccessType=ai` and `vouch:Agent=<name>` session tags are added, where `<name>` is the verbatim value of the detected agent environment variable (for agents that set `AI_AGENT` or `AGENT`, the raw value is forwarded; for agents detected by a marker variable like `CLAUDE_CODE` or `CURSOR_TRACE_ID`, the agent name is used). These tags appear on every CloudTrail event for the session, so you can attribute API calls to the specific agent that made them. ### Supported agents Vouch detects the following AI coding agents: | Agent | Environment Variable | |-------|---------------------| | Claude Code | `AI_AGENT` or `CLAUDE_CODE` | | Cursor | `CURSOR_TRACE_ID` | | GitHub Copilot | `COPILOT_MODEL` | | OpenAI Codex | `CODEX_SANDBOX` | | Google Gemini | `GEMINI_CLI` | | Augment | `AUGMENT_AGENT` | | Cline | `CLINE_ACTIVE` | | Amp | `AGENT=amp` | | Goose | `AGENT=goose` | If your agent is not listed, it will be detected if it sets the `AGENT` or `AI_AGENT` environment variable (an emerging convention). ### Role chaining with agents When using [role chaining](/docs/aws-multi-account/#architecture) with an AI agent, Vouch applies an additional inline session policy to the management-account hop that restricts it to STS actions only (`sts:AssumeRole`, `sts:TagSession`, `sts:SetSourceIdentity`). The final role hop receives the `ReadOnlyAccess` session policy. --- ## How it works `vouch login` authenticates the developer with their YubiKey, and the Vouch server issues a short-lived OIDC ID token carrying their email in the `sub` claim and the target role ARN in the `https://aws.amazon.com/roles` claim. When the developer runs an AWS command, the CLI calls **AWS STS AssumeRoleWithWebIdentity** with that token; AWS validates it against the Vouch server and returns temporary credentials valid for up to 1 hour. Because the token is scoped to the authenticated user, [pinned to the requested role](#require-role-pinning), and short-lived, credentials cannot be shared, redirected to another role, or reused after expiry. --- ## How Vouch compares to `aws login` and `aws sso login` AWS's built-in [`aws login`](https://docs.aws.amazon.com/signin/latest/userguide/command-line-sign-in.html#command-line-sign-in-local-development) and [`aws sso login`](https://docs.aws.amazon.com/signin/latest/userguide/command-line-sign-in.html#command-line-sign-in-sso) cover AWS only. `vouch login` covers AWS *and* SSH, GitHub, Docker, Cargo, CodeCommit, CodeArtifact, and databases -- from one phishing-resistant hardware-key session where every credential traces back to a physical key. | | `aws login` | `aws sso login` | `vouch login` | |---|---|---|---| | **Authentication** | Browser + console credentials | Browser + Identity Center | YubiKey tap (FIDO2) | | **Phishing-resistant** | Depends on IdP | Depends on IdP | Yes (hardware-bound) | | **AWS credentials** | Yes (up to 12h) | Yes | Yes (up to 1h) | | **SSH, GitHub, Docker, etc.** | No | No | Yes | | **Identity in CloudTrail** | IAM user or role | SSO user | Hardware-verified user | | **Requires AWS-managed service** | No | IAM Identity Center | No | If you already use IAM Identity Center, `aws sso login` may cover your AWS needs. Vouch fits when you want one authentication event to cover AWS and everything else your team uses. --- ## Troubleshooting ### "Not authorized to perform sts:AssumeRoleWithWebIdentity" - Verify the OIDC provider URL in the IAM trust policy matches `https://us.vouch.sh` exactly (no trailing slash). - Confirm the `aud` condition matches the client ID registered with the OIDC provider. - Ensure the trust policy `Action` includes all three required actions: `sts:AssumeRoleWithWebIdentity`, `sts:SetSourceIdentity`, and `sts:TagSession`. - If the trust policy requires [`sts:RoleAuthorizedByIdp`](#require-role-pinning), make sure the CLI is up to date -- older CLI versions issue tokens without the `roles` claim and fail the condition. ### "Token is expired" - Run `vouch login` again to refresh your session. OIDC tokens are short-lived by design. ### "Invalid identity token" - Ensure the OIDC provider in AWS points to the correct Vouch server URL: `https://us.vouch.sh`. - Verify that the Vouch server's JWKS endpoint (`https://us.vouch.sh/oauth/jwks`) is reachable from the internet (AWS must be able to fetch it). ### Credentials not appearing in the expected profile - Run `vouch setup aws` again and verify the profile name. - Check `~/.aws/config` for conflicting profile definitions. ### Permission errors after assuming the role - The trust policy controls who can assume the role; the permissions policy controls what they can do. Verify the correct permissions policies are attached to the IAM role. --- ## Related guides - [Multi-Account AWS Strategy](/docs/aws-multi-account/) -- Deploy Vouch across multiple AWS accounts with CloudFormation StackSets or Terraform modules. - [Amazon EKS](/docs/eks/) -- Use your Vouch-backed AWS credentials to authenticate to Kubernetes clusters. - [AWS Systems Manager](/docs/ssm/) -- Connect to EC2 instances through Session Manager using Vouch credentials. - [Database Authentication](/docs/databases/) -- Connect to RDS, Aurora, and Redshift with IAM authentication. - [AWS CodeArtifact](/docs/codeartifact/) -- Authenticate to package repositories using Vouch. - [Infrastructure as Code](/docs/iac/) -- Use CDK, Terraform, SAM, and other IaC tools with Vouch credentials. - [Claude & OpenAI APIs](/docs/ai-api-keys/) -- Replace long-lived AI provider API keys with short-lived tokens via Workload Identity Federation. --- # Vouch CLI Reference Source: https://vouch.sh/docs/cli-reference/ This page documents all available Vouch CLI commands. For installation instructions, see [Getting Started](/docs/getting-started/). ## Quick links - [Global flags](#global-flags) - [Authentication](#authentication) - [AWS](#aws) - [Setup](#setup) - [Credentials](#credentials) - [Key management](#key-management) - [Environment](#environment) - [Device posture](#device-posture) - [Diagnostics](#diagnostics) - [Exit codes](#exit-codes) - [Binary download verification](#binary-download-verification) --- ## Global flags These flags are available on all commands. | Flag | Description | |---|---| | `--server <URL>` | Vouch server URL (also settable via `VOUCH_SERVER` environment variable). Saved locally after enrollment. | | `-v`, `--verbose` | Enable debug logging | | `--color <MODE>` | Control color output: `auto` (default), `always`, or `never` | ### Configuration file The Vouch CLI follows the [XDG Base Directory specification](https://specifications.freedesktop.org/basedir-spec/latest/) on all platforms, including macOS: configuration lives at `~/.config/vouch/config.json` (`$XDG_CONFIG_HOME`), session state (cookie, audit log) under `~/.local/state/vouch/` (`$XDG_STATE_HOME`), and the agent sockets under `$XDG_RUNTIME_DIR/vouch/` (falling back to `~/.cache/vouch/` where `XDG_RUNTIME_DIR` is unset). The configuration file is created automatically during enrollment and contains the server URL and session state. | Field | Description | |---|---| | `server_url` | Vouch server URL | | `token` | Current session token (set by `vouch login`) | **Precedence:** CLI flags (`--server`) override the `VOUCH_SERVER` environment variable, which overrides the config file value. On Unix, the config file must have restrictive permissions (`0600`). The CLI rejects files that are group- or world-readable. --- ## Authentication ### `vouch enroll` Register your YubiKey with a Vouch server and link it to your identity. ``` vouch enroll --server <SERVER_URL> ``` You only need to enroll once per YubiKey. ### `vouch login` Authenticate with your YubiKey and start an 8-hour session. ``` vouch login [--timeout <SECONDS>] ``` | Flag | Description | |---|---| | `--timeout` | Timeout in seconds for YubiKey detection (default: `60`, use `0` for no timeout) | During login, the CLI automatically collects [device posture signals](/docs/device-posture/) and sends them to the server for policy evaluation. After login, all credential helpers use the session automatically. Run this once at the start of each workday. ### `vouch logout` End the current session and clear all cached credentials. ``` vouch logout ``` ### `vouch status` Display the current session status, including remaining session time and active integrations (SSH, AWS, SSM, Git, Docker, Cargo, Claude, OpenAI). ``` vouch status [--format <FORMAT>] ``` | Flag | Description | |---|---| | `--format` | Output format: `human` (default), `json`, or `shell`. The `shell` format outputs key=value pairs suitable for `eval`. | --- ## AWS Commands for authenticating with AWS IAM Identity Center and discovering available accounts and roles. ### `vouch aws login` Authenticate with AWS IAM Identity Center SSO. ``` vouch aws login [--sso-session <NAME>] ``` | Flag | Description | |---|---| | `--sso-session` | Named SSO session from `~/.aws/config` (optional; uses default if not specified) | ### `vouch aws accounts` List AWS accounts available through IAM Identity Center. ``` vouch aws accounts [--sso-session <NAME>] [--json] ``` | Flag | Description | |---|---| | `--sso-session` | Named SSO session (optional) | | `--json` | Output as JSON | ### `vouch aws roles` List IAM roles available in an AWS account through IAM Identity Center. ``` vouch aws roles [--sso-session <NAME>] [--account <ACCOUNT_ID>] [--json] ``` | Flag | Description | |---|---| | `--sso-session` | Named SSO session (optional) | | `--account` | AWS account ID to query (optional; lists roles across all accounts if not specified) | | `--json` | Output as JSON | See [Multi-Account AWS Strategy](/docs/aws-multi-account/) for full details. ### `vouch aws console` Open the AWS Management Console in your browser. ``` vouch aws console [--role <ROLE_ARN>] ``` | Flag | Description | |---|---| | `--role` | AWS IAM role ARN to assume (auto-detected from `~/.aws/config` if not specified) | This uses your active Vouch session to obtain temporary STS credentials, exchanges them for a federation sign-in token, and opens the console in your default browser. --- ## Setup Setup commands configure credential helpers for each integration. Run these once per machine. ### `vouch setup aws` Configure the AWS credential process for an IAM role, or auto-discover accounts and roles from IAM Identity Center. ``` vouch setup aws (--role <ROLE_ARN> | --discover) [--profile <PROFILE>] [--prefix <PREFIX>] [--region <REGION>] ``` | Flag | Description | |---|---| | `--role` | The IAM role ARN to assume (required unless `--discover` is used) | | `--discover` | Auto-discover accounts and roles from IAM Identity Center SSO (alternative to `--role`) | | `--profile` | AWS profile name to configure (default: `vouch`; additional profiles auto-name as `vouch-2`, `vouch-3`, etc.) | | `--prefix` | Prefix for auto-generated profile names when using `--discover` | | `--region` | AWS region to set in the profile | See [AWS Integration](/docs/aws/) for full details. ### `vouch setup ssh` Configure the SSH client to use the Vouch agent for certificate authentication. ``` vouch setup ssh [--hosts <PATTERN>] ``` | Flag | Description | |---|---| | `--hosts` | Host patterns to trust with this CA (e.g., `*.example.com`). If specified, adds an entry to `~/.ssh/known_hosts`. | See [SSH Certificates](/docs/ssh/) for full details. ### `vouch setup github` Configure Git to use Vouch as the credential helper for GitHub. ``` vouch setup github [--host <HOST>] [--configure] ``` | Flag | Description | |---|---| | `--host` | GitHub host to configure (default: `github.com`) | | `--configure` | Apply the configuration automatically (without this flag, the command only prints the configuration) | See [GitHub Integration](/docs/github/) for full details. ### `vouch setup docker` Configure Docker to use Vouch as the credential helper for container registries. ``` vouch setup docker [--configure] [REGISTRIES...] ``` | Flag | Description | |---|---| | `--configure` | Apply the configuration automatically (without this flag, the command only prints the configuration) | | `REGISTRIES` | Container registry URLs to configure (e.g., `ghcr.io`) | See [Docker Registries](/docs/docker/) for full details. ### `vouch setup cargo` Configure Cargo to use Vouch as the credential provider for private registries. ``` vouch setup cargo [--registry <NAME>] [--configure] ``` | Flag | Description | |---|---| | `--registry` | Name of the Cargo registry to configure | | `--configure` | Apply the configuration automatically | See [Cargo Integration](/docs/cargo/) for full details. ### `vouch setup codeartifact` Configure a package manager for an AWS CodeArtifact repository. ``` vouch setup codeartifact --tool <TOOL> --repository <REPO> [--domain <DOMAIN>] [--domain-owner <ACCOUNT_ID>] [--region <REGION>] [--profile <PROFILE>] ``` | Flag | Description | |---|---| | `--tool` | Package manager to configure: `cargo`, `pip`, `npm`, `pnpm`, or `uv` (required) | | `--repository` | The AWS CodeArtifact repository name (required) | | `--domain` | The AWS CodeArtifact domain name (optional if a profile is configured) | | `--domain-owner` | AWS account ID that owns the domain (optional if a profile is configured) | | `--region` | AWS region (optional if a profile is configured) | | `--profile` | Named AWS CodeArtifact profile to use or create (stores domain/owner/region for reuse) | See [AWS CodeArtifact](/docs/codeartifact/) for full details. ### `vouch setup codecommit` Configure Git to use Vouch as the credential helper for AWS CodeCommit. ``` vouch setup codecommit [--region <REGION>] [--profile <PROFILE>] [--configure] ``` | Flag | Description | |---|---| | `--region` | AWS region (default: wildcard matching all regions) | | `--profile` | AWS profile to use (defaults to auto-detected vouch profile) | | `--configure` | Apply the configuration automatically (without this flag, the command only prints the configuration) | See [AWS CodeCommit](/docs/codecommit/) for full details. ### `vouch setup eks` Configure kubectl to use Vouch for EKS cluster authentication. ``` vouch setup eks --cluster <CLUSTER_NAME> [--region <REGION>] [--profile <PROFILE>] [--kubeconfig <PATH>] ``` | Flag | Description | |---|---| | `--cluster` | The EKS cluster name (required) | | `--region` | AWS region (auto-detected from AWS profile or environment if not specified) | | `--profile` | AWS profile to use (defaults to auto-detected vouch profile) | | `--kubeconfig` | Path to kubeconfig file (defaults to `~/.kube/config`) | See [Amazon EKS](/docs/eks/) for full details. ### `vouch setup k8s` Configure kubectl to use Vouch for Kubernetes OIDC authentication. This works with any Kubernetes distribution that supports OIDC (self-hosted, GKE, AKS, k3s, etc.). ``` vouch setup k8s --cluster <NAME> --server <URL> [--certificate-authority <PATH>] [--audience <AUDIENCE>] [--kubeconfig <PATH>] ``` | Flag | Description | |---|---| | `--cluster` | Kubernetes cluster name (required) | | `--server` | Kubernetes API server URL, e.g., `https://k8s.example.com:6443` (required) | | `--certificate-authority` | Path to the cluster's CA certificate file (PEM format) | | `--audience` | OIDC audience — must match `--oidc-client-id` on the API server (default: `kubernetes`) | | `--kubeconfig` | Path to kubeconfig file (defaults to `~/.kube/config`) | See [Kubernetes](/docs/kubernetes/) for full details. ### `vouch setup anthropic` Configure Anthropic (Claude) Workload Identity Federation. Persists federation parameters to `~/.config/vouch/config.json` and auto-merges `~/.claude/settings.json` to set `apiKeyHelper` → `vouch credential anthropic` plus `env.CLAUDE_CODE_API_KEY_HELPER_TTL_MS` so Claude Code re-runs the helper before the token expires. ``` vouch setup anthropic --federation-rule-id <ID> --organization-id <UUID> --service-account-id <ID> --workspace-id <ID> [--audience <AUD>] [--token-endpoint <URL>] [--force] ``` | Flag | Description | |---|---| | `--federation-rule-id` | Anthropic federation rule ID (`fdrl_...`) (required) | | `--organization-id` | Anthropic organization ID (UUID) (required) | | `--service-account-id` | Anthropic service account ID (`svac_...`) (required) | | `--workspace-id` | Anthropic workspace ID (`wrkspc_...`) (required) | | `--audience` | `aud` claim to request on the assertion (optional; most federation rules match on `sub` alone) | | `--token-endpoint` | Override the Anthropic token endpoint (defaults to Anthropic's public endpoint) | | `--force` | Overwrite an existing Claude Code `apiKeyHelper` configuration | See [Claude & OpenAI APIs](/docs/ai-api-keys/) for full details. ### `vouch setup openai` Configure OpenAI Workload Identity Federation. Persists federation parameters to `~/.config/vouch/config.json` and auto-merges `~/.codex/config.toml` to add a `[model_providers.vouch]` block (with a refreshing `auth` command) and set the top-level `model_provider = "vouch"`. ``` vouch setup openai --identity-provider-id <ID> --service-account-id <ID> [--audience <AUD>] [--token-endpoint <URL>] [--force] ``` | Flag | Description | |---|---| | `--identity-provider-id` | OpenAI Workload Identity Provider ID for the Vouch issuer (required) | | `--service-account-id` | OpenAI service account ID (required) | | `--audience` | `aud` claim to request on the assertion (matches the audience OpenAI configured for the Vouch issuer) | | `--token-endpoint` | Override the OpenAI token endpoint (defaults to OpenAI's public endpoint) | | `--force` | Switch Codex's top-level `model_provider` away from another provider already in place | OpenAI must onboard the Vouch issuer as a workload identity provider before this works — custom OIDC issuers are not self-service on OpenAI's side. See [Claude & OpenAI APIs](/docs/ai-api-keys/) for full details. ### `vouch setup ssm` Configure SSH to use AWS Systems Manager Session Manager as a proxy for connections to EC2 and managed instances. ``` vouch setup ssm [--profile <PROFILE>] [--region <REGION>] [--hosts <HOSTS>] [--force] ``` | Flag | Description | |---|---| | `--profile` | AWS profile to use (defaults to auto-detected vouch profile) | | `--region` | AWS region to use in the ProxyCommand | | `--hosts` | Host patterns to match (default: `i-* mi-*`) | | `--force` | Overwrite any existing SSM configuration in `~/.ssh/config` | See [AWS Systems Manager](/docs/ssm/) for full details. --- ## Credentials Credential commands obtain service-specific credentials from your active session. These are typically called automatically by credential helpers, but can be run manually for debugging. ### `vouch credential aws` Obtain temporary AWS STS credentials. ``` vouch credential aws --role <ROLE_ARN> ``` | Flag | Description | |---|---| | `--role` | The IAM role ARN to assume (required) | ### `vouch credential ssh` Obtain an SSH certificate from the Vouch server. ``` vouch credential ssh [--key <PATH>] ``` | Flag | Description | |---|---| | `--key` | Path to SSH private key (default: `~/.ssh/id_ed25519_vouch`) | ### `vouch credential codeartifact` Obtain an AWS CodeArtifact authorization token. ``` vouch credential codeartifact [--domain <DOMAIN>] [--domain-owner <ACCOUNT_ID>] [--region <REGION>] [--profile <PROFILE>] ``` | Flag | Description | |---|---| | `--domain` | The AWS CodeArtifact domain name (optional if a profile is configured) | | `--domain-owner` | AWS account ID that owns the domain (optional if a profile is configured) | | `--region` | AWS region (optional if a profile is configured) | | `--profile` | Named AWS CodeArtifact profile to use | ### `vouch credential rds` Generate an RDS IAM authentication token for database connections. The token is valid for 15 minutes. ``` vouch credential rds --hostname <HOSTNAME> --username <USERNAME> [--port <PORT>] [--region <REGION>] [--role <ROLE>] ``` | Flag | Description | |---|---| | `--hostname` | RDS instance hostname (required) | | `--username` | Database username (required) | | `--port` | Database port (default: `5432`) | | `--region` | AWS region (auto-detected if not specified) | | `--role` | AWS IAM role ARN (auto-detected from vouch profile if not specified) | Example: ```bash TOKEN=$(vouch credential rds \ --hostname mydb.cluster-abc123.us-east-1.rds.amazonaws.com \ --username mydbuser) PGPASSWORD="$TOKEN" psql -h mydb.cluster-abc123.us-east-1.rds.amazonaws.com -U mydbuser -d mydb "sslmode=require" ``` ### `vouch credential redshift` Generate temporary credentials for Amazon Redshift. Supports both provisioned clusters and Redshift Serverless workgroups. ``` vouch credential redshift (--cluster-id <ID> | --workgroup <NAME>) [--db-name <NAME>] [--region <REGION>] [--role <ROLE>] [--duration <SECONDS>] ``` | Flag | Description | |---|---| | `--cluster-id` | Redshift provisioned cluster identifier (mutually exclusive with `--workgroup`) | | `--workgroup` | Redshift Serverless workgroup name (mutually exclusive with `--cluster-id`) | | `--db-name` | Database name (optional) | | `--region` | AWS region (auto-detected if not specified) | | `--role` | AWS IAM role ARN (auto-detected from vouch profile if not specified) | | `--duration` | Credential duration in seconds, 900--3600 (provisioned clusters only, default: `900`) | Examples: ```bash # Provisioned cluster vouch credential redshift --cluster-id my-cluster --db-name mydb # Serverless workgroup vouch credential redshift --workgroup my-workgroup --db-name mydb ``` ### `vouch credential k8s` Obtain an OIDC token for Kubernetes authentication. Outputs an [`ExecCredential`](https://kubernetes.io/docs/reference/config-api/client-authentication.v1/) JSON object for use as a kubectl exec-based credential plugin. ``` vouch credential k8s --cluster <NAME> [--audience <AUDIENCE>] ``` | Flag | Description | |---|---| | `--cluster` | Kubernetes cluster name — used as cache key (required) | | `--audience` | OIDC audience — must match `--oidc-client-id` on the API server (default: `kubernetes`) | ### `vouch credential anthropic` Obtain a short-lived Anthropic (Claude) API token via Workload Identity Federation ([RFC 7523](https://datatracker.ietf.org/doc/html/rfc7523) `jwt-bearer` grant). Requires `vouch setup anthropic` and an active session. ``` vouch credential anthropic ``` Prints a bare `sk-ant-oat01-...` token to stdout with no trailing newline — designed to be invoked by Claude Code's `apiKeyHelper`. The token is cached until just before its expiry; subsequent invocations within that window return the cached value. See [Claude & OpenAI APIs](/docs/ai-api-keys/) for full details. ### `vouch credential openai` Obtain a short-lived OpenAI API token via Workload Identity Federation ([RFC 8693](https://datatracker.ietf.org/doc/html/rfc8693) token-exchange grant). Requires `vouch setup openai`, an active session, and that OpenAI has onboarded the Vouch issuer. ``` vouch credential openai ``` Prints a bare OpenAI access token to stdout with no trailing newline — designed to be invoked by the OpenAI Codex CLI as a `[model_providers.*.auth]` command with `refresh_interval_ms`. The token is cached until just before its expiry. See [Claude & OpenAI APIs](/docs/ai-api-keys/) for full details. ### `vouch credential token` Print the raw session access token to stdout for use with curl or other tools. ``` vouch credential token ``` Example: ```bash curl -H "Authorization: Bearer $(vouch credential token)" https://api.example.com/endpoint ``` --- ## Key management ### `vouch register` Register an additional YubiKey with your account. ``` vouch register [--name <NAME>] [--timeout <SECONDS>] ``` | Flag | Description | |---|---| | `--name` | Human-readable name for this YubiKey (default: `YubiKey`) | | `--timeout` | Timeout in seconds for YubiKey detection (default: `60`, use `0` for no timeout) | This allows you to use multiple hardware keys (e.g., a primary and a backup) with the same Vouch identity. ### `vouch keys list` List all registered security keys for your account. ``` vouch keys list [--json] ``` | Flag | Description | |---|---| | `--json` | Output as JSON | ### `vouch keys remove` Remove a registered security key from your account. ``` vouch keys remove <KEY_ID> [--force] ``` | Flag | Description | |---|---| | `-f`, `--force` | Skip the confirmation prompt | ### `vouch keys rename` Rename a registered security key. ``` vouch keys rename <KEY_ID> <NEW_NAME> ``` --- ## Environment Both `vouch exec` and `vouch env` inject credentials as environment variables. They accept the same type-specific flags and set the same variables -- `exec` runs a command with the variables injected, while `env` outputs shell export statements for use with `eval`. ### Shared flags | Flag | Description | |---|---| | `--type` | Credential type: `aws`, `github`, `codeartifact`, `rds`, or `redshift` (required) | | `--role` | AWS IAM role ARN (required when `--type aws`) | | `--codeartifact-domain` | AWS CodeArtifact domain name (when `--type codeartifact`; optional if a profile is configured) | | `--codeartifact-domain-owner` | AWS account ID that owns the domain (when `--type codeartifact`; optional if a profile is configured) | | `--codeartifact-region` | AWS region (when `--type codeartifact`; optional if a profile is configured) | | `--codeartifact-profile` | Named AWS CodeArtifact profile to use (when `--type codeartifact`) | | `--rds-hostname` | RDS instance hostname (required when `--type rds`) | | `--rds-username` | Database username (required when `--type rds`) | | `--rds-port` | Database port (when `--type rds`, default: `5432`) | | `--rds-region` | AWS region (when `--type rds`; auto-detected if not specified) | | `--redshift-cluster-id` | Redshift provisioned cluster identifier (when `--type redshift`; mutually exclusive with `--redshift-workgroup`) | | `--redshift-workgroup` | Redshift Serverless workgroup name (when `--type redshift`; mutually exclusive with `--redshift-cluster-id`) | | `--redshift-db-name` | Database name (when `--type redshift`) | | `--redshift-duration` | Credential duration in seconds, 900--3600 (when `--type redshift`, provisioned clusters only, default: `900`) | | `--redshift-region` | AWS region (when `--type redshift`; auto-detected if not specified) | ### Environment variables by type | Type | Variables | |---|---| | `aws` | `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`, `AWS_SESSION_TOKEN` | | `github` | `GITHUB_TOKEN`, `GH_TOKEN` | | `codeartifact` | `CODEARTIFACT_AUTH_TOKEN` | | `rds` | `PGPASSWORD`, `PGHOST`, `PGPORT`, `PGUSER`, `PGSSLMODE` | | `redshift` | `PGPASSWORD`, `PGUSER`, `PGSSLMODE` | ### `vouch exec` Run a command with Vouch credentials injected as environment variables. ``` vouch exec --type <TYPE> [FLAGS...] -- <COMMAND> [ARGS...] ``` Examples: ```bash # AWS credentials vouch exec --type aws --role arn:aws:iam::123456789012:role/VouchDeveloper -- terraform plan # AWS CodeArtifact token vouch exec --type codeartifact -- mvn deploy -s settings.xml # RDS PostgreSQL — connect with psql, no manual token handling vouch exec --type rds \ --rds-hostname mydb.cluster-abc123.us-east-1.rds.amazonaws.com \ --rds-username mydbuser \ -- psql -d mydb # Redshift provisioned cluster vouch exec --type redshift \ --redshift-cluster-id my-cluster \ --redshift-db-name mydb \ -- psql -h my-cluster.abc123.us-east-1.redshift.amazonaws.com -p 5439 # Redshift Serverless vouch exec --type redshift \ --redshift-workgroup my-workgroup \ -- psql -h my-workgroup.123456789012.us-east-1.redshift-serverless.amazonaws.com -p 5439 ``` ### `vouch env` Output credential environment variables for use with `eval`. ``` eval "$(vouch env --type <TYPE> [--shell <SHELL>] [FLAGS...])" ``` | Flag | Description | |---|---| | `--shell` | Shell syntax: `bash` or `fish` (default: `bash`). The `bash` syntax also works for zsh. | Examples: ```bash # RDS PostgreSQL eval "$(vouch env --type rds \ --rds-hostname mydb.cluster-abc123.us-east-1.rds.amazonaws.com \ --rds-username mydbuser)" psql -d mydb # Redshift eval "$(vouch env --type redshift \ --redshift-cluster-id my-cluster \ --redshift-db-name mydb)" psql -h my-cluster.abc123.us-east-1.redshift.amazonaws.com -p 5439 ``` ### `vouch init` Output a shell hook that sets `VOUCH_AUTHENTICATED`, `VOUCH_EMAIL`, and `VOUCH_EXPIRES_IN` on each prompt. Add to your shell profile for ambient session awareness. ``` eval "$(vouch init <SHELL>)" ``` Supported shells: `bash`, `zsh`, `fish`. --- ## Device posture ### `vouch posture` Display the security posture signals detected on the current machine. This shows the same data that is sent to the server during `vouch login` for policy evaluation. ``` vouch posture [--format <FORMAT>] ``` | Flag | Description | |---|---| | `--format` | Output format: `text` (default) or `json`. The `json` format outputs the exact `authorization_details` payload sent during login. | This command does not require an active session — use it to verify device posture at any time. See [Device Posture Policies](/docs/device-posture/) for full details. --- ## Diagnostics ### `vouch doctor` Run diagnostic checks to verify your Vouch installation and configuration. ``` vouch doctor [--quiet] [--json] ``` | Flag | Description | |---|---| | `-q`, `--quiet` | Suppress all output (exit code only) | | `--json` | Output results as JSON | This checks: - CLI version and updates - Agent connectivity - Server reachability - Integration configurations (SSH, AWS, SSM, EKS, Git, Docker, Cargo) - Claude / OpenAI federation (cross-checks `~/.config/vouch/config.json` against Claude Code's `apiKeyHelper` and Codex's `model_provider`) ### `vouch completions` Generate shell completion scripts. ``` vouch completions <SHELL> ``` Supported shells: `bash`, `zsh`, `fish`, `powershell`, `elvish`. Example: ```bash # Add to your ~/.zshrc eval "$(vouch completions zsh)" ``` --- ## Exit codes | Code | Meaning | |---|---| | `0` | Success | | `1` | General error | | `2` | Not authenticated (session expired or missing) | | `3` | Hardware key not detected | | `4` | Network or server unreachable | | `5` | Permission denied | | `6` | Configuration error | --- ## Binary download verification If you downloaded the Vouch CLI binary directly from the [GitHub releases](https://github.com/vouch-sh/vouch/releases) page, you can verify its integrity using the SHA256 checksums and SLSA provenance attestation published alongside each release. ### SHA256 checksum Each release includes a `checksums.txt` file. Verify the downloaded binary: ```bash sha256sum --check checksums.txt ``` ### SLSA provenance Vouch release binaries are built with SLSA Level 3 provenance. You can verify the provenance attestation using the [slsa-verifier](https://github.com/slsa-framework/slsa-verifier) tool: ```bash slsa-verifier verify-artifact vouch-linux-amd64 \ --provenance-path vouch-linux-amd64.intoto.jsonl \ --source-uri github.com/vouch-sh/vouch ``` --- # Replace SSH Keys with Short-Lived Certificates Source: https://vouch.sh/docs/ssh/ > **Windows:** SSH certificate integration is not available on Windows. See the [FAQ](/docs/faq/#does-vouch-work-on-windows) for details on Windows platform support. Vouch replaces static SSH keys with short-lived certificates. Administrators trust a single certificate authority (CA) key, and developers receive SSH certificates that expire after 8 hours. There is no key distribution, no `authorized_keys` sprawl, and no offboarding checklist. {{< tldr >}} - **Prerequisites:** [Getting Started](/docs/getting-started/) → this page. - **Admin, once:** [configure each SSH server](#step-2----configure-ssh-servers-for-administrators) to trust the Vouch CA public key. - **Each developer:** `vouch setup ssh`, then `ssh user@server` just works. {{< /tldr >}} ## Step 1 -- Set up the CLI (for developers) {{< role developer >}} Enable the SSH agent integration: ```bash vouch setup ssh ``` This command configures your local SSH client to use the Vouch agent for certificate authentication. It adds the following to your `~/.ssh/config`: ``` Host * IdentityAgent /run/user/1000/vouch/ssh-agent.sock ``` The socket path is resolved for your platform: `$XDG_RUNTIME_DIR/vouch/ssh-agent.sock` on Linux, falling back to `~/.cache/vouch/ssh-agent.sock` where `XDG_RUNTIME_DIR` is unset (e.g. on macOS). After setup, every `ssh` connection will automatically use your Vouch certificate when available, falling back to regular keys if needed. ### Verify the agent is running ```bash vouch status ``` Look for the SSH agent line in the output. If the agent is not running, `vouch login` will start it automatically. ### Check your certificate After logging in, inspect the current certificate: ```bash vouch credential ssh ``` This obtains a certificate from the server and displays the certificate's principals, validity period, and signing CA. --- ## Step 2 -- Configure SSH servers (for administrators) {{< role admin >}} To accept Vouch certificates, each server must trust the Vouch CA public key. You can find the CA public key on the Integrations page of the Vouch dashboard: ![Integrations page showing SSH Certificates with CA public key](/images/admin/integrations.png) ### Fetch the CA public key Or retrieve it programmatically from the Vouch server: ```bash curl -s https://us.vouch.sh/v1/credentials/ssh/ca | jq -r '.public_key' ``` Save the output -- you will need it for each method below. --- #### CLI (manual) On each server, add the CA public key and create a drop-in `sshd` configuration file: ```bash # Write the CA public key echo "CONTENTS_OF_CA_PUB" | sudo tee /etc/ssh/vouch_ca.pub # Configure sshd to trust the CA (drop-in config) # See: https://man.openbsd.org/sshd_config#TrustedUserCAKeys # See: https://man.openbsd.org/sshd_config#AuthorizedPrincipalsFile sudo tee /etc/ssh/sshd_config.d/99-vouch.conf <<'SSHD' TrustedUserCAKeys /etc/ssh/vouch_ca.pub AuthorizedPrincipalsFile /etc/ssh/auth_principals/%u SSHD # Create a principals file for a specific user sudo mkdir -p /etc/ssh/auth_principals echo "alice@example.com" | sudo tee /etc/ssh/auth_principals/alice # Restart sshd sudo systemctl restart sshd ``` #### Ansible ```yaml - name: Configure Vouch SSH CA trust hosts: all become: true vars: vouch_ca_pub: "{{ (lookup('url', 'https://us.vouch.sh/v1/credentials/ssh/ca') | from_json).public_key }}" tasks: - name: Write Vouch CA public key copy: content: "{{ vouch_ca_pub }}" dest: /etc/ssh/vouch_ca.pub owner: root group: root mode: "0644" - name: Configure sshd to trust Vouch CA copy: content: | TrustedUserCAKeys /etc/ssh/vouch_ca.pub AuthorizedPrincipalsFile /etc/ssh/auth_principals/%u dest: /etc/ssh/sshd_config.d/99-vouch.conf owner: root group: root mode: "0644" notify: restart sshd - name: Create auth_principals directory file: path: /etc/ssh/auth_principals state: directory owner: root group: root mode: "0755" handlers: - name: restart sshd service: name: sshd state: restarted ``` #### Terraform (AWS EC2 user data) ```hcl resource "aws_instance" "example" { ami = "ami-0abcdef1234567890" instance_type = "t3.micro" user_data = <<-EOF #!/bin/bash set -e # Fetch and install the Vouch CA public key curl -s https://us.vouch.sh/v1/credentials/ssh/ca \ | jq -r '.public_key' | tee /etc/ssh/vouch_ca.pub # Configure sshd to trust the CA (drop-in config) cat > /etc/ssh/sshd_config.d/99-vouch.conf <<'SSHD' TrustedUserCAKeys /etc/ssh/vouch_ca.pub AuthorizedPrincipalsFile /etc/ssh/auth_principals/%u SSHD mkdir -p /etc/ssh/auth_principals systemctl restart sshd EOF tags = { Name = "vouch-ssh-example" } } ``` --- ## Tip: understanding principals {{< role admin >}} Vouch certificates include two principals by default: | Principal | Example | Use case | |-----------|---------|----------| | Email address | `alice@example.com` | Unique per-user access control | | Username | `alice` | Matches standard Unix usernames | When configuring `AuthorizedPrincipalsFile`, you can list either the email or the username (or both) in the principals file for each Unix account. **Example:** To allow both `alice@example.com` and `bob@example.com` to SSH as the `deploy` user: ```bash sudo mkdir -p /etc/ssh/auth_principals printf "alice@example.com\nbob@example.com\n" | sudo tee /etc/ssh/auth_principals/deploy ``` If you do not configure `AuthorizedPrincipalsFile`, OpenSSH will accept any certificate signed by the trusted CA. This is convenient for testing but is not recommended for production. --- ## Step 3 -- Test the connection {{< role developer >}} {{< session-note >}} From a developer machine: ```bash # Log in if you have not already vouch login # Connect to a configured server ssh alice@server.example.com ``` The connection should succeed without prompting for a password or key passphrase. To verify that certificate authentication was used, check the server's auth log: ```bash # On the server sudo grep "Accepted certificate" /var/log/auth.log ``` You should see an entry like: ``` Accepted publickey for alice from 192.168.1.100 port 54321 ssh2: ED25519-CERT SHA256:... ID "alice@example.com" serial 42 CA ED25519 SHA256:... ``` --- ## How it works 1. During `vouch login`, the Vouch server signs the developer's ephemeral public key with an **Ed25519 CA** key. 2. The resulting SSH certificate is valid for **8 hours** and contains the developer's **email address** and **username** as principals. It is cached by the local Vouch SSH agent for its lifetime, so subsequent `ssh` connections reuse it without contacting the Vouch server. 3. When the developer connects to a server, the SSH client presents the certificate. 4. The server verifies the certificate was signed by the trusted CA and that one of the certificate's principals matches an allowed user. 5. The connection is established without any `authorized_keys` lookup. --- ## Troubleshooting ### "Permission denied (publickey)" - Confirm you have an active Vouch session: run `vouch status`. - Verify the Vouch agent is running and `~/.ssh/config` includes the `IdentityAgent` line. - Check that the server has `TrustedUserCAKeys` pointing to the correct CA public key. - If using `AuthorizedPrincipalsFile`, verify that the file for the target user contains one of the certificate's principals (email or username). ### "Certificate has expired" - SSH certificates issued by Vouch are valid for 8 hours. Run `vouch login` to get a fresh certificate. ### Agent not found - Run `vouch login` to start the agent, or restart it with `vouch setup ssh`. - Verify the socket exists at the path your SSH config points to: `grep IdentityAgent ~/.ssh/config`, then `ls -la` that path (`$XDG_RUNTIME_DIR/vouch/ssh-agent.sock`, or `~/.cache/vouch/ssh-agent.sock` on macOS). ### Server rejects the certificate even though it was signed by the right CA - Check `AuthorizedPrincipalsFile` permissions. The file and its parent directory must be owned by root and not writable by group or others. - Ensure the principals file is in the correct location (`/etc/ssh/auth_principals/<username>`). - Review `/var/log/auth.log` (or `/var/log/secure` on RHEL-based systems) for detailed error messages. ### Connection falls back to password authentication - The server may not have `TrustedUserCAKeys` configured, or `sshd` may not have been restarted after the configuration change. - Run `ssh -v user@server` to see which authentication methods are attempted. Look for `Offering public key: ... ED25519-CERT` in the debug output. --- ## IDE Remote Development Because Vouch configures `~/.ssh/config` with its agent, tools that build on top of SSH work automatically: - **VS Code Remote-SSH** -- Open remote folders and terminals on any server trusted by the Vouch CA. No additional extension configuration is needed. - **JetBrains Gateway** -- Connect to remote development environments using the same SSH certificate. - **scp / rsync / sftp** -- File transfers use the Vouch SSH agent transparently. As long as the Vouch agent is running and you have an active session, any tool that uses the system SSH client will authenticate with your Vouch certificate. --- ## Related guides - [Getting Started](/docs/getting-started/) -- Install the CLI and enroll your YubiKey. - [AWS Integration](/docs/aws/) -- Federate into AWS with OIDC for temporary STS credentials. - [Amazon EKS](/docs/eks/) -- Authenticate to Kubernetes clusters running on EKS. - [Security Model](/docs/security/) -- How Vouch protects credentials at every layer. --- # Roll Out Vouch to Your Team Source: https://vouch.sh/docs/rollout/ Your job: get everyone on the team into AWS, Kubernetes, and the package and repo services around them — fast, without handing out static keys, and without you becoming the person everyone waits on. This page is the whole playbook. Each step links to a deep-dive guide, but you should rarely need one. {{< tldr >}} 1. **Once (admin):** register the OIDC provider, deploy one IAM role, note its ARN — [30 minutes](#day-0----the-foundation). 2. **Per service (admin):** add a few IAM actions to that role — [checklist below](#enable-services). 3. **Per developer:** they run `vouch enroll`, then `vouch setup aws --role <ARN>` — [send them this](#onboard-developers). 4. **Ongoing:** offboarding is your IdP + [one deny statement](#when-someone-leaves); nothing to hunt down. {{< /tldr >}} ## Day 0 -- The foundation {{< role admin >}} Everything below hangs off one OIDC provider and one IAM role. This is the only part with real decisions in it. 1. **Enroll yourself.** Install the CLI and enroll your YubiKey -- [Getting Started](/docs/getting-started/) (5 minutes). The first person to enroll from your Google Workspace domain becomes the organization owner. 2. **Register the Vouch OIDC provider** in AWS -- one CLI command or a few lines of Terraform/CloudFormation. Exactly one per organization, in your management account if you have an AWS Organization. [AWS guide, Step 1](/docs/aws/#step-1----register-the-vouch-oidc-provider). 3. **Pick your account layout.** This decides what roles you deploy: | Your AWS layout | What to deploy | Guide | |---|---|---| | Single account | One `VouchDeveloper` role | [AWS guide](/docs/aws/#step-2----deploy-a-role) | | Multiple accounts (Organizations) | A hub role in the management account, a spoke role per account (StackSets or a Terraform module) | [Multi-account](/docs/aws-multi-account/) | | IAM Identity Center already in place | Register Vouch as a trusted token issuer | [Identity Center](/docs/aws-multi-account/#aws-iam-identity-center) | 4. **Deploy the role(s)** with the standard trust policy scoped to `*@yourdomain.com`. Start the permissions policy at `ReadOnlyAccess` and broaden deliberately. **Write down the role ARN** -- it is the only thing developers need from you. 5. **Plan user lifecycle.** Under ~15 people, manual member management in the [admin dashboard](/docs/admin/) is fine. At 15+, connect [SCIM](/docs/scim/) so your IdP creates and deactivates Vouch users automatically. <div class="checkpoint"> <p><strong>Foundation is done when...</strong></p> <ul> <li><code>aws sts get-caller-identity --profile vouch</code> returns an assumed-role ARN with <em>your</em> email in the session name.</li> <li>You have the role ARN (and hub-role ARN, if multi-account) saved somewhere you can paste from.</li> </ul> </div> --- ## Enable services {{< role admin >}} Each additional service is **IAM permissions on the role you already deployed, plus at most one admin action**. Developers then enable it with a single command. Full guides are linked for when something goes sideways. | Service | Add to the role's permissions | One-time admin action | Each developer runs | |---|---|---|---| | [AWS CLI / SDKs](/docs/aws/) | Your chosen policy (start with `ReadOnlyAccess`) | -- | `vouch setup aws --role <ARN>` | | [EKS](/docs/eks/) | `eks:DescribeCluster` | Create an [Access Entry](/docs/eks/#creating-eks-access-entries) mapping the role to cluster permissions | `vouch setup eks --cluster <NAME>` | | [CodeCommit](/docs/codecommit/) | `codecommit:GitPull`, `codecommit:GitPush` | -- | `vouch setup codecommit --configure` | | [CodeArtifact](/docs/codeartifact/) | `codeartifact:GetAuthorizationToken`, `codeartifact:GetRepositoryEndpoint`, `codeartifact:ReadFromRepository`, `sts:GetServiceBearerToken` | -- | `vouch setup codeartifact --tool <npm\|pip\|cargo\|pnpm\|uv> --repository <REPO>` | | [Docker / ECR](/docs/docker/) | `ecr:GetAuthorizationToken` + pull/push actions | -- | `vouch setup docker` | | [SSM Session Manager](/docs/ssm/) | `ssm:StartSession` (scope by tag) | Instances need the SSM agent + instance profile | `aws ssm start-session --target <ID> --profile vouch` | | [RDS / Aurora](/docs/databases/) | `rds-db:connect` | Create IAM-auth database users (`GRANT rds_iam`) | `vouch exec --type rds -- psql ...` | | [Bedrock](/docs/bedrock/) | `bedrock:InvokeModel` (scope by model) | -- | works via the `vouch` AWS profile | | [GitHub](/docs/github/) | -- (not AWS) | Install the Vouch GitHub App on your org | `vouch setup github` | | [SSH](/docs/ssh/) | -- (not AWS) | Trust the Vouch CA in `sshd_config` | automatic after `vouch login` | Sequence tip: ship **AWS first** (everything else chains off it), then EKS and CodeCommit/CodeArtifact, then the rest as teams ask for them. --- ## Onboard developers {{< role developer >}} Developers never touch IAM. Paste the block below into Slack or your onboarding wiki, fill in the two placeholders, and each person is productive in about five minutes -- no action from you. ````markdown **Set up Vouch (one time, ~5 min, YubiKey required)** 1. Install the CLI and background agent: brew install vouch-sh/tap/vouch brew services start vouch (Linux/Windows: https://vouch.sh/docs/getting-started/) 2. Enroll your YubiKey -- opens the browser for SSO, then asks for a tap: vouch enroll --server https://us.vouch.sh 3. Connect AWS (paste the role ARN from your admin): vouch setup aws --role <ROLE_ARN> 4. Connect the cluster (if you use Kubernetes): vouch setup eks --cluster <CLUSTER_NAME> 5. Start each workday with one tap: vouch login Check it worked: `aws sts get-caller-identity --profile vouch` shows your email in the role ARN. Questions -> #devops-help ```` Enrollment needs no invite codes or approval -- anyone authenticating through your Google Workspace domain lands in your organization automatically. --- ## Pilot, then roll out Vouch installs alongside existing credentials -- the `vouch` AWS profile, SSH certificates, and stacked Git credential helpers don't disturb anything your team uses today. So de-risk the rollout the boring way: 1. **Pilot on yourself plus one volunteer** for a week, with static credentials still in place. 2. **Migrate one integration at a time**, AWS first. The [migration guide](/docs/migration/) has the recommended order, per-integration checklists, and a rollback plan for each integration. 3. **Onboard the team** with the block above once the pilot is clean. 4. **Revoke static credentials last** -- deactivate old AWS access keys, remove stale `authorized_keys` entries and PATs only after everyone has run on Vouch for a week. Commands in [migration, Phase 3](/docs/migration/#phase-3----revoke-old-credentials). CI/CD pipelines are a separate track: they have no YubiKeys and should use their platform's own OIDC federation or instance roles, not Vouch. See [CI/CD considerations](/docs/migration/#cicd-considerations) -- Vouch's [CI/CD integration](/docs/cicd/) adds *human approval gates* on top, it does not replace machine credentials. --- ## When someone leaves This is the payoff for never distributing static keys: offboarding is your identity provider plus, at most, one IAM statement. 1. **Deactivate their account in your IdP** (you were doing this anyway). - **With [SCIM](/docs/scim/):** their Vouch sessions are revoked automatically the moment the IdP deactivates them. Nothing else to do on the Vouch side. - **Without SCIM:** use **Deactivate** and **Revoke credentials** in the [admin dashboard](/docs/admin/). 2. **Cut off cached AWS credentials** (optional, for immediate effect): STS credentials already on their laptop live up to 1 hour. To kill them instantly, add the `vouch:Email` deny statement to your roles or as an SCP -- copy it from [Revoke access for a specific user](/docs/aws/#revoke-access-for-a-specific-user). What expires on its own: | Credential | Gone after | |---|---| | Vouch session (new credentials) | immediately on deactivation | | Cached AWS STS credentials | up to 1 hour | | SSH certificate, GitHub tokens | up to 8 hours (end of session) | | ECR / CodeArtifact authorization tokens | up to 12 hours | There are no access keys to hunt down, no `authorized_keys` to scrub, no PATs to revoke. Full failure-mode detail (including **break-glass access** if the Vouch server is ever unreachable) is in [Availability](/docs/availability/). --- ## Related guides - [AWS Integration](/docs/aws/) -- the OIDC provider and role this whole page builds on. - [Multi-Account AWS](/docs/aws-multi-account/) -- hub/spoke roles, StackSets, SCP guardrails. - [SCIM Provisioning](/docs/scim/) -- automatic user lifecycle from your IdP. - [Migration Guide](/docs/migration/) -- integration-by-integration checklists and rollback. - [Availability and Failure Modes](/docs/availability/) -- offline behavior and break-glass planning. --- # Admin Dashboard Source: https://vouch.sh/docs/admin/ The Vouch admin dashboard provides a browser-based interface for organization administrators to manage members, review audit events, and configure integrations. Access it at `https://<your-vouch-server>/admin` after logging in with an administrator account. --- ## Member management The **Members** page lists all users in your organization with their current status, role, and registered security keys. ![Organization Members page showing a table of members with email, role, status, key count, and actions columns](/images/admin/admin-members.png) ### Actions | Action | Description | |---|---| | **Promote to admin** | Grant administrator privileges to a member. | | **Demote from admin** | Remove administrator privileges. | | **Deactivate** | Suspend a member's account. They cannot log in or obtain credentials until reactivated. | | **Activate** | Reactivate a previously deactivated member. | | **Revoke credentials** | Immediately invalidate all active credentials (SSH certificates, AWS sessions, tokens) for a member. | | **Remove** | Permanently remove a member from the organization. | Administrators cannot demote or remove themselves. This prevents accidental lockout. --- ## Audit log The **Audit** page (`/admin/audit`) displays a chronological record of security-relevant events across the organization. Each entry includes: - **Timestamp** -- When the event occurred. - **Actor** -- The user who performed the action. - **Event type** -- What happened (login, credential issuance, member change, etc.). - **Location** -- Approximate geographic location based on the client IP address (city, country). - **Details** -- Additional context such as credential type, target resource, or policy name. ![Audit Log page showing security events with timestamps, event types, domain, and details](/images/admin/admin-audit-log.png) ### Filtering Use the event type filter buttons to narrow the audit log to specific categories: Logins, Promotions, Demotions, Deactivations, Removals, or Revocations. ### Geographic data Vouch enriches audit events with GeoIP location data. Login events show the approximate location of the client, helping security teams identify anomalous access patterns such as logins from unexpected countries. --- ## SCIM token management The **SCIM Tokens** page (`/admin/scim-tokens`) lets administrators create and revoke SCIM provisioning tokens from the browser. These tokens are used by your identity provider to authenticate SCIM 2.0 API requests. | Action | Description | |---|---| | **Create token** | Generate a new SCIM bearer token. The token is displayed once at creation -- copy it immediately. | | **Revoke token** | Invalidate an existing token. Your identity provider will no longer be able to push user changes using that token. | For full SCIM setup instructions including identity provider configuration, see [SCIM Provisioning](/docs/scim/). --- ## Device posture policies The **Policies** page lets administrators enforce device security requirements. Built-in policies cover disk encryption, firewall, screen lock, endpoint protection, platform integrity, and OS recency. Custom policies can be written using CEL (Common Expression Language) expressions. ![Device Posture Policies page showing built-in policies with toggle controls and a custom policies section](/images/admin/admin-policies.png) For full details on available signals, CEL expressions, and enforcement behavior, see [Device Posture Policies](/docs/device-posture/). --- ## Access control Only organization administrators can access the admin dashboard. If you are not an administrator, the dashboard returns an error. To become an administrator, ask an existing admin to promote your account from the Members page. --- # Architecture Overview Source: https://vouch.sh/docs/architecture/ This page describes the components that make up Vouch, the protocols they use, and how they interact to turn a YubiKey tap into short-lived credentials for SSH, AWS, GitHub, Docker, and more. --- ## Components ### Vouch CLI (`vouch`) The command-line interface that developers interact with directly. It handles: - **Enrollment** -- Registers a FIDO2 key with the Vouch server. - **Login** -- Performs a FIDO2 assertion and establishes a session. - **Credential helpers** -- Provides credentials to tools like `aws`, `git`, `ssh`, `docker`, and `cargo` on demand. - **Setup commands** -- Configures local tool integrations (`vouch setup aws`, `vouch setup codecommit`, etc.). The CLI communicates with the Vouch agent over a local Unix domain socket and with the Vouch server over HTTPS. All requests to the server are authenticated using [HTTP Message Signatures (RFC 9421)](https://datatracker.ietf.org/doc/html/rfc9421) for cryptographic proof of request authenticity. ### Vouch Agent A background process that holds session state in memory. The agent: - **Caches the active session** so that credential requests do not require repeated FIDO2 assertions. - **Serves as an SSH agent** (implementing the SSH agent protocol) so that `ssh` can request certificates without additional configuration. - **Listens on a Unix domain socket** with filesystem permissions restricting access to the owning user. - **Holds no persistent state** -- if the agent process stops, the session is lost and a new `vouch login` is required. On macOS, the agent runs as a Homebrew service (`brew services start vouch`). On Linux, it runs as a systemd user service. ### Vouch Server The server is the identity broker. It: - **Authenticates users** via FIDO2/WebAuthn assertions, with identity federation through your organization's OIDC or [SAML 2.0](/docs/saml/) identity provider. - **Issues OIDC ID tokens** (signed with ES256 via AWS KMS) that AWS and other services consume via standard OIDC federation. - **Signs SSH certificates** using an Ed25519 certificate authority key managed by AWS KMS. - **Exchanges tokens** with GitHub Apps, AWS STS, and other external services on behalf of authenticated users. - **Manages the user directory** via SCIM 2.0 integration with identity providers. - **Publishes OIDC metadata** at `/.well-known/openid-configuration`, JWKS at `/oauth/jwks`, and Protected Resource Metadata at `/.well-known/oauth-protected-resource` ([RFC 9728](https://datatracker.ietf.org/doc/html/rfc9728)). When mTLS is configured, the discovery document includes `mtls_endpoint_aliases` for mTLS-capable clients. - **Provides OIDC discovery** for automatic identity provider detection during enrollment. The server does not store AWS credentials, SSH private keys, or GitHub tokens. It brokers short-lived credentials from external services. --- ## Protocol details ### FIDO2 / WebAuthn Used for all user authentication. The FIDO2 exchange happens between the YubiKey (authenticator), the Vouch CLI (client), and the Vouch server (relying party). - **Registration** (enrollment): The YubiKey generates a key pair. The public key is sent to the server along with an attestation certificate. The private key never leaves the hardware. When attestation verification is enabled, the server validates the certificate chain against pinned Yubico root CA certificates to confirm the key is a genuine hardware device. - **Authentication** (login): The server sends a challenge. The YubiKey signs it with the private key after PIN + touch verification. The server validates the signature against the stored public key. ### OIDC (OpenID Connect) The Vouch server acts as an OIDC identity provider. After FIDO2 authentication, it issues a signed JWT (ID token) containing: | Claim | Description | |---|---| | `iss` | Vouch server URL (e.g., `https://us.vouch.sh`) | | `sub` | Subject. For cloud federation ID tokens (AWS, Kubernetes), this is the user's email — what the consuming service matches in trust policies. For OAuth 2.0 access tokens issued under [RFC 9068](https://datatracker.ietf.org/doc/html/rfc9068), this is a stable opaque user identifier; the email is carried in a separate `email` claim when the `email` scope is granted. | | `aud` | Audience. Cloud federation: the Vouch issuer URL (AWS) or a configurable value (Kubernetes — default `kubernetes`, matches the API server's `--oidc-client-id`). Standard OIDC auth-code flow: the registered `client_id`. Tokens can be re-scoped to a different audience via [RFC 8707](https://datatracker.ietf.org/doc/html/rfc8707) resource indicators or [RFC 8693](https://datatracker.ietf.org/doc/html/rfc8693) token exchange. | | `exp` | Token expiration (default 8 hours, configurable via `VOUCH_SESSION_HOURS`) | | `iat` | Token issued-at timestamp | | `hd` | Google Workspace hosted domain | | `amr` | Authentication methods (e.g., `["hwk", "pin"]`) | | `acr` | Authentication context class (NIST AAL3) | | `cnf` | Confirmation claim for sender-constrained tokens — contains `jkt` (DPoP key thumbprint) or `x5t#S256` (mTLS certificate thumbprint) | External services (AWS, Kubernetes, custom OIDC applications) validate these tokens using the Vouch server's JWKS endpoint. ### ES256 (ECDSA over P-256) Used to sign OIDC ID tokens and access tokens. The signing key is managed by AWS KMS — the private key never exists outside the KMS boundary. External services fetch the public key from `/oauth/jwks` to verify token signatures. The JWKS endpoint supports key rotation — consuming services should re-fetch the JWKS when they encounter a token signed with an unknown `kid`. ### Ed25519 Used for SSH certificate signing. The Ed25519 CA key is managed by AWS KMS. The Vouch server delegates each signing operation to KMS and returns the signed certificate. The CA private key never leaves KMS. ### SSH Agent Protocol The Vouch agent implements the [SSH agent protocol](https://datatracker.ietf.org/doc/html/draft-miller-ssh-agent), making certificates available to `ssh` via the `SSH_AUTH_SOCK` environment variable. This is the same protocol used by `ssh-agent` and compatible with all standard SSH clients. ### AWS STS (AssumeRoleWithWebIdentity) The Vouch CLI calls [AssumeRoleWithWebIdentity](https://docs.aws.amazon.com/STS/latest/APIReference/API_AssumeRoleWithWebIdentity.html) with the OIDC ID token. AWS validates the token against the Vouch JWKS endpoint and returns temporary credentials (access key ID, secret access key, session token). ### FAPI 2.0 The Vouch CLI operates as a [FAPI 2.0](https://openid.net/specs/fapi-security-profile-2_0-final.html) client. On first use, it generates an ES256 key pair, stores it in the OS keychain, and auto-registers with the server ([RFC 7591](https://datatracker.ietf.org/doc/html/rfc7591)). Token requests use DPoP ([RFC 9449](https://datatracker.ietf.org/doc/html/rfc9449)) for sender-constrained tokens, PAR ([RFC 9126](https://datatracker.ietf.org/doc/html/rfc9126)) for protected authorization requests, RAR ([RFC 9396](https://datatracker.ietf.org/doc/html/rfc9396)) for structured authorization details, and `private_key_jwt` ([RFC 7523](https://datatracker.ietf.org/doc/html/rfc7523)) for client authentication — no shared secrets between CLI and server. FAPI 2.0 also accepts Mutual TLS ([RFC 8705](https://datatracker.ietf.org/doc/html/rfc8705)) as an alternative sender-constraining mechanism — see [Mutual TLS](#mutual-tls-rfc-8705) below. ### HTTP Message Signatures (RFC 9421) All authenticated requests from the CLI to the Vouch server include [HTTP Message Signatures](https://datatracker.ietf.org/doc/html/rfc9421). The CLI signs each request using the FAPI key pair stored in the OS keychain. The server verifies the signature before processing the request, providing cryptographic proof that the request was not tampered with in transit and originated from the registered client. Supported algorithms: ECDSA P-256/P-384, EdDSA, and RSA-PSS-SHA512. ### Mutual TLS (RFC 8705) The Vouch server supports [OAuth 2.0 Mutual-TLS Client Authentication and Certificate-Bound Access Tokens](https://datatracker.ietf.org/doc/html/rfc8705) as an alternative to DPoP for sender-constrained tokens. When configured, a separate mTLS listener runs on port 8443 and verifies client certificates during the TLS handshake. Two client authentication methods are supported: `tls_client_auth` (PKI-validated certificates where the server verifies the certificate chain against a configured Client Certificate CA) and `self_signed_tls_client_auth` (self-signed certificates registered via the client's `jwks` or `jwks_uri` with `x5c` certificate representations). Certificate-bound access tokens include an `x5t#S256` thumbprint (SHA-256 hash of the client's DER-encoded X.509 certificate) in the `cnf` claim. Resource servers validate that the certificate presented at the TLS layer matches the thumbprint bound to the token. The Client Certificate CA can be managed locally or via AWS KMS, following the same pattern as the SSH CA. The OpenID Configuration discovery document advertises mTLS support via `mtls_endpoint_aliases`, which provides alternative endpoint URLs for token, revocation, and introspection endpoints on the mTLS port. ### Protected Resource Metadata (RFC 9728) The Vouch server publishes [OAuth 2.0 Protected Resource Metadata](https://datatracker.ietf.org/doc/html/rfc9728) at `/.well-known/oauth-protected-resource`. This document describes the resource's authorization policy: which authorization server to use, the JWKS URI, supported scopes, bearer token presentation methods, DPoP and mTLS binding requirements, and descriptive URLs. Every response includes a `signed_metadata` field — an ES256 JWS with `typ=oauth-protected-resource+jwt`, verifiable via the advertised `jwks_uri`. When a protected endpoint returns a 401, the `WWW-Authenticate` header includes a `resource_metadata` parameter pointing clients to the metadata document for automatic authorization server discovery. Descriptive metadata fields are configurable via environment variables: `VOUCH_RESOURCE_NAME`, `VOUCH_RESOURCE_DOCUMENTATION`, `VOUCH_RESOURCE_POLICY_URI`, and `VOUCH_RESOURCE_TOS_URI`. ### Step-Up Authentication (RFC 9470) The Vouch server supports the [OAuth 2.0 Step-Up Authentication Challenge Protocol](https://datatracker.ietf.org/doc/html/rfc9470). When a protected resource requires a higher authentication assurance level than the current token provides, it returns a `WWW-Authenticate` challenge with `error="insufficient_user_authentication"` and `acr_values` or `max_age` parameters specifying the required authentication strength or recency. Clients use these parameters in a new authorization request to obtain a token meeting the elevated requirements. Vouch's FIDO2 hardware authentication satisfies NIST AAL3 (`acr` claim), which meets most step-up requirements. ### SAML 2.0 For organizations using SAML-based identity providers, the Vouch server acts as a SAML Service Provider. It publishes SP metadata at `/saml/metadata` and accepts assertions at the Assertion Consumer Service endpoint (`/saml/acs`). Both HTTP-POST and HTTP-Redirect bindings are supported. See [SAML Identity Providers](/docs/saml/) for configuration details. ### SCIM 2.0 The Vouch server implements [SCIM 2.0](https://datatracker.ietf.org/doc/html/rfc7644) endpoints for automated user provisioning. Identity providers (Google Workspace, Okta, Azure AD) push user lifecycle events to synchronize the Vouch user directory. ### Standards compliance The Vouch server implements the following standards, each with dedicated test coverage: | Standard | Title | Usage in Vouch | |---|---|---| | [OIDC Core](https://openid.net/specs/openid-connect-core-1_0.html) | OpenID Connect Core 1.0 | Identity provider, ID tokens, UserInfo | | [FAPI 2.0 SP](https://openid.net/specs/fapi-security-profile-2_0-final.html) | FAPI 2.0 Security Profile | Security controls for CLI and application clients | | [FAPI 2.0 MS](https://openid.net/specs/fapi-message-signing-2_0-final.html) | FAPI 2.0 Message Signing | HTTP Message Signatures on requests and responses | | [RFC 6749](https://datatracker.ietf.org/doc/html/rfc6749) | OAuth 2.0 Authorization Framework | Core authorization flows | | [RFC 7009](https://datatracker.ietf.org/doc/html/rfc7009) | Token Revocation | `/oauth/revoke` endpoint | | [RFC 7523](https://datatracker.ietf.org/doc/html/rfc7523) | JWT Bearer Client Authentication | `private_key_jwt` client authentication | | [RFC 7591](https://datatracker.ietf.org/doc/html/rfc7591) | Dynamic Client Registration | CLI auto-registration | | [RFC 7592](https://datatracker.ietf.org/doc/html/rfc7592) | Dynamic Client Registration Management | Client registration updates | | [RFC 7636](https://datatracker.ietf.org/doc/html/rfc7636) | PKCE | Code challenge for public clients | | [RFC 7644](https://datatracker.ietf.org/doc/html/rfc7644) | SCIM 2.0 | User provisioning | | [RFC 7662](https://datatracker.ietf.org/doc/html/rfc7662) | Token Introspection | `/oauth/introspect` endpoint | | [RFC 7800](https://datatracker.ietf.org/doc/html/rfc7800) | Proof-of-Possession Key Semantics | `cnf` claim in tokens | | [RFC 8176](https://datatracker.ietf.org/doc/html/rfc8176) | Authentication Method Reference Values | `amr` claim values | | [RFC 8414](https://datatracker.ietf.org/doc/html/rfc8414) | Authorization Server Metadata | `/.well-known/openid-configuration` | | [RFC 8628](https://datatracker.ietf.org/doc/html/rfc8628) | Device Authorization Grant | CLI and native app authentication | | [RFC 8693](https://datatracker.ietf.org/doc/html/rfc8693) | Token Exchange | Service-to-service delegation | | [RFC 8705](https://datatracker.ietf.org/doc/html/rfc8705) | Mutual-TLS | mTLS client auth, certificate-bound tokens | | [RFC 8707](https://datatracker.ietf.org/doc/html/rfc8707) | Resource Indicators | Audience-restricted tokens | | [RFC 8725](https://datatracker.ietf.org/doc/html/rfc8725) | JWT Best Current Practices | JWT security hardening | | [RFC 9068](https://datatracker.ietf.org/doc/html/rfc9068) | JWT Profile for Access Tokens | Access token format | | [RFC 9101](https://datatracker.ietf.org/doc/html/rfc9101) | JWT-Secured Authorization Request | Signed authorization requests | | [RFC 9126](https://datatracker.ietf.org/doc/html/rfc9126) | Pushed Authorization Requests | Back-channel authorization | | [RFC 9207](https://datatracker.ietf.org/doc/html/rfc9207) | AS Issuer Identification | Mix-up attack prevention via `iss` parameter | | [RFC 9396](https://datatracker.ietf.org/doc/html/rfc9396) | Rich Authorization Requests | Structured authorization details | | [RFC 9421](https://datatracker.ietf.org/doc/html/rfc9421) | HTTP Message Signatures | Request-level integrity | | [RFC 9449](https://datatracker.ietf.org/doc/html/rfc9449) | DPoP | Sender-constrained tokens | | [RFC 9470](https://datatracker.ietf.org/doc/html/rfc9470) | Step-Up Authentication | Authentication challenge protocol | | [RFC 9728](https://datatracker.ietf.org/doc/html/rfc9728) | Protected Resource Metadata | `/.well-known/oauth-protected-resource` | | [SAML 2.0](http://docs.oasis-open.org/security/saml/v2.0/) | SAML 2.0 | Identity provider federation | --- ## Authentication flow The complete flow from YubiKey tap to credential consumption: ``` ┌──────────┐ ┌──────────┐ ┌──────────────┐ ┌─────────────────┐ │ YubiKey │ │ Vouch CLI│ │ Vouch Server │ │ External Service│ │ (FIDO2) │ │ + Agent │ │ (IdP/CA) │ │(AWS/GitHub/etc.)│ └─────┬────┘ └─────┬────┘ └──────┬───────┘ └───────┬─────────┘ │ │ │ │ │ 1. Challenge │ │ │ │◄──────────────┤ Get challenge │ │ │ ├──────-────────►│ │ │ │ │ │ │ 2. Sign │ │ │ │ (PIN+touch) │ │ │ ├──────────────►│ │ │ │ │ 3. Assertion │ │ │ ├─────-─────────►│ │ │ │ │ 4. Validate │ │ │ │ │ │ │ 5. Session │ │ │ │◄───────-───────┤ │ │ │ │ │ │ │ 6. Credential │ │ │ │ request │ │ │ ├─────────────-─►│ │ │ │ │ 7. Exchange │ │ │ ├──────────────────-►│ │ │ │ 8. Short-lived │ │ │ │◄──────────────────-┤ │ │ 9. Credential │ │ │ │◄────────────-──┤ │ │ │ │ │ │ │ 10. Tool uses │ │ │ │ credential │ │ │ ├─────────────────────────────--─────►│ ``` Steps 1--5 happen once during `vouch login`. Steps 6--10 happen on demand each time a tool needs a credential. --- ## Agent architecture The Vouch agent is a long-running process that provides two services: ### Unix domain socket The CLI communicates with the agent over a Unix domain socket at a well-known path. The socket is protected by multiple layers: - **Filesystem permissions** — The socket file has restrictive permissions (owner-only) to prevent other users on the system from accessing session material. - **Peer credential verification** — Every incoming connection is checked using OS-level peer credentials (`SO_PEERCRED` on Linux, `getpeereid` on macOS) to verify the connecting process has the same UID as the agent. Connections from a different UID are rejected and audit-logged, following the same approach used by `gpg-agent`. - **Directory safety** — On startup, the agent validates that its socket directory (`$XDG_RUNTIME_DIR/vouch/`, or `~/.cache/vouch/` where `XDG_RUNTIME_DIR` is unset) is not a symlink and is owned by the current user, preventing symlink-based directory hijacking where an attacker pre-creates the directory pointing to an attacker-controlled location. ### In-memory credential cache The agent caches: - **Session token** -- Used to authenticate requests to the Vouch server. - **SSH certificate** -- Served to SSH clients via the agent protocol. - **Cached STS credentials** -- AWS credentials are cached until their 1-hour expiry to avoid redundant STS calls. All cached material is held in process memory. Nothing is written to disk. When the agent process stops (logout, reboot, crash), all cached credentials are lost and a new `vouch login` is required. --- ## Network requirements The Vouch CLI and agent need to reach the following endpoints: | Destination | Port | Protocol | Purpose | |---|---|---|---| | Vouch server (e.g., `us.vouch.sh`) | 443 | HTTPS | Authentication, credential exchange, OIDC | | Vouch server mTLS endpoint | 8443 | mTLS (HTTPS) | Certificate-bound token requests, mTLS client authentication (when configured) | | AWS STS (`sts.amazonaws.com`) | 443 | HTTPS | `AssumeRoleWithWebIdentity` | | Target SSH hosts | 22 | SSH | SSH connections (if SSH integration is used) | The Vouch server additionally requires outbound access to GitHub (`api.github.com`, port 443, HTTPS) for installation token exchange when the GitHub integration is enabled. The Vouch server must be reachable from the internet so that AWS can fetch the JWKS endpoint for token validation. If your organization uses a firewall or proxy, ensure these destinations are allowed. --- ## Data residency Vouch server instances are deployed in specific geographic regions: | Instance | Region | Status | |---|---|---| | `us.vouch.sh` | United States | Active | | EU instance | Europe | Coming soon | | APAC instance | Asia-Pacific | Coming soon | All user data (enrolled keys, user metadata, audit logs) resides in the region of the Vouch server instance you enroll with. Credentials brokered through AWS STS, GitHub, and other external services are subject to those services' own data residency policies. --- # Multi-Account AWS Strategy Source: https://vouch.sh/docs/aws-multi-account/ Multi-account AWS layouts have two models, both anchored in the **management account** where the Vouch OIDC provider lives. **Most teams start with role chaining**; choose Identity Center if you already run it. - **Role chaining (STS)** -- developers federate into a single management-account "hub" role, and the hub assumes "spoke" roles in member accounts using `sts:AssumeRole`. Covered in Steps 1--3 below. - **[IAM Identity Center](#aws-iam-identity-center)** -- Vouch is registered as a trusted-token-issuer application in Identity Center, and developers get credentials for the accounts and permission sets they are assigned. Covered in the Identity Center section below. We don't recommend deploying separate OIDC providers in every account -- it multiplies maintenance, and AWS Organizations exists precisely so you don't have to. One management account plus per-account access covers the same use cases with less surface area. {{< tldr >}} - **Prerequisite:** the [OIDC provider is registered](/docs/aws/#step-1----register-the-vouch-oidc-provider) in your management account. - **Admin, once:** deploy the [hub role](#step-1----deploy-the-hub-role) there, then a [spoke role per member account](#step-2----deploy-spoke-roles-in-member-accounts) via StackSets or Terraform. - **Each developer:** `vouch setup aws --management-role <HUB_ARN> --role <SPOKE_ARN> --profile vouch-<env>` -- one profile per account. - On Identity Center? Skip the spokes and [register Vouch as a trusted token issuer](#aws-iam-identity-center) instead. {{< /tldr >}} --- ## Architecture ``` Vouch (OIDC) ──▶ Management account ──▶ Member account hub role spoke role (federate in) (chain into the account) ``` You don't need to follow the STS calls to deploy this: put the OIDC provider and one hub role in the management account, and a spoke role in each member account. - The Vouch OIDC provider lives in the **management account only**. - The **hub role** is defined in [Step 1](#step-1--deploy-the-hub-role) -- its identity policy is `sts:AssumeRole` only. - Each **spoke role** trusts the hub through a plain AWS-principal trust (no OIDC). - The developer's verified email propagates through the chain as [`SourceIdentity`](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_control-access_monitor.html): set to `alice@example.com` at `AssumeRoleWithWebIdentity`, carried forward through each `AssumeRole`, recorded in CloudTrail in every member account. - All session tags are [transitive](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_session-tags.html#id_session-tags_role-chaining), so conditions like `aws:PrincipalTag/vouch:Domain` and `aws:PrincipalTag/vouch:Email` work in spoke trust policies just as they do in the hub. Every developer uses the same `vouch login` session. The AWS profile they select determines which spoke role -- and therefore which account -- they assume. --- ## Step 1 -- Deploy the hub role {{< role admin >}} Deploy the hub role in your management account. Developers federate into it with `vouch login`, and it can do nothing in this account except assume spoke roles in member accounts. Its **trust policy** is the shared Vouch OIDC trust -- the `sub` condition uses your email domain: ```json { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "Federated": "arn:aws:iam::ACCOUNT_ID:oidc-provider/us.vouch.sh" }, "Action": [ "sts:AssumeRoleWithWebIdentity", "sts:SetSourceIdentity", "sts:TagSession" ], "Condition": { "StringEquals": { "us.vouch.sh:aud": "https://us.vouch.sh" }, "StringLike": { "us.vouch.sh:sub": "*@example.com", "sts:RoleSessionName": "${us.vouch.sh:sub}" }, "Bool": { "sts:RoleAuthorizedByIdp": "true" } } } ] } ``` > **Note:** The `sts:RoleAuthorizedByIdp` condition requires the token to be [pinned to the hub role](/docs/aws/#require-role-pinning), which the Vouch CLI does automatically. Do **not** add that condition to spoke roles -- their second hop is a plain SigV4 `sts:AssumeRole` with no OIDC token, so the condition would never match. Its **identity policy** grants `sts:AssumeRole` only, scoped to the spoke role ARNs you'll deploy in Step 2. The `aws:ResourceOrgID` condition restricts the hub to assuming roles only inside your AWS Organization -- it matches the org of the role being assumed against your own: ```json { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "sts:AssumeRole", "sts:SetSourceIdentity", "sts:TagSession" ], "Resource": "arn:aws:iam::*:role/vouch/VouchAccess", "Condition": { "StringEquals": { "aws:ResourceOrgID": "${aws:PrincipalOrgID}" } } } ] } ``` `${aws:PrincipalOrgID}` resolves to your organization automatically, so there's nothing to hand-edit -- and no account IDs to maintain as the org grows. Record the hub role ARN (e.g. `arn:aws:iam::999999999999:role/vouch/VouchAccess`) -- every spoke role's trust policy references it. --- ## Step 2 -- Deploy spoke roles in member accounts {{< role admin >}} Each member account needs a `VouchAccess` role that trusts the hub role and grants the actual permissions developers need in that account. The spoke role's trust policy is a plain AWS-principal trust (no OIDC provider, no JWT condition) because the chained `AssumeRole` call comes from a regular IAM role, not from a federated identity. The `aws:SourceIdentity` condition ensures only requests originating from an authenticated Vouch user with a matching email domain can assume the role. Pick one of the deployment options below. Both produce the same result. #### CloudFormation StackSets StackSets deploy a single template across every account in your AWS Organization (or a chosen OU). ```yaml AWSTemplateFormatVersion: "2010-09-09" Description: "Vouch spoke role (deployed via StackSet)" Parameters: ManagementAccountId: Type: String Description: "Account ID of the AWS Organization management account" EmailDomain: Type: String Description: Your Google Workspace domain (e.g. example.com) ManagedPolicyArn: Type: String Default: "arn:aws:iam::aws:policy/ReadOnlyAccess" Description: "Permissions policy to attach to the spoke role" Resources: VouchSpokeRole: Type: AWS::IAM::Role Properties: RoleName: VouchAccess Path: /vouch/ AssumeRolePolicyDocument: Version: "2012-10-17" Statement: - Effect: Allow Principal: AWS: !Sub "arn:${AWS::Partition}:iam::${ManagementAccountId}:role/vouch/VouchAccess" Action: - "sts:AssumeRole" - "sts:SetSourceIdentity" - "sts:TagSession" Condition: StringLike: "aws:SourceIdentity": !Sub "*@${EmailDomain}" ManagedPolicyArns: - !Ref ManagedPolicyArn Outputs: RoleArn: Value: !GetAtt VouchSpokeRole.Arn ``` Deploy from the management account: ```bash aws cloudformation create-stack-set \ --stack-set-name vouch-spokes \ --template-body file://vouch-spoke.yaml \ --capabilities CAPABILITY_NAMED_IAM \ --permission-model SERVICE_MANAGED \ --auto-deployment Enabled=true,RetainStacksOnAccountRemoval=false aws cloudformation create-stack-instances \ --stack-set-name vouch-spokes \ --deployment-targets OrganizationalUnitIds=ou-xxxx-xxxxxxxx \ --regions us-east-1 \ --parameter-overrides \ ParameterKey=ManagementAccountId,ParameterValue=999999999999 \ ParameterKey=EmailDomain,ParameterValue=example.com ``` With `--auto-deployment Enabled`, new accounts added to the OU automatically receive the spoke role. #### Per-account permissions Override `ManagedPolicyArn` per account to scope permissions: ```bash # Development: PowerUserAccess aws cloudformation create-stack-instances \ --stack-set-name vouch-spokes \ --accounts 111111111111 \ --regions us-east-1 \ --parameter-overrides \ ParameterKey=ManagementAccountId,ParameterValue=999999999999 \ ParameterKey=EmailDomain,ParameterValue=example.com \ ParameterKey=ManagedPolicyArn,ParameterValue=arn:aws:iam::aws:policy/PowerUserAccess # Production: ReadOnlyAccess aws cloudformation create-stack-instances \ --stack-set-name vouch-spokes \ --accounts 222222222222 \ --regions us-east-1 \ --parameter-overrides \ ParameterKey=ManagementAccountId,ParameterValue=999999999999 \ ParameterKey=EmailDomain,ParameterValue=example.com \ ParameterKey=ManagedPolicyArn,ParameterValue=arn:aws:iam::aws:policy/ReadOnlyAccess ``` #### Terraform Create a reusable module for the spoke role: ```hcl # modules/vouch-spoke/main.tf variable "management_account_id" { type = string description = "AWS Organization management account ID" } variable "email_domain" { type = string description = "Email domain to allow via SourceIdentity" } variable "policy_arns" { type = list(string) default = ["arn:aws:iam::aws:policy/ReadOnlyAccess"] } data "aws_partition" "current" {} resource "aws_iam_role" "vouch_spoke" { name = "VouchAccess" path = "/vouch/" assume_role_policy = jsonencode({ Version = "2012-10-17" Statement = [ { Effect = "Allow" Principal = { AWS = "arn:${data.aws_partition.current.partition}:iam::${var.management_account_id}:role/vouch/VouchAccess" } Action = [ "sts:AssumeRole", "sts:SetSourceIdentity", "sts:TagSession", ] Condition = { StringLike = { "aws:SourceIdentity" = "*@${var.email_domain}" } } } ] }) } resource "aws_iam_role_policy_attachment" "vouch_spoke" { count = length(var.policy_arns) role = aws_iam_role.vouch_spoke.name policy_arn = var.policy_arns[count.index] } output "role_arn" { value = aws_iam_role.vouch_spoke.arn } ``` Per-account usage: ```hcl # environments/dev/main.tf module "vouch" { source = "../../modules/vouch-spoke" management_account_id = "999999999999" email_domain = "example.com" policy_arns = ["arn:aws:iam::aws:policy/PowerUserAccess"] } # environments/prod/main.tf module "vouch" { source = "../../modules/vouch-spoke" management_account_id = "999999999999" email_domain = "example.com" policy_arns = ["arn:aws:iam::aws:policy/ReadOnlyAccess"] } ``` <div class="checkpoint"> <p><strong>You are done with role deployment when...</strong></p> <ul> <li>The hub role exists in the management account with an <code>sts:AssumeRole</code>-only identity policy.</li> <li>Each member account has a <code>/vouch/VouchAccess</code> role whose trust policy lists the hub role as principal.</li> <li>From an authenticated Vouch session you can assume the hub and chain into a spoke; CloudTrail in the spoke account records the developer's email as <code>SourceIdentity</code>.</li> </ul> </div> --- ## Step 3 -- Configure developer profiles {{< role developer >}} Each developer configures a named AWS profile per account. Point `--role` at the spoke role (`/vouch/VouchAccess`) in the target account and `--management-role` at the hub role; the Vouch CLI stores the hub as an organization anchor and handles the chain through it: ```bash # Development account vouch setup aws \ --management-role arn:aws:iam::999999999999:role/vouch/VouchAccess \ --role arn:aws:iam::111111111111:role/vouch/VouchAccess \ --profile vouch-dev # Staging account vouch setup aws \ --management-role arn:aws:iam::999999999999:role/vouch/VouchAccess \ --role arn:aws:iam::333333333333:role/vouch/VouchAccess \ --profile vouch-staging # Production account vouch setup aws \ --management-role arn:aws:iam::999999999999:role/vouch/VouchAccess \ --role arn:aws:iam::222222222222:role/vouch/VouchAccess \ --profile vouch-prod ``` Running `vouch setup aws` with no flags launches an interactive wizard that captures the management role and target roles for you. This produces the following `~/.aws/config`. Each profile chains through the hub via `--via`: ```ini [profile vouch-dev] credential_process = vouch credential aws --role arn:aws:iam::111111111111:role/vouch/VouchAccess --via arn:aws:iam::999999999999:role/vouch/VouchAccess [profile vouch-staging] credential_process = vouch credential aws --role arn:aws:iam::333333333333:role/vouch/VouchAccess --via arn:aws:iam::999999999999:role/vouch/VouchAccess [profile vouch-prod] credential_process = vouch credential aws --role arn:aws:iam::222222222222:role/vouch/VouchAccess --via arn:aws:iam::999999999999:role/vouch/VouchAccess ``` > **Note:** Passing `--management-role` stores the hub as an organization anchor and writes `--via` into each profile, so chaining is explicit and unambiguous. Once the anchor is stored, later profiles can drop `--management-role` -- `vouch credential aws --role <spoke-arn>` resolves the management role from your stored organization automatically (pass `--via <management-role-arn>` to pick one when several organizations are configured). Use profiles per command: ```bash # Deploy to dev cdk deploy --profile vouch-dev # Check production aws s3 ls --profile vouch-prod ``` Or set a default: ```bash export AWS_PROFILE=vouch-dev ``` --- ## AWS IAM Identity Center Instead of role chaining, you can register Vouch as a **trusted token issuer** in AWS IAM Identity Center. Developers then get credentials for exactly the accounts and permission sets they are assigned in Identity Center -- no spoke roles to deploy. Vouch signs a short-lived RS256 token, exchanges it for an Identity Center access token via `CreateTokenWithIAM`, and calls the SSO portal (`ListAccounts`, `ListAccountRoles`, `GetRoleCredentials`) on the developer's behalf. This model requires an [organization instance](https://docs.aws.amazon.com/singlesignon/latest/userguide/organization-instances-identity-center.html) of IAM Identity Center and users provisioned so their email matches the Vouch identity (the token `sub`). If you provision Identity Center from the same identity provider Vouch uses, [SCIM](/docs/scim/) keeps them in sync. > **AI agents cannot use this path.** Permission-set credentials cannot be constrained with a `ReadOnlyAccess` session policy, so Vouch **refuses to issue them to a detected AI coding agent** rather than downscoping. If your workflows include AI agents that need AWS access, use the [role-chaining](#step-1--deploy-the-hub-role) model above, where Vouch enforces read-only automatically. ### IdC Step 1 -- Deploy the management role {{< role admin >}} Deploy a management role in the management account using the same [shared Vouch OIDC trust policy](/docs/aws/#shared-trust-policy) as the rest of this guide (`AssumeRoleWithWebIdentity`, with a `*@example.com` `sub` condition). Vouch assumes this role via web identity and uses it to sign the `CreateTokenWithIAM` call. The token for this hop is [pinned to the management role](/docs/aws/#require-role-pinning), so this trust policy can also require `"Bool": {"sts:RoleAuthorizedByIdp": "true"}` like any other web-identity role. The role needs **no identity policy** for this. Permission to call `CreateTokenWithIAM` is not granted through an identity policy on the role -- instead you attach a **resource policy to the customer managed application** (the *application credentials*) that names this role as the principal allowed to call the action. You apply it in [IdC Step 2](#idc-step-2--register-the-trusted-token-issuer-and-application); it looks like this: ```json { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "AWS": "arn:aws:iam::999999999999:role/vouch/VouchAccess" }, "Action": "sso-oauth:CreateTokenWithIAM", "Resource": "*" } ] } ``` Record the management role ARN -- IdC Step 2 references it as the principal in this resource policy. ### IdC Step 2 -- Register the trusted token issuer and application {{< role admin >}} Register Vouch as a **trusted token issuer** and add an OAuth 2.0 [customer managed application](https://docs.aws.amazon.com/singlesignon/latest/userguide/customermanagedapps.html) so it can exchange tokens and read your account assignments. The trusted token issuer, the application, and its account-access scope are managed in Terraform. The JWT-bearer grant (which binds the issuer and sets the `aud` claim) and the application credentials (the resource policy that lets the management role call `CreateTokenWithIAM`) have no Terraform resource yet, so apply those two with the AWS CLI. #### Terraform ```hcl data "aws_ssoadmin_instances" "this" {} locals { instance_arn = tolist(data.aws_ssoadmin_instances.this.arns)[0] } # Trust Vouch's RS256 tokens. Vouch carries the user's email in `sub`; # match it to the Identity Center user's email. resource "aws_ssoadmin_trusted_token_issuer" "vouch" { name = "Vouch" instance_arn = local.instance_arn trusted_token_issuer_type = "OIDC_JWT" trusted_token_issuer_configuration { oidc_jwt_configuration { issuer_url = "https://us.vouch.sh" claim_attribute_path = "sub" identity_store_attribute_path = "emails.value" jwks_retrieval_option = "OPEN_ID_DISCOVERY" } } } # Customer managed OAuth 2.0 application. resource "aws_ssoadmin_application" "vouch" { name = "Vouch" instance_arn = local.instance_arn application_provider_arn = "arn:aws:sso::aws:applicationProvider/custom" } # Let the application list accounts/roles and fetch credentials for the # authenticated user's own assignments. resource "aws_ssoadmin_application_access_scope" "vouch" { application_arn = aws_ssoadmin_application.vouch.arn scope = "sso:account:access" } output "vouch_application_arn" { value = aws_ssoadmin_application.vouch.arn } output "vouch_trusted_token_issuer_arn" { value = aws_ssoadmin_trusted_token_issuer.vouch.arn } ``` #### Grant and credentials (AWS CLI) The AWS Terraform provider does not yet expose the JWT-bearer grant or the application authentication method, so set them with `aws sso-admin` after `terraform apply`: ```bash APP_ARN=$(terraform output -raw vouch_application_arn) TTI_ARN=$(terraform output -raw vouch_trusted_token_issuer_arn) MGMT_ROLE_ARN=arn:aws:iam::999999999999:role/vouch/VouchAccess # Bind the trusted token issuer and require aud = the Vouch issuer. aws sso-admin put-application-grant \ --application-arn "$APP_ARN" \ --grant-type urn:ietf:params:oauth:grant-type:jwt-bearer \ --grant "{\"JwtBearer\":{\"AuthorizedTokenIssuers\":[{\"TrustedTokenIssuerArn\":\"$TTI_ARN\",\"AuthorizedAudiences\":[\"https://us.vouch.sh\"]}]}}" # Let the management role call CreateTokenWithIAM. aws sso-admin put-application-authentication-method \ --application-arn "$APP_ARN" \ --authentication-method-type IAM \ --authentication-method "{\"Iam\":{\"ActorPolicy\":{\"Version\":\"2012-10-17\",\"Statement\":[{\"Effect\":\"Allow\",\"Principal\":{\"AWS\":\"$MGMT_ROLE_ARN\"},\"Action\":\"sso-oauth:CreateTokenWithIAM\",\"Resource\":\"*\"}]}}}" ``` Both calls are idempotent -- re-running updates the grant or credentials in place. Record the application ARN (`terraform output -raw vouch_application_arn`); developers pass it to `vouch setup aws`. #### Console alternative Prefer the console? In the [IAM Identity Center console](https://console.aws.amazon.com/singlesignon): 1. Under **Settings**, add a trusted token issuer with **Issuer URL** `https://us.vouch.sh`, mapping the token's identity claim to the Identity Center user's email. 2. Under **Applications** -> **Customer managed** -> **Add application**, choose **I have an application I want to set up**, then **OAuth 2.0**. 3. On **Specify authentication settings**, select the trusted token issuer and set the **Aud claim** to `https://us.vouch.sh` (Vouch sets the audience equal to its issuer). 4. On **Specify application credentials**, name the management role from Step 1 as the principal allowed to call `sso-oauth:CreateTokenWithIAM`. 5. Open the application and turn on **Enable AWS account access** (the `sso:account:access` scope). This must be done from the management or a delegated administrator account. See [Enable AWS account access for customer managed applications](https://docs.aws.amazon.com/singlesignon/latest/userguide/enable-account-access-customer-managed-apps.html). > **Note:** The `sso:account:access` scope grants the application access to every account and permission set assigned to the authenticated user; you cannot scope it to a subset. Access is still bounded by each user's own Identity Center assignments. ### IdC Step 3 -- Discover accounts and permission sets {{< role developer >}} With the application registered, developers run a single command to enumerate every account and permission set they are assigned and write one profile per assignment: ```bash vouch setup aws \ --management-role arn:aws:iam::999999999999:role/vouch/VouchAccess \ --identity-center-application arn:aws:sso::999999999999:application/ssoins-1111/apl-2222 \ --region us-east-1 \ --discover ``` No `aws sso login` is required -- `vouch login` is the only authentication, because Vouch is the trusted token issuer. The `--discover` run writes profiles named `vouch-<account>-<permission-set>`: ```ini [profile vouch-production-administratoraccess] credential_process = vouch credential aws --idc-application arn:aws:sso::999999999999:application/ssoins-1111/apl-2222 --account 222222222222 --permission-set "AdministratorAccess" output = json ``` Use them like any other profile: ```bash aws s3 ls --profile vouch-production-administratoraccess ``` Re-run `vouch setup aws --discover` at any time to pick up newly assigned accounts and permission sets; existing profiles are left untouched. <div class="checkpoint"> <p><strong>You are done with Identity Center setup when...</strong></p> <ul> <li>The management role is named as the principal in the customer managed application's resource policy (<em>application credentials</em>), allowing it to call <code>sso-oauth:CreateTokenWithIAM</code>.</li> <li>The trusted token issuer's <strong>Issuer URL</strong> and the application's <strong>Aud claim</strong> are both <code>https://us.vouch.sh</code>.</li> <li><code>vouch setup aws --discover</code> writes a profile per assignment, and <code>aws sts get-caller-identity</code> against one returns the expected account.</li> </ul> </div> --- ## Restricting federation with SCPs Use [Service Control Policies](https://docs.aws.amazon.com/organizations/latest/userguide/orgs_manage_policies_scps.html) to lock down who can register or change an OIDC provider, so a developer can't add a rogue identity provider that federates into your accounts. This denies every OIDC-provider change except from your deployment principal: ```json { "Version": "2012-10-17", "Statement": [ { "Sid": "DenyUnauthorizedOIDCProviders", "Effect": "Deny", "Action": [ "iam:CreateOpenIDConnectProvider", "iam:DeleteOpenIDConnectProvider", "iam:UpdateOpenIDConnectProviderThumbprint", "iam:AddClientIDToOpenIDConnectProvider", "iam:RemoveClientIDFromOpenIDConnectProvider" ], "Resource": "*", "Condition": { "ArnNotLike": { "aws:PrincipalArn": "arn:aws:iam::*:role/VouchDeploymentRole" } } } ] } ``` > **Note:** Replace `VouchDeploymentRole` with the principal your CloudFormation StackSet, Terraform pipeline, or platform team uses to manage the Vouch OIDC provider -- the same one referenced in [Deny deletion with an SCP](#deny-deletion-with-an-scp). Every other principal, including developers, is then blocked from creating or modifying OIDC providers. --- ## Protecting Vouch roles from accidental deletion A common convention is to prefix critical roles with `DO-NOT-DELETE-*`, but that is a social signal, not a control. Tired engineers ignore it; automation never reads it. Use technical guardrails as the real protection, and use names, paths, and tags only as the addressing scheme those guardrails attach to. ### Use an IAM path, not a name prefix Place Vouch roles under a dedicated IAM [path](https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_identifiers.html#identifiers-friendly-names) such as `/vouch/`. Paths are first-class in the role ARN, can be wildcarded in policy `Resource` fields, and don't pollute the role's display name: ``` arn:aws:iam::123456789012:role/vouch/VouchAccess ``` In CloudFormation, add a single line to the role: ```yaml VouchRole: Type: AWS::IAM::Role Properties: RoleName: VouchAccess Path: /vouch/ ``` In Terraform: ```hcl resource "aws_iam_role" "vouch" { name = var.role_name path = "/vouch/" # ... } ``` ### Tag for ABAC and inventory Tag every Vouch-managed resource so an SCP or audit query can find it even if someone forgets the path: ```yaml Tags: - Key: ManagedBy Value: Vouch - Key: Purpose Value: OIDCFederation ``` ### Deny deletion with an SCP This is the actual control. Deny destructive IAM actions against anything in the `/vouch/` path and against the Vouch OIDC provider, with an exception for your deployment principal so legitimate updates can still happen: ```json { "Version": "2012-10-17", "Statement": [ { "Sid": "ProtectVouchRoles", "Effect": "Deny", "Action": [ "iam:DeleteRole", "iam:DeleteRolePolicy", "iam:DetachRolePolicy", "iam:UpdateAssumeRolePolicy", "iam:DeleteOpenIDConnectProvider" ], "Resource": [ "arn:aws:iam::*:role/vouch/*", "arn:aws:iam::*:oidc-provider/us.vouch.sh" ], "Condition": { "ArnNotLike": { "aws:PrincipalArn": "arn:aws:iam::*:role/VouchDeploymentRole" } } } ] } ``` Replace `VouchDeploymentRole` with whatever principal your CloudFormation StackSet, Terraform pipeline, or platform team uses. ### IaC-level protections Belt-and-suspenders settings in your stack definitions catch the cases where someone bypasses the SCP exception and runs `terraform destroy` or deletes a CloudFormation stack: - **CloudFormation:** set `DeletionPolicy: Retain` and `UpdateReplacePolicy: Retain` on the role and OIDC provider resources. - **Terraform:** add `lifecycle { prevent_destroy = true }` to each resource. - **StackSets:** enable termination protection on the stack set itself. ### Detective backstop Even with all of the above, alert on the action so you find out fast if something slips through. An EventBridge rule on `DeleteRole` and `DeleteOpenIDConnectProvider` events filtered to the `/vouch/` path, targeting an SNS topic, gives you a notification within seconds. --- ## Role design patterns ### By environment The default spoke role is `/vouch/VouchAccess`, scoped per-account by the policy you attach. For higher-privilege production access, deploy a second spoke role with a tighter `aws:SourceIdentity` allowlist: | Account | Spoke role | Policy | Who can chain in | |---|---|---|---| | Development | `vouch/VouchAccess` | `PowerUserAccess` | Anyone in `*@example.com` | | Staging | `vouch/VouchAccess` | `PowerUserAccess` | Anyone in `*@example.com` | | Production | `vouch/VouchAccess` | `ReadOnlyAccess` | Anyone in `*@example.com` | | Production | `vouch/VouchDeploy` | Custom deploy policy | Specific emails only | ### Restricting production access to specific people Tighten the `aws:SourceIdentity` condition on the production deployer's spoke role: ```json "Condition": { "StringEquals": { "aws:SourceIdentity": [ "alice@example.com", "bob@example.com" ] } } ``` Only Alice and Bob can chain into `vouch/VouchDeploy`. Everyone else still gets the read-only `vouch/VouchAccess` spoke. --- ## Troubleshooting ### Credentials for the wrong account If `aws sts get-caller-identity` shows an unexpected account, verify you are using the correct profile: ```bash aws sts get-caller-identity --profile vouch-dev ``` Check `~/.aws/config` for the correct role ARN in each profile. --- # Access GitHub Repos without Personal Access Tokens Source: https://vouch.sh/docs/github/ Vouch replaces GitHub PATs and deploy keys with short-lived tokens (valid for up to 1 hour) issued through a [GitHub App](https://docs.github.com/en/apps) installed in your organization. Tokens are automatically scoped to the right repositories and tied to a hardware-verified identity. {{< tldr >}} - **Prerequisites:** [Getting Started](/docs/getting-started/) → this page. - **Admin, once:** [install the Vouch GitHub App](#step-1----install-the-vouch-github-app-admin) in your GitHub organization. - **Each developer:** `vouch setup github --configure`, then `git clone https://github.com/your-org/private-repo.git` just works. {{< /tldr >}} ## Step 1 -- Install the Vouch GitHub App (admin) {{< role admin >}} An organization administrator must connect at least one GitHub organization to the Vouch server before any team member can use the integration. 1. Navigate to https://us.vouch.sh/github/connect and follow the prompts to install the GitHub App in your GitHub organization. You can choose to grant access to all repositories or select specific ones. ![Connect GitHub page showing installation flow and connected accounts](/images/admin/github-connect.png) 2. After installing the app, confirm the organization connection on the GitHub connect page. The server will verify it can issue tokens for the connected organization. 3. Optionally, adjust which repositories the GitHub App has access to at any time through your GitHub organization settings under **Settings > GitHub Apps > Vouch**. --- ## Step 2 -- Configure Git Credential Helper {{< role developer >}} You need a **verified identity** linked to your Vouch account (via your organization's SSO provider). Run the setup command to install the Vouch credential helper for GitHub: ``` vouch setup github ``` This prints the Git configuration that will be added. To apply it automatically: ``` vouch setup github --configure ``` The command adds the following to your `~/.gitconfig`: ```ini [credential "https://github.com"] helper = vouch ``` This tells Git to use the Vouch credential helper whenever it needs credentials for `github.com` over HTTPS. --- ## Step 3 -- Use Git normally {{< role developer >}} {{< session-note >}} With the credential helper configured and an active session, Git commands work without any extra flags or tokens: ```bash # Clone a private repository git clone https://github.com/your-org/private-repo.git # Pull latest changes cd private-repo git pull # Push commits git add . git commit -m "Update feature" git push ``` Vouch handles authentication transparently. You do not need to enter a username, password, or token. --- ## How it works 1. **Git requests credentials** -- Git calls the Vouch credential helper when it needs to authenticate to `github.com`. 2. **Vouch exchanges your session** -- The credential helper contacts the Vouch server and exchanges your active hardware-backed session for a GitHub installation access token. 3. **GitHub App issues a token** -- The Vouch server uses the GitHub App to generate an access token valid for **1 hour**, scoped to the repositories your organization has granted access to, and never written to disk. If more than one GitHub organization is connected, Vouch selects the correct token based on the repository you are accessing. 4. **Git authenticates** -- The token is returned to Git and used for the current operation. --- ## Troubleshooting ### Authentication failed ``` fatal: Authentication failed for 'https://github.com/org/repo.git' ``` - Verify you have an active Vouch session by running `vouch login`. - Confirm the credential helper is configured: `git config --global credential.https://github.com.helper` should return `vouch`. - Check that the Vouch agent is running: `vouch status`. ### Organization not connected ``` error: GitHub organization "org-name" is not connected to this Vouch server ``` Your organization administrator has not yet installed the Vouch GitHub App for this organization. Ask them to connect the organization at https://us.vouch.sh/github/connect. ### Requires membership ``` error: you are not a member of the GitHub organization "org-name" ``` The GitHub App is installed, but your GitHub account is not a member of the organization. Verify that your GitHub username is associated with the correct organization and that your Vouch identity is linked to the right email address. ### Repository not accessible ``` error: repository not accessible with current token scope ``` The Vouch GitHub App does not have access to the specific repository you are trying to reach. Ask your organization administrator to update the app's repository permissions in GitHub organization settings. ### Multiple GitHub accounts If you have multiple GitHub accounts and the wrong one is being used: 1. Check which credential helpers are configured: `git config --global --get-all credential.https://github.com.helper` 2. Ensure Vouch is listed and no other helpers are overriding it. 3. If you use different GitHub accounts for different organizations, Vouch handles this automatically by issuing tokens scoped to the correct organization based on the repository URL. ### Wrong credential helper being used If another credential helper (such as `osxkeychain` or `manager`) is taking priority over Vouch: 1. List all configured helpers: ``` git config --show-origin --get-all credential.https://github.com.helper ``` 2. Remove or reorder conflicting entries so that `vouch` appears first. 3. Re-run `vouch setup github --configure` to ensure the configuration is correct. --- ## Related guides - [Getting Started](/docs/getting-started/) -- Install the CLI and enroll your YubiKey. - [AWS Integration](/docs/aws/) -- Federate into AWS with OIDC for temporary STS credentials. - [Docker Registries](/docs/docker/) -- Authenticate to container registries like ECR and GHCR. - [AWS CodeCommit](/docs/codecommit/) -- Authenticate to AWS CodeCommit Git repositories. --- # Vouch for Startups Source: https://vouch.sh/docs/startups/ You just created an AWS account. Every tutorial says "create an IAM user." Don't. IAM users come with long-lived access keys that never expire, get committed to Git, leaked in logs, and compromised by malware. Rotating them is a manual chore. When someone leaves, you have to hunt down every key they ever created. And none of this is necessary -- AWS supports OIDC federation, which means you can authenticate with the identity system your team already uses. If your team uses **Google Workspace**, Vouch bridges it directly into AWS. One `vouch login` gives every developer short-lived credentials for AWS, SSH, GitHub, Docker registries, databases, and more -- all tied to their Google Workspace identity, all backed by a hardware key. > **Already have AWS accounts and a growing team?** This page is for day-one setups. For rolling Vouch out across an existing organization -- service enablement checklists, onboarding blocks, offboarding -- use the [Team Rollout playbook](/docs/rollout/). Wondering how Vouch compares to [IAM Identity Center or Builder ID](#why-not-iam-identity-center)? That's at the end. --- ## What you get After following this guide, your team will have: - **No IAM users** -- Every developer authenticates with their Google Workspace account + YubiKey. - **No access keys** -- AWS credentials are temporary (1 hour) and never written to disk. - **No credential files** -- No `~/.aws/credentials`, no SSH keys to distribute, no GitHub PATs. - **Instant offboarding** -- When someone's Google Workspace account is deactivated, their AWS access ends immediately. - **Full audit trail** -- Every AWS API call in CloudTrail shows which developer made it. --- ## Step 1 -- Get YubiKeys for the team Order [YubiKey 5 series](https://www.yubico.com/products/yubikey-5-overview/) keys for each team member. Any FIDO2-compatible security key works, but YubiKey 5 series is recommended. --- ## Step 2 -- Enroll the team The first person to log into Vouch from your Google Workspace domain becomes the organization owner. **Owner enrollment:** ```bash # Install the CLI brew install vouch-sh/tap/vouch brew services start vouch # Enroll (first person becomes the org owner) vouch enroll --server https://us.vouch.sh ``` **Team member enrollment:** Each team member installs the CLI and enrolls with the same server: ```bash brew install vouch-sh/tap/vouch brew services start vouch vouch enroll --server https://us.vouch.sh ``` As long as they authenticate with the same Google Workspace domain, they join the same organization. No invite codes or admin approval needed for initial enrollment. --- ## Step 3 -- Deploy AWS federation Follow the [AWS integration guide](/docs/aws/) to register the OIDC provider and deploy a role with a `*@yourcompany.com` email-domain trust condition. For permissions, **start with `ReadOnlyAccess`** and broaden to exactly what your team needs -- don't default to `PowerUserAccess`. You can tighten access further later (see [Tips for restricting access](/docs/aws/#tips-for-restricting-access) for single-user restrictions, ABAC session tags, and similar patterns). When the role exists, copy its ARN -- you'll need it in Step 4. --- ## Step 4 -- Configure each developer's CLI Each developer runs one command to configure their AWS profile: ```bash vouch setup aws --role arn:aws:iam::123456789012:role/VouchDeveloper ``` Replace the account ID with your own (printed in the CloudFormation stack outputs). --- ## Step 5 -- Log in and verify ```bash vouch login aws sts get-caller-identity --profile vouch ``` You should see your email address in the assumed role ARN: ```json { "UserId": "AROA...:alice@yourcompany.com", "Account": "123456789012", "Arn": "arn:aws:sts::123456789012:assumed-role/VouchDeveloper/alice@yourcompany.com" } ``` From here, `aws s3 ls --profile vouch`, `cdk deploy`, `terraform apply`, and any other AWS tool works with no additional setup. --- ## Step 6 -- Add more integrations With the same `vouch login` session, configure the rest of your toolchain: | Integration | Setup | Docs | |---|---|---| | SSH certificates | Automatic after login | [SSH](/docs/ssh/) | | GitHub | `vouch setup github` | [GitHub](/docs/github/) | | Docker (ECR) | `vouch setup docker` | [Docker](/docs/docker/) | | CodeCommit | `vouch setup codecommit` | [CodeCommit](/docs/codecommit/) | | CodeArtifact | `vouch setup codeartifact` | [CodeArtifact](/docs/codeartifact/) | | EKS | `vouch setup eks` | [EKS](/docs/eks/) | Each integration takes one command. After setup, every tool uses the same session -- one YubiKey tap covers the entire developer toolchain. --- ## What happens when someone leaves This is where the investment pays off: deactivate their Google Workspace account (which you were going to do anyway) and their access ends -- no access keys to hunt down, no SSH keys to remove from servers, no PATs to revoke. The exact sequence, expiry timeline, and the optional AWS-side deny statement are in [When someone leaves](/docs/rollout/#when-someone-leaves) on the rollout playbook. --- ## Scaling up As your team grows, switch to the [Team Rollout playbook](/docs/rollout/) -- it covers service enablement, onboarding, and offboarding as an ongoing process. The usual thresholds: - **5--15 people:** Manual user management works fine. SCIM is optional. - **15--50 people:** Set up [SCIM provisioning](/docs/scim/) to automate onboarding and offboarding with Google Workspace. - **Multiple AWS accounts:** See [Multi-Account AWS Strategy](/docs/aws-multi-account/) for chaining into dev/staging/prod accounts through a single hub. - **CI/CD gates:** Add [human approval gates](/docs/cicd/) to production deployments. - **Compliance requirements:** See [Security](/docs/security/) and the [Threat Model](/docs/threat-model/) for details on how Vouch protects credentials. --- ## Why not IAM Identity Center? [AWS IAM Identity Center](https://docs.aws.amazon.com/singlesignon/latest/userguide/what-is.html) (formerly AWS SSO) is AWS's own solution for federated access. It's a good product, but it's designed for enterprises with dozens of accounts and hundreds of users. For a startup: | Consideration | IAM Identity Center | Vouch | |---|---|---| | **Setup complexity** | Requires an AWS Organizations management account, an Identity Center instance, permission sets, and user/group sync | Deploy one CloudFormation template and run `vouch setup aws` | | **Scope** | AWS only | AWS + SSH + GitHub + Docker + CodeCommit + CodeArtifact + databases + more | | **Authentication** | Browser-based SSO (MFA depends on IdP config) | FIDO2 hardware key (phishing-resistant by design) | | **Team size sweet spot** | 20+ people across multiple accounts | 2--50 people | | **Credential type** | Session credentials via `aws sso login` | Session credentials via `vouch login` | If you have a large organization with complex permission requirements across many AWS accounts, IAM Identity Center is the right choice (and Vouch can [federate into it](/docs/aws-multi-account/#aws-iam-identity-center)). If you are a startup that wants secure AWS access without the overhead, Vouch gets you there faster. ## Why not AWS Builder ID? [AWS Builder ID](https://docs.aws.amazon.com/signin/latest/userguide/sign-in-aws_builder_id.html) provides individual developer identity for AWS services. The key difference: Builder ID is **individual** identity, not **organizational** identity. It does not know about your Google Workspace domain, your team structure, or your offboarding process. You cannot restrict AWS access to "people who work at my company" using Builder ID alone. Vouch federates your organization's identity (Google Workspace domain) into AWS, so access is tied to employment by design. --- # Authenticate to Amazon EKS without Static Credentials Source: https://vouch.sh/docs/eks/ > **Not using EKS?** For standard Kubernetes clusters (self-hosted, GKE, AKS, k3s, etc.) that use OIDC authentication, see [Kubernetes](/docs/kubernetes/). [EKS Access Entries](https://docs.aws.amazon.com/eks/latest/userguide/access-entries.html) map IAM principals directly to Kubernetes permissions, with every authentication event recorded in CloudTrail. Combined with Vouch, every `kubectl` command traces back to a hardware-verified human identity -- no static tokens, no shared kubeconfigs. {{< tldr >}} - **Prerequisites:** [Getting Started](/docs/getting-started/) → [AWS integration](/docs/aws/) → this page. - **Admin, once per cluster:** [create an Access Entry](#creating-eks-access-entries) mapping the Vouch IAM role to cluster permissions; the cluster's auth mode must include `API`. - **Each developer:** `vouch setup eks --cluster <NAME>`, then `kubectl get pods` on the `<NAME>-vouch` context. {{< /tldr >}} ## Prerequisites Before setting up EKS authentication with Vouch, ensure you have: - **Vouch CLI installed and enrolled** -- Complete the [Getting Started](/docs/getting-started/) guide. - **AWS integration configured** -- Complete the [AWS Integration](/docs/aws/) guide. You need a working `vouch credential aws` setup with an IAM role. - **kubectl** installed (`kubectl version --client`). - **An EKS cluster** with the API server authentication mode set to include `API` (either `API` or `API_AND_CONFIG_MAP`). Clusters created with the default `CONFIG_MAP` mode must be updated. > See [EKS cluster authentication modes](https://docs.aws.amazon.com/eks/latest/userguide/cluster-auth.html) for details on switching to API mode, and [EKS Access Entries](https://docs.aws.amazon.com/eks/latest/userguide/access-entries.html) for the Access Entries feature. --- ## Setup {{< role developer >}} Configure `kubectl` to use your Vouch-backed credentials for cluster authentication (an admin must have [created an Access Entry](#creating-eks-access-entries) for your role first): ```bash vouch setup eks --cluster YOUR_CLUSTER_NAME ``` Optional flags: - `--region` -- AWS region (auto-detected from your AWS profile or environment if not specified). - `--profile` -- AWS profile to use (defaults to the auto-detected Vouch profile). - `--kubeconfig` -- Path to kubeconfig file (defaults to `~/.kube/config`). This command fetches the cluster endpoint and CA certificate via a native SigV4-signed EKS `DescribeCluster` API call, then writes or updates your kubeconfig with an `exec`-based user entry that calls `vouch credential eks`. The context is named `YOUR_CLUSTER_NAME-vouch`. ### Verify the kubeconfig Check that the context is set and working: ```bash kubectl config use-context YOUR_CLUSTER_NAME-vouch kubectl get pods ``` --- ## Usage {{< role developer >}} With everything configured, daily usage is straightforward: ```bash # Start your day vouch login # Switch to your Vouch EKS context kubectl config use-context YOUR_CLUSTER_NAME-vouch # Use kubectl as normal kubectl get pods kubectl get namespaces kubectl logs deployment/my-app ``` All authentication happens transparently. If your session expires (after 8 hours), run `vouch login` again. --- ## Creating EKS Access Entries {{< role admin >}} EKS Access Entries map IAM principals (users or roles) to Kubernetes permissions. An administrator must create an Access Entry for the IAM role used by Vouch. #### AWS CLI ```bash # Create the Access Entry for the Vouch IAM role aws eks create-access-entry \ --cluster-name YOUR_CLUSTER_NAME \ --principal-arn arn:aws:iam::123456789012:role/VouchDeveloper \ --type STANDARD # Associate an access policy (e.g., cluster admin) aws eks associate-access-policy \ --cluster-name YOUR_CLUSTER_NAME \ --principal-arn arn:aws:iam::123456789012:role/VouchDeveloper \ --policy-arn arn:aws:eks::aws:cluster-access-policy/AmazonEKSClusterAdminPolicy \ --access-scope '{"type": "cluster"}' ``` To restrict access to specific namespaces: ```bash aws eks associate-access-policy \ --cluster-name YOUR_CLUSTER_NAME \ --principal-arn arn:aws:iam::123456789012:role/VouchDeveloper \ --policy-arn arn:aws:eks::aws:cluster-access-policy/AmazonEKSEditPolicy \ --access-scope '{"type": "namespace", "namespaces": ["default", "staging"]}' ``` #### Terraform ```hcl resource "aws_eks_access_entry" "vouch_developer" { cluster_name = aws_eks_cluster.main.name principal_arn = aws_iam_role.vouch_developer.arn type = "STANDARD" } resource "aws_eks_access_policy_association" "vouch_developer_admin" { cluster_name = aws_eks_cluster.main.name principal_arn = aws_iam_role.vouch_developer.arn policy_arn = "arn:aws:eks::aws:cluster-access-policy/AmazonEKSClusterAdminPolicy" access_scope { type = "cluster" } } ``` For namespace-scoped access: ```hcl resource "aws_eks_access_policy_association" "vouch_developer_edit" { cluster_name = aws_eks_cluster.main.name principal_arn = aws_iam_role.vouch_developer.arn policy_arn = "arn:aws:eks::aws:cluster-access-policy/AmazonEKSEditPolicy" access_scope { type = "namespace" namespaces = ["default", "staging"] } } ``` --- ## Available Access Policies EKS provides several built-in [access policies](https://docs.aws.amazon.com/eks/latest/userguide/access-policies.html) that map to standard Kubernetes RBAC roles: | Access Policy ARN | Kubernetes Equivalent | Description | |---|---|---| | `arn:aws:eks::aws:cluster-access-policy/AmazonEKSClusterAdminPolicy` | `cluster-admin` | Full access to all resources in the cluster. | | `arn:aws:eks::aws:cluster-access-policy/AmazonEKSAdminPolicy` | `admin` | Full access within namespaces, plus limited cluster-scoped access. | | `arn:aws:eks::aws:cluster-access-policy/AmazonEKSEditPolicy` | `edit` | Read/write access to most resources in a namespace (no role or role-binding changes). | | `arn:aws:eks::aws:cluster-access-policy/AmazonEKSViewPolicy` | `view` | Read-only access to most resources in a namespace. | | `arn:aws:eks::aws:cluster-access-policy/AmazonEKSAdminViewPolicy` | N/A | Read-only access to all resources in the cluster, including secrets. | When associating a policy, choose the appropriate **access scope**: - **`cluster`** -- The policy applies across all namespaces. - **`namespace`** -- The policy applies only to the specified namespaces. Cluster-scoped policies (like `AmazonEKSClusterAdminPolicy`) must use `type: cluster`. Namespace-scoped policies (like `AmazonEKSEditPolicy`) can use either scope type. --- ## Custom RBAC If the built-in access policies do not fit your needs, you can use standard Kubernetes RBAC instead. Create the Access Entry with type `STANDARD` and do not associate any EKS access policies. Then create Kubernetes `ClusterRoleBinding` or `RoleBinding` resources that reference the IAM role's assumed-role ARN. ```yaml apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: name: vouch-developer-custom subjects: - kind: Group name: "arn:aws:iam::123456789012:role/VouchDeveloper" apiGroup: rbac.authorization.k8s.io roleRef: kind: ClusterRole name: your-custom-role apiGroup: rbac.authorization.k8s.io ``` For per-user permissions using session tags from Vouch, you can create separate IAM roles per team or per access level and map each to different Kubernetes roles. --- ## How it works The authentication flow chains three components: ``` vouch login --> vouch credential eks --> kubectl ``` 1. **`vouch login`** -- The developer authenticates with their YubiKey and receives an OIDC ID token from the Vouch server. 2. **`vouch credential eks`** -- The CLI exchanges the OIDC token for temporary AWS STS credentials, then builds a presigned STS `GetCallerIdentity` URL with the `x-k8s-aws-id` header, base64url-encodes it, and outputs a Kubernetes `ExecCredential` JSON. 3. **`kubectl`** -- The Kubernetes client sends the token to the EKS API server, which validates it against IAM and applies the permissions defined by Access Entries or RBAC. Because every step uses short-lived credentials, there are no static kubeconfig tokens or long-lived AWS keys to manage. No AWS CLI installation is required -- Vouch handles STS and EKS API calls natively. --- ## EKS authentication modes compared EKS supports three ways to manage cluster authentication. The `aws-auth` ConfigMap is considered legacy; AWS recommends Access Entries for all new clusters, and Vouch builds on them. | | **`aws-auth` ConfigMap** | **EKS Access Entries** | **Access Entries + Vouch** | |---|---|---|---| | **How it works** | A Kubernetes ConfigMap (`kube-system/aws-auth`) maps IAM principals to Kubernetes users/groups. | IAM principals are mapped to Kubernetes permissions via the EKS API, outside the cluster. | Same as Access Entries, but credentials are issued through Vouch's OIDC-backed flow. | | **Credential type** | Long-lived kubeconfig tokens or static IAM keys. | Temporary STS credentials. | Short-lived STS credentials; no local AWS keys needed. | | **Access management** | Edit a ConfigMap with `kubectl`. Changes are immediate but unversioned. | Create and modify entries via the AWS API, CLI, or Terraform. | Same AWS API/Terraform workflow as Access Entries. | | **Audit trail** | No native audit trail for ConfigMap edits. Kubernetes audit logs show API calls but not who edited the map. | All changes recorded in CloudTrail. Authentication events logged. | CloudTrail logs plus Vouch audit trail tying every action to a hardware-verified identity. | | **Granularity** | Map IAM roles/users to Kubernetes groups; RBAC handles the rest. | Built-in access policies (view, edit, admin, cluster-admin) with cluster or namespace scope, plus custom RBAC. | Same granularity as Access Entries. | | **Revocation** | Edit or delete the ConfigMap entry. Easy to make mistakes. | Delete the access entry via the AWS API. | Remove the IAM role mapping or revoke the user's Vouch enrollment. | | **Risk** | Misconfigured ConfigMap can lock out all users, including admins. Shared tokens hard to revoke per-user. | No cluster lockout risk -- cluster creator always retains access. Per-principal entries are independent. | Same safety as Access Entries, with the added benefit of no static credentials on developer machines. | | **EKS auth mode** | `CONFIG_MAP` or `API_AND_CONFIG_MAP` | `API` or `API_AND_CONFIG_MAP` | `API` or `API_AND_CONFIG_MAP` | --- ## Troubleshooting ### "error: You must be logged in to the server (Unauthorized)" - Confirm you have an active Vouch session: `vouch status`. - Verify AWS credentials are working: `vouch credential aws`. - Check that an EKS Access Entry exists for your IAM role: `aws eks list-access-entries --cluster-name YOUR_CLUSTER_NAME`. - Ensure the cluster's authentication mode includes `API`. Check with: `aws eks describe-cluster --name YOUR_CLUSTER_NAME --query "cluster.accessConfig.authenticationMode"`. ### "AWS not configured" error - Run `vouch setup aws --role <role-arn>` first to configure the AWS integration before setting up EKS. See the [AWS Integration](/docs/aws/) guide. ### Credentials expire during long operations - STS credentials obtained through Vouch last up to 1 hour. The EKS token itself is short-lived, but kubectl re-fetches it automatically via the exec plugin on each command. - For long-running operations such as Helm deployments or large-scale rollouts, run `vouch login` beforehand to ensure a fresh 8-hour session. - If a command fails mid-operation, run `vouch login` and retry. The kubeconfig exec plugin will automatically pick up the new credentials. ### "AccessDeniedException" when calling EKS APIs - The IAM role assumed by Vouch needs `eks:DescribeCluster` permission (at minimum) to run `vouch setup eks`. - For Access Entry management, the administrator's IAM role needs `eks:CreateAccessEntry`, `eks:AssociateAccessPolicy`, and related permissions. ### Cannot see resources in a specific namespace - Check the access scope of the associated access policy. If the policy is scoped to specific namespaces, you can only access resources in those namespaces. - Verify with: `aws eks list-associated-access-policies --cluster-name YOUR_CLUSTER_NAME --principal-arn YOUR_ROLE_ARN`. ### kubectl works but Helm does not - Helm may require additional permissions beyond what `view` or `edit` policies provide (e.g., creating `ServiceAccount`, `Role`, or `RoleBinding` resources). Consider using `AmazonEKSAdminPolicy` or a custom RBAC role that grants the necessary permissions. ### Diagnosing configuration issues - Run `vouch doctor` to detect Vouch-configured EKS contexts and check for common misconfigurations. --- # Authenticate to Container Registries without Stored Passwords Source: https://vouch.sh/docs/docker/ Vouch's [credential helper](https://docs.docker.com/engine/reference/commandline/login/#credential-helpers) generates registry tokens on demand -- no stored passwords, no refresh scripts, and no `docker login`. After a single `vouch login`, Docker pulls and pushes to supported registries authenticate automatically. {{< tldr >}} - **Prerequisites:** [Getting Started](/docs/getting-started/) → this page; the ECR path also requires the [AWS integration](/docs/aws/). - **Admin, once:** grant the [registry-specific permissions](#registry-specific-setup) -- ECR actions on the Vouch IAM role, or `packages:read` on the GitHub App. - **Each developer:** `vouch setup docker --configure <registry>`, then `docker pull` and `docker push` just work. {{< /tldr >}} ## Supported Registries | Registry | Domain Pattern | Token Type | |---|---|---| | **AWS ECR** | `<account-id>.dkr.ecr.<region>.amazonaws.com` | AWS STS temporary credentials | | **GitHub Container Registry** | `ghcr.io` | GitHub installation access token | --- ## Prerequisites {{< role admin >}} Before developers can configure the Docker integration: - For **ECR**: AWS IAM must be configured to trust the Vouch OIDC provider (see [AWS Integration](/docs/aws/)) - For **GHCR**: A GitHub organization must be connected to the Vouch server (see [GitHub Integration](/docs/github/)) --- ## Step 1 -- Configure Docker Credential Helper {{< role developer >}} With Docker installed and running, run the setup command to install the Vouch Docker credential helper: ``` vouch setup docker ``` This prints the Docker configuration that will be added. To apply it automatically for a specific registry: ```bash # Configure for GitHub Container Registry vouch setup docker --configure ghcr.io # Configure for AWS ECR vouch setup docker --configure 123456789012.dkr.ecr.us-east-1.amazonaws.com ``` The command updates your `~/.docker/config.json` to register the Vouch credential helper for the specified registry: ```json { "credHelpers": { "ghcr.io": "vouch", "123456789012.dkr.ecr.us-east-1.amazonaws.com": "vouch" } } ``` You can configure multiple registries by running the command once for each registry domain. --- ## Step 2 -- Use Docker normally {{< role developer >}} {{< session-note >}} With the credential helper configured and an active session, Docker commands work without any extra flags or manual login: ```bash # Pull from GitHub Container Registry docker pull ghcr.io/your-org/your-image:latest # Pull from AWS ECR docker pull 123456789012.dkr.ecr.us-east-1.amazonaws.com/your-repo:latest # Push to GitHub Container Registry docker push ghcr.io/your-org/your-image:v1.2.3 # Push to AWS ECR docker push 123456789012.dkr.ecr.us-east-1.amazonaws.com/your-repo:v1.2.3 ``` Vouch handles authentication transparently. You do not need to run `docker login` or `aws ecr get-login-password`. --- ## Registry-Specific Setup {{< role admin >}} ### AWS ECR For ECR authentication, Vouch uses your AWS integration to obtain temporary STS credentials, which are then exchanged for an ECR authorization token. **IAM Policy** -- The IAM role assumed by Vouch must include ECR permissions. At minimum, the role needs: ```json { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "ecr:GetAuthorizationToken", "ecr:BatchGetImage", "ecr:GetDownloadUrlForLayer", "ecr:BatchCheckLayerAvailability" ], "Resource": "*" } ] } ``` For push access, add these additional actions: ```json { "Effect": "Allow", "Action": [ "ecr:PutImage", "ecr:InitiateLayerUpload", "ecr:UploadLayerPart", "ecr:CompleteLayerUpload" ], "Resource": "arn:aws:ecr:us-east-1:123456789012:repository/*" } ``` See the [AWS Integration](/docs/aws/) guide for full IAM configuration details. ### GitHub Container Registry (GHCR) For GHCR authentication, Vouch uses the same GitHub App integration used for Git access. The token issued is scoped to the packages your organization has granted access to. **Token scope** -- The GitHub App must have the `packages:read` permission (and `packages:write` if you need push access). Your organization administrator can configure this when installing the Vouch GitHub App. See the [GitHub Integration](/docs/github/) guide for GitHub App setup details. --- ## How it works 1. **Docker requests credentials** -- The Docker daemon calls the `docker-credential-vouch` helper when it needs to authenticate to a configured registry. 2. **Vouch exchanges your session** -- The credential helper contacts the Vouch server and exchanges your active hardware-backed session for registry-specific credentials. 3. **Short-lived token issued** -- The Vouch server returns a temporary token appropriate for the target registry (an AWS STS token for ECR, or a GitHub token for GHCR). Tokens are generated on demand and never persisted to disk. 4. **Docker authenticates** -- The token is passed back to Docker and used for the current pull or push operation. --- ## Troubleshooting ### docker-credential-vouch not found ``` error getting credentials - err: exec: "docker-credential-vouch": executable file not found in $PATH ``` The `docker-credential-vouch` binary is not in your `PATH`. This binary is included with the Vouch CLI installation. Verify: 1. The Vouch CLI is installed: `vouch --version` 2. The credential helper binary exists: `which docker-credential-vouch` 3. If the binary is missing, reinstall the Vouch CLI or ensure the installation directory is in your `PATH`. ### Authentication failed ``` Error response from daemon: Head "https://ghcr.io/v2/...": denied ``` - Verify you have an active Vouch session: `vouch login` - Check that the credential helper is configured for the registry: inspect `~/.docker/config.json` and confirm the registry appears in `credHelpers`. - Ensure the Vouch agent is running: `vouch status` ### AWS not configured ``` error: AWS integration is not configured for this Vouch server ``` Your organization administrator has not set up the AWS OIDC provider. Ask them to complete the [AWS Integration](/docs/aws/) setup before using ECR through Vouch. ### Unsupported registry ``` error: registry "registry.example.com" is not supported by Vouch ``` Vouch currently supports AWS ECR and GitHub Container Registry (ghcr.io). Other registries are not yet supported. Check the [supported registries](#supported-registries) table above. ### Credentials not being used If Docker appears to ignore the Vouch credential helper and prompts for a username and password: 1. Check for conflicting `credsStore` settings in `~/.docker/config.json`. A top-level `credsStore` may override individual `credHelpers` entries. 2. Remove or rename any conflicting credential store: ```json { "credsStore": "", "credHelpers": { "ghcr.io": "vouch" } } ``` 3. Ensure there are no cached credentials for the registry. Run `docker logout <registry>` to clear any stored credentials, then retry. --- ## Helm & Compatible Tools ### Helm Helm 3.8+ supports OCI registries for chart storage. Because Helm reads the Docker credential store, charts hosted in ECR or GHCR authenticate through Vouch automatically: ```bash # Push a chart to ECR helm push my-chart-0.1.0.tgz oci://123456789012.dkr.ecr.us-east-1.amazonaws.com/charts # Pull a chart from ECR helm pull oci://123456789012.dkr.ecr.us-east-1.amazonaws.com/charts/my-chart --version 0.1.0 # Install directly from OCI helm install my-release oci://123456789012.dkr.ecr.us-east-1.amazonaws.com/charts/my-chart ``` ### Other compatible tools Any tool that reads `~/.docker/config.json` credHelpers will use Vouch automatically: - **crane** -- Image manipulation without a Docker daemon - **skopeo** -- Copy images between registries - **Podman** -- Daemonless container engine - **buildah** -- OCI image builder - **ORAS** -- OCI artifacts (Wasm modules, ML models, signatures) --- # Automate User Provisioning with SCIM Source: https://vouch.sh/docs/scim/ Manually adding and removing users from Vouch when people join or leave your organization is error-prone and easy to forget. A missed offboarding means someone retains access to hardware-backed credentials they should no longer have. SCIM (System for Cross-domain Identity Management) lets your identity provider -- Google Workspace, Okta, Azure AD, or OneLogin -- handle this automatically in real time. Vouch supports the **SCIM 2.0** protocol ([RFC 7644](https://datatracker.ietf.org/doc/html/rfc7644)) for automated user provisioning and de-provisioning. When SCIM is configured, your identity provider (IdP) can automatically: - **Create** new Vouch user accounts when people join your organization. - **Update** user attributes (name, email, role) when they change in your directory. - **Deactivate** accounts instantly when someone leaves or changes roles. This eliminates manual user management and ensures that credential access is always in sync with your corporate directory. --- ## How It Works 1. You generate a SCIM bearer token from the Vouch server. 2. You configure your identity provider to point at Vouch's SCIM 2.0 endpoint. 3. The IdP pushes user lifecycle events (create, update, deactivate) to Vouch in real time. 4. Vouch processes each event and updates its internal user directory accordingly. Because SCIM is a standardized protocol, Vouch works with any identity provider that supports SCIM 2.0 -- including Google Workspace, Okta, Azure AD (Entra ID), and OneLogin. --- ## Step 1 -- Generate a SCIM Token Before your identity provider can communicate with Vouch, you need to generate a bearer token that the IdP will use to authenticate its requests. First, ensure you are logged in with an account that has **organization administrator** privileges: ```bash vouch login ``` Then create a SCIM token: ```bash curl -X POST https://us.vouch.sh/api/v1/org/scim-tokens \ -b ~/.local/state/vouch/cookie.txt \ -H "Content-Type: application/json" \ -d '{"description": "Google Workspace SCIM", "expires_in_days": 365}' ``` The response includes the plaintext token: ```json { "id": "scim_tok_abc123", "description": "Google Workspace SCIM", "token": "vouch_scim_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", "expires_at": "2027-01-15T00:00:00Z", "created_at": "2026-01-15T12:00:00Z" } ``` > **Security note:** Vouch stores only a cryptographic hash of the token. The plaintext value is shown **exactly once** in the creation response. Copy it immediately and store it securely -- you will not be able to retrieve it again. If you lose the token, revoke it and create a new one. The `expires_in_days` field is **required** and must be an integer between **1** and **365**. Choose an expiration period that balances security with operational convenience. Most organizations use 365 days and rotate tokens annually. --- ## Step 2 -- Configure Your Identity Provider Use the following values when configuring SCIM in your identity provider: | Setting | Value | |---|---| | **SCIM Base URL** | `https://us.vouch.sh/scim/v2` | | **Authentication Type** | Bearer Token | | **Authorization Header** | `Bearer vouch_scim_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx` | Replace the token value with the plaintext token you received in Step 1. ### Google Workspace 1. Open the [Google Admin Console](https://admin.google.com). 2. Navigate to **Apps > Web and mobile apps**. 3. Find or add the Vouch application. 4. Open the **Auto-provisioning** section. 5. Set the **SCIM Base URL** to `https://us.vouch.sh/scim/v2`. 6. Set the **Authentication Type** to **Bearer Token** and paste your SCIM token. 7. Click **Test Connection** to verify. 8. Enable auto-provisioning and configure the desired attribute mappings. 9. Click **Save**. ### Okta 1. Open the Okta Admin Dashboard. 2. Navigate to **Applications > Applications**. 3. Select or create the Vouch application. 4. Go to the **Provisioning** tab and click **Configure API Integration**. 5. Check **Enable API Integration**. 6. Set the **SCIM 2.0 Base URL** to `https://us.vouch.sh/scim/v2`. 7. Set the **API Token** to the SCIM bearer token from Step 1. 8. Click **Test API Credentials** to verify connectivity. 9. Click **Save**. 10. Under **Provisioning > To App**, enable the desired actions: **Create Users**, **Update User Attributes**, and **Deactivate Users**. ### Azure AD (Entra ID) 1. Open the [Azure Portal](https://portal.azure.com) and navigate to **Azure Active Directory > Enterprise Applications**. 2. Select or create the Vouch application. 3. Go to **Provisioning** and set the **Provisioning Mode** to **Automatic**. 4. Under **Admin Credentials**: - Set **Tenant URL** to `https://us.vouch.sh/scim/v2`. - Set **Secret Token** to the SCIM bearer token from Step 1. 5. Click **Test Connection** to verify that Azure can reach the Vouch SCIM endpoint. 6. Configure attribute mappings under **Mappings** to align Azure AD attributes with Vouch user fields. 7. Set **Provisioning Status** to **On**. 8. Click **Save** to begin provisioning. ### OneLogin 1. Open the OneLogin Admin Panel. 2. Navigate to **Applications > Applications**. 3. Select or create the Vouch application. 4. Go to the **Provisioning** tab. 5. Enable provisioning and set the **SCIM Base URL** to `https://us.vouch.sh/scim/v2`. 6. Set the **SCIM Bearer Token** to the token from Step 1. 7. Under **Provisioning Actions**, enable **Create user**, **Update user**, and **Delete user**. 8. Click **Save**. --- ## Step 3 -- Test the Integration After configuring your identity provider, verify that the SCIM connection is working correctly by querying the Vouch SCIM endpoints directly. ### Check the Service Provider Configuration This endpoint returns the SCIM capabilities supported by Vouch: ```bash curl -s https://us.vouch.sh/scim/v2/ServiceProviderConfig \ -H "Authorization: Bearer vouch_scim_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \ | jq . ``` A successful response returns a JSON object describing the supported SCIM features, including authentication schemes, bulk support, and filtering capabilities. ### List Provisioned Users Retrieve the list of users that have been provisioned through SCIM: ```bash curl -s https://us.vouch.sh/scim/v2/Users \ -H "Authorization: Bearer vouch_scim_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \ | jq . ``` This returns a `ListResponse` containing all SCIM-managed users and their attributes. ### List Provisioned Groups Retrieve the list of groups that have been provisioned through SCIM: ```bash curl -s https://us.vouch.sh/scim/v2/Groups \ -H "Authorization: Bearer vouch_scim_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \ | jq . ``` This returns a `ListResponse` containing all SCIM-managed groups and their membership information. --- ## SCIM 2.0 Endpoints Vouch implements the following SCIM 2.0 endpoints: | Method | Endpoint | Description | |---|---|---| | `GET` | `/scim/v2/ServiceProviderConfig` | Returns the SCIM service provider configuration and supported features. | | `GET` | `/scim/v2/ResourceTypes` | Lists the resource types (Users, Groups) supported by the server. | | `GET` | `/scim/v2/Schemas` | Returns the full SCIM schemas supported by the server. | | `GET` | `/scim/v2/Users` | Lists all provisioned users. Supports filtering and pagination. | | `POST` | `/scim/v2/Users` | Creates a new user account. | | `GET` | `/scim/v2/Users/{id}` | Retrieves a specific user by their SCIM ID. | | `PUT` | `/scim/v2/Users/{id}` | Replaces all attributes of a specific user. | | `PATCH` | `/scim/v2/Users/{id}` | Updates specific attributes of a user (partial update). | | `DELETE` | `/scim/v2/Users/{id}` | Deactivates (soft-deletes) a user account. | | `GET` | `/scim/v2/Groups` | Lists all provisioned groups. Supports filtering and pagination. | | `POST` | `/scim/v2/Groups` | Creates a new group. | | `GET` | `/scim/v2/Groups/{id}` | Retrieves a specific group by its SCIM ID. | | `PUT` | `/scim/v2/Groups/{id}` | Replaces all attributes of a specific group. | | `PATCH` | `/scim/v2/Groups/{id}` | Updates specific attributes of a group (partial update). | | `DELETE` | `/scim/v2/Groups/{id}` | Deletes a group. | All endpoints require a valid SCIM bearer token in the `Authorization` header. Filtering is supported on the `Users` and `Groups` list endpoints using the SCIM filter syntax (e.g., `?filter=userName eq "alice@example.com"`). --- ## Immediate De-provisioning One of the most important benefits of SCIM integration is **immediate de-provisioning**. When an employee leaves your organization or changes roles: 1. Your identity provider sends a `PATCH` or `DELETE` request to the Vouch SCIM endpoint to deactivate the user. 2. Vouch immediately marks the user account as inactive. 3. All **active sessions** for that user are revoked instantly. 4. The user can no longer obtain new credentials. Previously issued short-lived credentials (SSH certificates, AWS STS credentials) will continue to function until their natural expiration (up to 8 hours). No new credentials can be issued after de-provisioning. Because all Vouch credentials are short-lived (maximum 8 hours), the exposure window after de-provisioning is limited. Sessions are revoked immediately, and outstanding credentials expire on their own shortly after. This is a significant security improvement over traditional provisioning workflows where revoking access requires manual steps across multiple systems. With Vouch and SCIM, de-provisioning is automated and the blast radius is minimized by the short credential lifetime. --- ## Managing SCIM Tokens Organization administrators can list, create, and revoke SCIM tokens through the Vouch API. ### List All SCIM Tokens Retrieve all active SCIM tokens for your organization: ```bash curl -s https://us.vouch.sh/api/v1/org/scim-tokens \ -b ~/.local/state/vouch/cookie.txt \ | jq . ``` The response includes token metadata (ID, description, creation date, expiration date) but never the plaintext token value. ### Create a New Token ```bash curl -X POST https://us.vouch.sh/api/v1/org/scim-tokens \ -b ~/.local/state/vouch/cookie.txt \ -H "Content-Type: application/json" \ -d '{"description": "Okta SCIM Integration", "expires_in_days": 180}' ``` The response includes the plaintext token. Store it securely -- it will not be shown again. ### Revoke a Token Revoke a SCIM token by its ID to immediately disable it: ```bash curl -X DELETE https://us.vouch.sh/api/v1/org/scim-tokens/scim_tok_abc123 \ -b ~/.local/state/vouch/cookie.txt ``` After revocation, any identity provider using this token will receive `401 Unauthorized` responses and provisioning will stop until a new token is configured. --- ## Rotating SCIM Tokens To rotate a SCIM token without interrupting provisioning, follow this four-step process: 1. **Create a new token** with a descriptive name that indicates it is the replacement: ```bash curl -X POST https://us.vouch.sh/api/v1/org/scim-tokens \ -b ~/.local/state/vouch/cookie.txt \ -H "Content-Type: application/json" \ -d '{"description": "Google Workspace SCIM (rotated 2026-02)", "expires_in_days": 365}' ``` 2. **Update your identity provider** with the new token value. Follow the configuration steps for your IdP described in Step 2 above, replacing the old token with the new one. 3. **Test the new token** by triggering a sync from your identity provider or by querying the SCIM endpoint directly with the new token: ```bash curl -s https://us.vouch.sh/scim/v2/Users \ -H "Authorization: Bearer vouch_scim_NEW_TOKEN_HERE" \ | jq '.totalResults' ``` 4. **Revoke the old token** once you have confirmed that the new token is working: ```bash curl -X DELETE us.vouch.sh/api/v1/org/scim-tokens/scim_tok_OLD_ID \ -b ~/.local/state/vouch/cookie.txt ``` By creating the new token before revoking the old one, you ensure there is no window during which provisioning is interrupted. --- ## Troubleshooting ### 401 Unauthorized The SCIM token is invalid, expired, or revoked. - Verify the token has not expired by listing your active tokens. - Ensure the `Authorization` header uses the format `Bearer <token>` with no extra whitespace or characters. - If the token was recently created, confirm you copied the full plaintext value -- it is only shown once during creation. - If the token has expired or been revoked, create a new token and update your identity provider configuration. ### 409 Conflict A user or group with the same unique identifier already exists. - This typically occurs when your identity provider attempts to create a user who has already been provisioned, either through a previous SCIM sync or through manual registration. - Check whether the user already exists in Vouch by querying the Users endpoint with a filter: ```bash curl -s "https://us.vouch.sh/scim/v2/Users?filter=userName%20eq%20%22alice%40example.com%22" \ -H "Authorization: Bearer vouch_scim_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \ | jq . ``` - If the user exists, your IdP should use `PUT` or `PATCH` to update the existing record rather than `POST` to create a new one. Most identity providers handle this automatically after the initial conflict. ### Users Not Syncing If users are not appearing in Vouch after configuring SCIM: - **Verify the SCIM Base URL.** Ensure it is set to `https://us.vouch.sh/scim/v2` with no trailing slash. - **Test connectivity.** Use the `ServiceProviderConfig` endpoint to confirm the IdP can reach Vouch: ```bash curl -s https://us.vouch.sh/scim/v2/ServiceProviderConfig \ -H "Authorization: Bearer vouch_scim_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \ | jq . ``` - **Check your IdP's provisioning logs.** Most identity providers maintain a log of SCIM operations and any errors encountered. Look for HTTP error codes or timeout messages. - **Confirm provisioning is enabled.** In some IdPs (especially Okta and Azure AD), you must explicitly enable provisioning actions (Create, Update, Deactivate) after configuring the API connection. - **Verify user assignment.** In many IdPs, users must be explicitly assigned to the Vouch application before they will be provisioned. Check that the intended users or groups are assigned. ### Attribute Mapping Issues If user attributes (name, email, department) are not appearing correctly in Vouch: - Review the attribute mappings in your identity provider's SCIM configuration. - Vouch expects the standard SCIM 2.0 `User` schema attributes: - `userName` -- The user's email address (used as the unique identifier). - `name.givenName` -- The user's first name. - `name.familyName` -- The user's last name. - `emails` -- An array of email objects; the `primary` email is used for notifications. - `active` -- A boolean indicating whether the account is active. - Ensure your IdP is mapping its directory attributes to these standard SCIM fields. --- # Security Model Source: https://vouch.sh/docs/security/ Vouch brokers the most sensitive credentials in a developer's stack: SSH certificates, AWS STS tokens, GitHub installation tokens, and container registry passwords. This page explains exactly how those credentials are protected at every layer. ## Executive summary - Vouch does not store AWS credentials, SSH private keys, GitHub tokens, or registry passwords. It brokers short-lived credentials after hardware-backed authentication. - User authentication is based on FIDO2/WebAuthn assertions from enrolled YubiKeys, requiring both the key and user verification. - Credentials are scoped to one authenticated user, held in memory by the local agent, and expire automatically. - Server-side signing keys are managed by AWS KMS, and authenticated requests use modern OAuth and FAPI protections including DPoP, PAR, private-key client authentication, and HTTP Message Signatures. - Security reviewers should also read the [Threat Model](/docs/threat-model/), [Architecture](/docs/architecture/), [Availability](/docs/availability/), and [Migration](/docs/migration/) guides. --- ## Data flow Every Vouch credential follows the same path from hardware key to cloud service: ``` YubiKey (FIDO2) → Vouch CLI (local machine) → Vouch Server (validates assertion, evaluates device posture, issues session) → External service (AWS STS / GitHub / SSH CA) → Short-lived credential returned to CLI → Tool uses credential (aws, ssh, git, docker) ``` 1. **FIDO2 assertion** -- The YubiKey signs a challenge using a private key that never leaves the hardware. The assertion includes origin binding (preventing phishing) and user verification (PIN + touch). 2. **Device posture evaluation** -- If active [posture policies](/docs/device-posture/) exist, the server evaluates the device's security state (disk encryption, firewall, EDR, screen lock, etc.) against configured policies. If any policy fails, access is denied with OS-specific remediation guidance. 3. **Session issuance** -- The Vouch server validates the signed assertion against the enrolled public key and issues a session token valid for 8 hours. 4. **Credential exchange** -- When a tool needs a credential, the CLI exchanges the session token for a service-specific credential (STS `AssumeRoleWithWebIdentity`, SSH certificate signing, GitHub App installation token, etc.). All requests include [HTTP Message Signatures (RFC 9421)](https://datatracker.ietf.org/doc/html/rfc9421) for request-level integrity. 5. **Tool consumption** -- The tool receives the short-lived credential and uses it normally. The credential expires on its own -- there is nothing to revoke or rotate. --- ## Credential lifecycle All Vouch credentials share these properties: | Property | Detail | |---|---| | **Storage** | In-memory only, held by the Vouch agent process. Never written to disk. | | **Lifetime** | Session: 8 hours. AWS STS: up to 1 hour. SSH certificate: 8 hours. GitHub token: 1 hour. | | **Scope** | Tied to a single authenticated user. Cannot be shared or transferred. | | **Revocation** | Sessions can be revoked server-side (e.g., via SCIM de-provisioning). Outstanding short-lived credentials expire naturally. | | **Rotation** | Not applicable -- credentials are issued fresh on each request and expire automatically. | Because credentials are never written to disk, they cannot be exfiltrated by malware scanning `~/.aws/credentials`, `~/.ssh/`, or environment variables. --- ## Trust boundaries Vouch operates across three trust boundaries: ### 1. Hardware key (YubiKey) - Private key material is generated on the YubiKey and **never exported**. - FIDO2 assertions are origin-bound -- the key will not sign challenges from phishing domains. - User verification requires both a PIN and a physical touch. ### 2. Local machine (CLI + Agent) - The Vouch agent runs as a user-level process and holds session material in memory. - Communication between the CLI and agent uses a Unix domain socket with filesystem permissions restricting access to the owning user. Every incoming connection is verified using OS-level peer credentials (`SO_PEERCRED` on Linux, `getpeereid` on macOS) to confirm the connecting process runs as the same UID as the agent. Connections from a different UID are rejected and audit-logged. - On startup, the agent validates that its socket directory (`$XDG_RUNTIME_DIR/vouch/`, or `~/.cache/vouch/` where `XDG_RUNTIME_DIR` is unset) is not a symlink and is owned by the current user, preventing symlink-based directory hijacking attacks. - No credentials are persisted to disk. If the agent process stops, sessions must be re-established with a new `vouch login`. ### 3. Vouch server - The server validates FIDO2 assertions (with identity federation through OIDC or [SAML 2.0](/docs/saml/) identity providers) and issues signed OIDC tokens (ES256 over P-256). - Signing keys (OIDC ES256 and SSH CA Ed25519) are managed by AWS KMS — the server delegates signing operations and never holds private key material. - The server does not store AWS credentials, SSH private keys, or GitHub tokens. It acts as an identity broker, not a secrets vault. - Communication between CLI and server uses TLS 1.3. Authenticated requests include HTTP Message Signatures ([RFC 9421](https://datatracker.ietf.org/doc/html/rfc9421)) for request-level integrity verification. --- ## Threat model For the complete STRIDE-based threat analysis — including threat actors, trust boundaries, assumptions, structured threat statements, and mitigations — see the dedicated [Threat Model](/docs/threat-model/) page. --- ## Encryption ### In transit All communication between the Vouch CLI and server uses **TLS 1.3**. The FIDO2 assertion is transmitted over this encrypted channel. ### At rest Vouch does not store credentials at rest. The server stores: - **Enrolled public keys** -- The FIDO2 public key registered during enrollment. This is not sensitive (it cannot be used to impersonate the user). - **User metadata** -- Email address, organization membership, and enrollment status. - **Audit logs** -- Records of authentication events and credential issuance. Organization administrators can view and filter audit events from the admin dashboard. ![Audit Log page showing authentication events with type filters](/images/admin/admin-audit-log.png) No AWS credentials, SSH keys, or GitHub tokens are stored on the server. User data and metadata are protected with **document-level encryption** using HPKE ([RFC 9180](https://datatracker.ietf.org/doc/html/rfc9180)) with DHKEM(P-384), HKDF-SHA384, and AES-256-GCM. Each document is encrypted individually with its own encapsulated key — the encryption is bound to the document type and ID, preventing ciphertext relocation. The document encryption key pair is generated via AWS KMS (`GenerateDataKeyPairWithoutPlaintext`), and the private key is only decrypted at server startup using a KMS key with NitroTPM attestation (when available), ensuring the plaintext private key is only recoverable on attested EC2 instances. Blind equality indexes (for lookups by email, etc.) use HMAC-SHA256 with a KMS-managed key, so the database never contains plaintext identifiers. --- ## FIDO2 security properties Vouch uses [FIDO2/WebAuthn](https://fidoalliance.org/fido2/) for all user authentication. Key security properties: - **Origin binding** -- The authenticator (YubiKey) includes the relying party ID in the signed assertion. If an attacker stands up a phishing site at a different domain, the assertion will not validate against the Vouch server. - **Hardware key storage** -- The private key is generated on the YubiKey's secure element and cannot be extracted, cloned, or backed up. - **User verification** -- Every assertion requires the user's PIN and a physical touch of the key, providing two-factor authentication in a single gesture. - **Replay protection** -- Each assertion includes a signature counter that the server tracks. Replayed assertions are rejected. - **Attestation certificate chain validation** -- When `VOUCH_REQUIRE_ATTESTATION_CERT=true` is set, the server validates the authenticator's attestation certificate chain against pinned [Yubico root CA certificates](https://developers.yubico.com/PKI/). This cryptographically proves the key is a genuine Yubico device, not a software emulator or unknown authenticator. The server also extracts the FIDO AAGUID from the attestation certificate to identify the exact key model. --- ## OAuth 2.0 security architecture FIDO2 proves the human is present. The OAuth 2.0 layer protects everything after — how the CLI identifies itself, how authorization requests are transmitted, and how tokens are bound to the device that requested them. Together, these form a [FAPI 2.0 Security Profile](https://openid.net/specs/fapi-security-profile-2_0-final.html). **No shared secrets.** The CLI generates its own key pair and registers with the server automatically ([RFC 7591](https://datatracker.ietf.org/doc/html/rfc7591)). Client authentication uses `private_key_jwt` ([RFC 7523](https://datatracker.ietf.org/doc/html/rfc7523)) — there is no client secret to extract from a binary or config file. **Protected authorization requests.** Authorization parameters are sent directly to the server over a back-channel ([RFC 9126](https://datatracker.ietf.org/doc/html/rfc9126)) and signed as JWTs ([RFC 9101](https://datatracker.ietf.org/doc/html/rfc9101)). The browser redirect carries only an opaque reference — nothing sensitive in URLs, browser history, or referrer headers. **Sender-constrained tokens.** Every access token is bound to the CLI's key pair via DPoP ([RFC 9449](https://datatracker.ietf.org/doc/html/rfc9449)). A stolen token cannot be used from a different machine. For service-to-service scenarios, Mutual TLS ([RFC 8705](https://datatracker.ietf.org/doc/html/rfc8705)) provides an alternative — the token's `cnf` claim contains the SHA-256 thumbprint of the client's TLS certificate, and resource servers validate that the certificate presented at the TLS layer matches. **Audience-restricted tokens.** Each token includes a resource indicator ([RFC 8707](https://datatracker.ietf.org/doc/html/rfc8707)) restricting it to a specific service. A token issued for AWS cannot be presented to GitHub. **Request-level integrity.** Every authenticated request includes an HTTP Message Signature ([RFC 9421](https://datatracker.ietf.org/doc/html/rfc9421)). The CLI signs each request using the FAPI key pair stored in the OS keychain. The server verifies the signature before processing, providing cryptographic proof that the request was not tampered with and originated from the registered client. **Fine-grained authorization requests.** Applications can request structured permissions using Rich Authorization Requests ([RFC 9396](https://datatracker.ietf.org/doc/html/rfc9396)). Instead of flat scope strings, `authorization_details` objects describe the type, actions, and resources being requested — enabling precise, machine-readable authorization that goes beyond what scopes can express. --- ## Why request forgery is infeasible The sections above describe individual security layers. Here is how they combine to make forging a CLI authentication request infeasible without physical possession of the user's enrolled YubiKey and knowledge of its PIN. A successful login requires producing **all** of the following, and each is independently verified: 1. **FIDO2 assertion** -- A COSE signature that can only be produced by the YubiKey's private key, which never leaves the hardware secure element. The server verifies this signature against the public key registered during enrollment. Both the user presence (physical touch) and user verification (PIN) flags are checked server-side. 2. **Single-use challenge** -- The server generates 32 random bytes embedded in a signed state JWT with a 5-minute expiry. The challenge is atomically consumed on first use and bound into the `client_data_json` that the YubiKey signs — an old assertion cannot be paired with a new challenge. 3. **DPoP proof** -- The client proves possession of the same ES256 private key used during registration. Each proof carries a unique `jti` tracked in the database to prevent replay. The server can require a nonce for additional resistance to precomputation. 4. **Client assertion** -- OAuth client authentication uses a `private_key_jwt` (RFC 7523) signed with the device's ES256 key, with a 60-second lifetime and unique `jti`. 5. **Counter validation** -- The YubiKey's monotonic signature counter must strictly increase on each assertion. A cloned authenticator would have a stale counter, which the server detects and rejects. 6. **HTTP Message Signature** -- Every authenticated request is signed using the client's FAPI key pair ([RFC 9421](https://datatracker.ietf.org/doc/html/rfc9421)). The server verifies the signature covers the request method, path, and body, preventing request tampering even if TLS termination occurs at an intermediary. ### Attack scenarios | Attack | Why it fails | |---|---| | **Replay a captured login** | Challenge is single-use (atomic DB check); DPoP `jti` is single-use | | **Forge a FIDO2 assertion** | Requires the YubiKey's private key, which never leaves the hardware | | **Steal an access token from the network** | Token is DPoP-bound or certificate-bound — unusable without the device's private key or matching TLS certificate | | **Man-in-the-middle the challenge** | Challenge is signed in a state JWT with a server-only key; tampering is detected | | **Tamper with a request in transit** | HTTP Message Signature verification fails — the signature covers the request method, path, and body | | **Reuse an old assertion with a new challenge** | Challenge is embedded in `client_data_json`, which is signed by the YubiKey; mismatch is detected | | **Clone the YubiKey** | Counter validation detects cloned authenticators | | **Brute-force the PIN remotely** | PIN is verified locally by YubiKey hardware, which locks after 8 failed attempts | | **Present a certificate-bound token without the matching certificate** | The `x5t#S256` thumbprint in the token's `cnf` claim is validated against the client certificate presented in the TLS handshake; mismatch is rejected | | **Authorization server mix-up** | The `iss` parameter in authorization responses ([RFC 9207](https://datatracker.ietf.org/doc/html/rfc9207)) lets clients verify they are communicating with the expected authorization server | | **Access the agent socket from another process** | Socket permissions (0600) restrict access; the agent verifies the connecting process has the same UID via OS peer credentials | --- ## Supply chain security ### SLSA provenance Vouch release binaries are built with [SLSA Level 3](https://slsa.dev/) provenance. Each release includes a provenance attestation that you can verify: ```bash slsa-verifier verify-artifact vouch-linux-amd64 \ --provenance-path vouch-linux-amd64.intoto.jsonl \ --source-uri github.com/vouch-sh/vouch ``` This confirms the binary was built from the expected source repository using a tamper-resistant build process. ### SHA256 checksums Every release includes a `checksums.txt` file. Verify downloaded binaries: ```bash sha256sum --check checksums.txt ``` ### Package manager verification When installed via Homebrew, APT, or DNF, package signatures are verified automatically by the package manager using Vouch's published GPG key. --- ## Shared responsibility ### Vouch's responsibilities - Secure the server infrastructure and FIDO2 registration data. - Issue credentials with minimum necessary lifetime and scope. - Provide SLSA-attested builds and signed packages. - Revoke sessions when triggered by SCIM de-provisioning. - Maintain audit logs of all authentication and credential issuance events. ### Your responsibilities - Protect YubiKeys and PINs. Report lost or stolen keys immediately. - Configure IAM roles with least-privilege permissions. - Set up SCIM to automate user lifecycle management. - Monitor CloudTrail and server audit logs for anomalous activity. - Keep the Vouch CLI updated to receive security patches. --- ## Compliance Vouch's FAPI 2.0 security profile and hardware-backed authentication satisfy requirements across multiple compliance frameworks: - **NIST 800-53** — IA-2 (identification/authentication), IA-5 (authenticator management), SC-23 (session authenticity) - **SOC 2** — CC6.1 (logical access), CC6.8 (unauthorized access prevention), CC7.1 (detection) - **FedRAMP** — Hardware MFA, DPoP and mTLS sender-constrained tokens, non-extractable keys - **HIPAA** — 164.312(d) (person authentication), 164.312(e) (transmission security) Detailed control-by-control mappings are available in the [Vouch server documentation](https://docs.vouch.sh/reference/compliance.html). --- ## Incident response If you suspect a security issue with the Vouch service or have discovered a vulnerability: - Email **security@vouch.sh** with details. - Include reproduction steps if possible. - Do not disclose the issue publicly until it has been addressed. If a YubiKey is lost or stolen, remove it from the user's account immediately to prevent unauthorized authentication. --- # Access AWS CodeCommit without Git Credentials Source: https://vouch.sh/docs/codecommit/ Vouch authenticates to [AWS CodeCommit](https://docs.aws.amazon.com/codecommit/latest/userguide/welcome.html) using short-lived STS credentials -- no SSH keys, no HTTPS Git credentials, and no IAM access keys to manage. Both HTTPS credential helper and native `codecommit://` remote helper are supported. {{< tldr >}} - **Prerequisites:** [Getting Started](/docs/getting-started/) → [AWS integration](/docs/aws/) → this page. - **Admin, once:** add `codecommit:GitPull` and `codecommit:GitPush` to the Vouch IAM role. - **Each developer:** `vouch setup codecommit --configure`, then `git clone https://git-codecommit.<region>.amazonaws.com/v1/repos/<repo>` just works. {{< /tldr >}} ## Prerequisites {{< role admin >}} Before developers can configure the AWS CodeCommit integration: - The **[AWS integration](/docs/aws/)** must be configured (OIDC provider and IAM role) - The IAM role must have `codecommit:GitPull` and `codecommit:GitPush` permissions on the target repositories --- ## Step 1 -- Configure the Git credential helper {{< role developer >}} Run the setup command to install the Vouch credential helper for AWS CodeCommit: ``` vouch setup codecommit [--region <REGION>] [--profile <PROFILE>] [--configure] ``` | Flag | Description | |---|---| | `--region` | AWS region (default: wildcard matching all regions) | | `--profile` | AWS profile to use (defaults to auto-detected vouch profile) | | `--configure` | Apply the configuration automatically (without this flag, the command only prints the configuration) | This configures Git to use Vouch as the credential helper for AWS CodeCommit HTTPS URLs across all supported AWS partitions. It adds the following to your `~/.gitconfig`: ```ini [credential "https://git-codecommit.*.amazonaws.com"] helper = !'/usr/local/bin/vouch' credential codecommit --profile vouch useHttpPath = true [credential "https://git-codecommit.*.amazonaws.com.cn"] helper = !'/usr/local/bin/vouch' credential codecommit --profile vouch useHttpPath = true [credential "https://git-codecommit.*.amazonaws.eu"] helper = !'/usr/local/bin/vouch' credential codecommit --profile vouch useHttpPath = true ``` The helper value uses the absolute path to your `vouch` binary and bakes in the AWS profile name, so your entries will differ slightly from the example above. The setup also installs the native `git-remote-codecommit` helper as a symlink at `~/.local/bin/git-remote-codecommit`, enabling `codecommit://` URL support (see below). --- ## Step 2 -- Use Git normally {{< role developer >}} {{< session-note >}} With the credential helper configured and an active session, Git commands work without any extra flags or tokens: ```bash # Clone an AWS CodeCommit repository git clone https://git-codecommit.us-east-1.amazonaws.com/v1/repos/my-repo # Pull latest changes cd my-repo git pull # Push commits git add . git commit -m "Update feature" git push ``` Vouch handles authentication transparently. You do not need to enter a username, password, or configure IAM HTTPS Git credentials. --- ## Native `codecommit://` remote helper Vouch ships its own native `git-remote-codecommit` remote helper, which is installed automatically by `vouch setup codecommit`. This provides `codecommit://` URL support without requiring any external dependencies -- no Python or `pip install` needed. The remote helper uses the same Vouch session → STS flow as the credential helper, but bypasses Git's credential helper system entirely. This avoids known conflicts with macOS Keychain and Git Credential Manager. ### URL formats | Format | Description | |---|---| | `codecommit://my-repo` | Uses the default AWS profile and region | | `codecommit://vouch@my-repo` | Uses the `vouch` AWS profile | | `codecommit::us-west-2://my-repo` | Uses a specific region | | `codecommit::us-west-2://vouch@my-repo` | Uses a specific region and profile | ### Usage ```bash # Clone using the default profile git clone codecommit://my-repo # Clone using a specific AWS profile git clone codecommit://vouch@my-repo # Clone from a specific region git clone codecommit::us-west-2://vouch@my-repo ``` The profile name before `@` must match your AWS profile (typically `vouch`). All subsequent Git operations (`push`, `pull`, `fetch`) work normally. ### When to use `codecommit://` URLs Use `codecommit://` URLs when: - You have multiple credential helpers installed and experience conflicts (macOS Keychain, Git Credential Manager) - You want to avoid HTTPS URL region-specific hostnames - You need to work with multiple AWS profiles or regions across repositories --- ## Cross-partition support All AWS partitions -- standard (`aws`), GovCloud (`aws-us-gov`, covered by the `amazonaws.com` wildcard), China (`aws-cn`), and European Sovereign Cloud (`aws-eusc`) -- are configured automatically during setup, as the `~/.gitconfig` entries in Step 1 show. --- ## Troubleshooting ### Authentication failed ``` fatal: Authentication failed for 'https://git-codecommit.us-east-1.amazonaws.com/v1/repos/my-repo' ``` - Verify you have an active Vouch session: `vouch login`. - Confirm the credential helper is configured: `git config --global credential.https://git-codecommit.*.amazonaws.com.helper` should return a value containing `vouch' credential codecommit`. - Check that the Vouch agent is running: `vouch status`. ### "Not authorized to perform codecommit:GitPull" - Verify the IAM role you are assuming has the correct AWS CodeCommit permissions. - Check that the IAM trust policy allows `AssumeRoleWithWebIdentity` from the Vouch OIDC provider. ### Wrong region - AWS CodeCommit repository URLs include the region (e.g., `git-codecommit.us-east-1.amazonaws.com`). Make sure you are using the correct region for your repository. - Alternatively, use `codecommit://` URLs with an explicit region: `codecommit::us-east-1://vouch@my-repo`. ### Another credential helper is interfering 1. List all configured credential helpers: ``` git config --show-origin --get-all credential.https://git-codecommit.*.amazonaws.com.helper ``` 2. Remove or reorder conflicting entries so that `vouch` appears first. 3. Re-run `vouch setup codecommit` to ensure the configuration is correct. 4. Alternatively, switch to `codecommit://` URLs which bypass Git's credential helper system entirely. --- ## How it works 1. **Git requests credentials** -- Git calls the Vouch credential helper when it needs to authenticate to an AWS CodeCommit repository. 2. **OIDC to STS** -- Vouch exchanges your active hardware-backed session for temporary AWS STS credentials via `AssumeRoleWithWebIdentity`. 3. **SigV4 signing** -- Vouch uses the STS credentials to sign the Git HTTP request with AWS Signature Version 4, authenticating directly to AWS CodeCommit -- bypassing the legacy HTTPS Git credential system, with no stored secrets on disk. 4. **Git authenticates** -- The signed credentials are returned to Git and used for the current operation. --- ## Authentication method comparison | Method | Credential Type | Conflicts | Works with Vouch | |---|---|---|---| | SSH keys | Static key pair | No | Not through Vouch | | HTTPS Git credentials | Static username/password | macOS Keychain, GCM | Not through Vouch | | HTTPS credential helper (Vouch) | SigV4-signed (from STS) | macOS Keychain, GCM | Yes | | `codecommit://` remote helper (Vouch) | SigV4-signed (from STS) | None | Yes (recommended) | --- # Authenticate to Private Cargo Registries Source: https://vouch.sh/docs/cargo/ Vouch replaces the plaintext token in `~/.cargo/credentials.toml` with tokens derived from your hardware-backed session -- short-lived, never written to disk, and revoked when your session ends. Vouch implements the [Cargo credential provider protocol](https://doc.rust-lang.org/cargo/reference/credential-provider-protocol.html), so it works with any registry that supports Bearer token authentication. {{< tldr >}} - **Prerequisites:** [Getting Started](/docs/getting-started/) → this page. - **Admin, once:** confirm your private registry meets the [registry server requirements](#registry-server-requirements) (Bearer token authentication). - **Each developer:** `vouch setup cargo --configure`, then `cargo build` and `cargo publish` just work. {{< /tldr >}} ## Prerequisites Before configuring the Cargo integration, make sure you have: - **Cargo** installed via [rustup](https://rustup.rs/) (version **1.74** or later, which includes credential provider protocol support) - A **private Cargo registry** that supports Bearer token authentication --- ## Step 1 -- Configure Cargo Credential Provider {{< role developer >}} Run the setup command to install the Vouch credential provider for Cargo: ``` vouch setup cargo ``` This prints the Cargo configuration that will be added. To apply it automatically: ``` vouch setup cargo --configure ``` To configure a specific named registry: ``` vouch setup cargo --registry my-private-registry --configure ``` The command adds the following to your `~/.cargo/config.toml`: ```toml [registry] global-credential-providers = ["vouch"] [registries.my-private-registry] index = "sparse+https://cargo.example.com/index/" credential-provider = ["vouch"] ``` The `global-credential-providers` setting registers Vouch as the default credential provider for all registries. You can also configure it per-registry using the `credential-provider` key under a specific `[registries.*]` section. --- ## Step 2 -- Use Cargo normally {{< role developer >}} {{< session-note >}} With the credential provider configured and an active session, Cargo commands work without any extra flags or manual token management: ```bash # Build a project that depends on private crates cargo build # Publish a crate to your private registry cargo publish --registry my-private-registry # Add a dependency from a private registry cargo add my-crate --registry my-private-registry # Update dependencies including private ones cargo update ``` Vouch handles authentication transparently. You do not need to run `cargo login` or set any environment variables. --- ## Private Registry Configuration To use a private Cargo registry with Vouch, you need to define the registry in your Cargo configuration. Add the following to your project's `.cargo/config.toml` or your global `~/.cargo/config.toml`: ```toml [registries.my-private-registry] index = "sparse+https://cargo.example.com/index/" credential-provider = ["vouch"] ``` If your `Cargo.toml` references dependencies from the private registry: ```toml [dependencies] my-crate = { version = "1.0", registry = "my-private-registry" } ``` Cargo will automatically call the Vouch credential provider when it needs to fetch or publish crates from this registry. ### Registry server requirements {{< role admin >}} The private registry must support: - **Sparse index protocol** (recommended) or Git index protocol - **Bearer token authentication** via the `Authorization` HTTP header - The token format issued by Vouch (a signed JWT) Consult your registry server's documentation to confirm Bearer token support. --- ## How it works When Cargo needs to authenticate to a private registry, it delegates to the Vouch credential provider: 1. **Cargo requests a token** -- Cargo calls the Vouch credential provider binary when it needs to authenticate to a configured private registry. 2. **Vouch exchanges your session** -- The credential provider contacts the Vouch server and exchanges your active hardware-backed session for a registry-scoped Bearer token. 3. **Token derived from your session** -- The token (a signed JWT) is scoped to the specific registry, expires when your session ends, and is never written to disk. 4. **Cargo authenticates** -- The Bearer token is sent to the registry as part of the HTTP request, and the operation proceeds. --- ## Troubleshooting ### Not authenticated ``` error: failed to get token for registry `my-private-registry` caused by: not logged in to registry `my-private-registry` ``` - Verify you have an active Vouch session: `vouch login` - Check that the Vouch agent is running: `vouch status` - Confirm the credential provider is configured for the registry: inspect `~/.cargo/config.toml` and verify the `credential-provider` key is set to `["vouch"]`. ### Cargo not using Vouch If Cargo prompts you for a token or uses a different credential provider: 1. Check your Cargo configuration for conflicting credential provider settings: ``` cargo config get registry.global-credential-providers ``` 2. Ensure no `CARGO_REGISTRY_TOKEN` or `CARGO_REGISTRIES_*_TOKEN` environment variables are set, as these override credential providers: ```bash env | grep CARGO_REGISTR ``` 3. Re-run `vouch setup cargo --configure` to ensure the configuration is correct. ### Unsupported protocol version ``` error: credential provider `vouch` failed: unsupported credential provider protocol version ``` The Cargo credential provider protocol requires **Cargo 1.74 or later**. Check your Cargo version: ``` cargo --version ``` If your version is older than 1.74, update via rustup: ``` rustup update stable ``` ### Token not cached If Vouch appears to request a new token for every Cargo operation (causing delays): - This is expected behavior. Vouch derives tokens from your active session on each request rather than caching them to disk. The overhead is minimal (typically under 100ms). - If latency is a concern, ensure the Vouch agent is running (`vouch status`), as it keeps your session in memory for fast token derivation. ### Multiple credential providers If you have multiple credential providers configured and they conflict: 1. Check the provider order in `~/.cargo/config.toml`: ```toml [registry] global-credential-providers = ["vouch", "cargo:token"] ``` Cargo tries providers in order. Place `vouch` first to ensure it is used before any fallback providers. 2. To use Vouch for only specific registries, remove it from `global-credential-providers` and set it per-registry instead: ```toml [registries.my-private-registry] credential-provider = ["vouch"] ``` --- # Device Posture Policies Source: https://vouch.sh/docs/device-posture/ Vouch can collect security signals from developer devices at login and enforce policies against them. If a device does not meet your organization's security baseline, Vouch denies access and tells the developer exactly what to fix. This means you no longer need to trust that developers have configured their machines correctly — Vouch verifies it on every authentication. --- ## How it works 1. **Signal collection.** When a developer runs `vouch login`, the CLI automatically detects the security posture of the local machine — disk encryption, firewall, screen lock, endpoint protection, and more. This takes under 2 seconds and requires no elevated privileges. 2. **Transmission.** The posture data is sent to the Vouch server as structured [RFC 9396](https://datatracker.ietf.org/doc/html/rfc9396) `authorization_details` alongside the FIDO2 assertion. 3. **Policy evaluation.** The server evaluates the posture data against your organization's active policies. All active policies must pass (AND logic). 4. **Result.** If all policies pass, the session is issued normally. If any policy fails, access is denied and the developer receives OS-specific remediation guidance. ``` vouch login → CLI collects device posture signals (< 2 seconds) → FIDO2 assertion + posture data sent to server → Server evaluates posture against active policies → All pass → session issued → Any fail → access denied + remediation guidance ``` ### Fail-closed enforcement Posture policies use **fail-closed** enforcement. If a developer's CLI does not send posture data (e.g., running an older CLI version), active policies deny access. This prevents bypassing policies by downgrading the CLI. --- ## Collected signals The Vouch CLI detects the following signals on macOS, Linux, and Windows. All detection is best-effort and does not require administrator privileges. | Signal | Description | macOS | Linux | Windows | |---|---|---|---|---| | **Disk encryption** | Whether the system disk is encrypted | FileVault | LUKS | BitLocker | | **Firewall** | Whether the OS firewall is enabled | Application Firewall | iptables / nftables | Windows Firewall | | **Screen lock** | Whether an idle screen lock is configured | System Preferences | GNOME / KDE Plasma | Lock screen settings | | **Secure boot** | Whether secure boot is active | Apple Silicon (always) | UEFI Secure Boot | UEFI Secure Boot | | **TPM** | Whether a Trusted Platform Module is present | N/A | TPM 2.0 | TPM 2.0 | | **EDR** | Endpoint detection & response agent installed | CrowdStrike, SentinelOne, Carbon Black, Microsoft Defender | CrowdStrike, SentinelOne, Carbon Black, Microsoft Defender | CrowdStrike, SentinelOne, Carbon Black, Microsoft Defender | | **MDM** | Mobile device management agent installed | Jamf, Kandji, Intune | Intune | Intune | | **OS auto-update** | Whether automatic OS updates are enabled | SoftwareUpdate | unattended-upgrades | Windows Update | | **Access control** | MAC enforcement status | System Integrity Protection (SIP) / Gatekeeper | SELinux / AppArmor | N/A | | **OS info** | Distribution, version, build, architecture | Detected | Detected | Detected | | **System uptime** | Time since last reboot | Detected | Detected | Detected | | **Execution context** | Elevated privileges, TTY presence, parent process | Detected | Detected | Detected | --- ## Inspecting device posture Use the `vouch posture` command to see what the Vouch CLI detects on your machine, without logging in. ### Text output (default) ```bash vouch posture ``` ``` Device Posture (v1) OS: macOS 15.3.1 (darwin) Architecture: aarch64 Disk encryption: enabled (FileVault) Firewall: enabled (Application Firewall) Screen lock: enabled (300s idle timeout) Secure boot: enabled SIP: enabled EDR: CrowdStrike MDM: Jamf Auto-update: enabled (SoftwareUpdate) Uptime: 3d 4h 22m ``` ### JSON output ```bash vouch posture --format json ``` This outputs the exact `authorization_details` JSON that would be sent to the server during login: ```json [ { "type": "device_posture", "posture_version": 1, "os": "darwin", "os_version": "15.3.1", "os_distribution": "macOS", "arch": "aarch64", "disk_encryption_enabled": true, "disk_encryption_technology": "FileVault", "firewall_enabled": true, "firewall_technology": "Application Firewall", "screen_lock_enabled": true, "screen_lock_idle_timeout_secs": 300, "secure_boot_enabled": true, "sip_enabled": true, "tpm_present": false, "edr": ["CrowdStrike"], "mdm": ["Jamf"], "auto_update_enabled": true, "auto_update_technology": "SoftwareUpdate", "access_control_enforcing": true, "access_control_technology": "SIP", "uptime_secs": 273720, "elevated": false, "tty": true, "parent_process": "zsh", "cli_version": "0.28.0" } ] ``` Use `vouch posture --format json` to debug why a policy might be failing — it shows the exact data the server evaluates. --- ## Pre-configured policies Vouch provides six pre-configured policies that cover common security baselines. Activate them from the admin dashboard — no CEL knowledge required. ### Disk encryption Requires full-disk encryption (FileVault, LUKS, or BitLocker) to be enabled. **Why it matters:** An unencrypted laptop that is lost or stolen exposes every file on disk — cached credentials, source code, configuration files, and session tokens. **Remediation:** - **macOS:** System Settings → Privacy & Security → FileVault → Turn On - **Linux:** Reinstall with LUKS full-disk encryption, or use `cryptsetup` to encrypt partitions - **Windows:** Settings → Privacy & security → Device encryption, or enable BitLocker via Group Policy ### Firewall Requires the OS-level firewall to be active. **Why it matters:** A disabled firewall exposes local services (development servers, databases, debug ports) to the network. **Remediation:** - **macOS:** System Settings → Network → Firewall → Turn On - **Linux:** Enable `ufw` (`sudo ufw enable`) or configure `iptables`/`nftables` - **Windows:** Settings → Privacy & security → Windows Security → Firewall & network protection ### Screen lock Requires an idle screen lock to be configured. **Why it matters:** An unlocked, unattended machine gives anyone physical access to active sessions and credentials. **Remediation:** - **macOS:** System Settings → Lock Screen → set "Require password after screen saver begins" to a short interval - **Linux:** Configure screen lock in your desktop environment settings (GNOME Settings → Privacy → Screen Lock, or KDE System Settings → Screen Locking) - **Windows:** Settings → Accounts → Sign-in options → configure "Require sign-in" ### Endpoint protection Requires at least one endpoint detection and response (EDR) agent to be running. **Why it matters:** EDR agents detect and respond to malware, ransomware, and other threats that could compromise developer credentials or inject into build pipelines. **Detected agents:** CrowdStrike, SentinelOne, Carbon Black, Microsoft Defender for Endpoint. ### Platform integrity Requires platform-specific integrity protections to be active: System Integrity Protection (SIP) and Gatekeeper on macOS, SELinux or AppArmor enforcement on Linux. **Why it matters:** Disabling platform integrity protections makes it easier for malware to persist, modify system binaries, and tamper with security controls. **Remediation:** - **macOS:** Reboot into Recovery Mode and run `csrutil enable` to re-enable SIP. Ensure Gatekeeper is enabled via `spctl --master-enable`. - **Linux:** Set SELinux to enforcing (`sudo setenforce 1` and update `/etc/selinux/config`), or ensure AppArmor profiles are loaded and enforcing. ### OS recency Requires the operating system to have automatic updates enabled. **Why it matters:** Machines without automatic updates miss critical security patches, leaving known vulnerabilities exploitable. **Remediation:** - **macOS:** System Settings → General → Software Update → Automatic Updates → enable all options - **Linux:** Install and enable `unattended-upgrades` (Debian/Ubuntu) or equivalent - **Windows:** Settings → Windows Update → Advanced options → enable automatic updates --- ## Custom policies with CEL For requirements that go beyond the pre-configured policies, you can write custom policies using the [Common Expression Language (CEL)](https://cel.dev/). CEL is a lightweight, non-Turing-complete expression language designed for policy evaluation. ### Available fields Custom CEL expressions can reference any posture field: | Field | Type | Description | |---|---|---| | `os` | `string` | Operating system: `"darwin"`, `"linux"`, `"windows"` | | `os_version` | `string` | OS version number | | `os_distribution` | `string` | OS distribution name | | `arch` | `string` | CPU architecture: `"aarch64"`, `"x86_64"` | | `disk_encryption_enabled` | `bool` | Full-disk encryption active | | `firewall_enabled` | `bool` | OS firewall active | | `screen_lock_enabled` | `bool` | Screen lock configured | | `screen_lock_idle_timeout_secs` | `int` | Screen lock idle timeout in seconds | | `secure_boot_enabled` | `bool` | Secure boot active | | `sip_enabled` | `bool` | System Integrity Protection active (macOS) | | `tpm_present` | `bool` | TPM chip detected | | `tpm_version` | `string` | TPM version (e.g., `"2.0"`) | | `edr` | `list(string)` | EDR agent names detected | | `mdm` | `list(string)` | MDM agent names detected | | `auto_update_enabled` | `bool` | Automatic OS updates enabled | | `access_control_enforcing` | `bool` | MAC enforcement active (SELinux/AppArmor/SIP) | | `uptime_secs` | `int` | System uptime in seconds | | `elevated` | `bool` | Running with elevated privileges | | `tty` | `bool` | Running in a terminal | | `cli_version` | `string` | Vouch CLI version | ### Example expressions **Require CrowdStrike specifically:** ```cel "CrowdStrike" in edr ``` **Require any EDR on macOS, but not on Linux (where agents may not be available):** ```cel os != "darwin" || edr.size() > 0 ``` **Enforce a maximum screen lock timeout of 5 minutes (300 seconds):** ```cel screen_lock_enabled && screen_lock_idle_timeout_secs <= 300 ``` **Require machines to have rebooted within the last 7 days** (to pick up kernel updates): ```cel uptime_secs < 604800 ``` **Require MDM enrollment:** ```cel mdm.size() > 0 ``` **Block logins from elevated (root/admin) shells:** ```cel !elevated ``` **Combine multiple conditions in a single policy:** ```cel disk_encryption_enabled && firewall_enabled && edr.size() > 0 && uptime_secs < 604800 ``` ### Validating expressions The admin dashboard validates CEL syntax in real time and dry-runs the expression against your device's posture data. The field reference table on the policies page lists all available fields and their current values from your device. Always validate custom policies before enabling them — a syntax error in an active policy will deny all logins (fail-closed). ![Policies page with field reference table expanded showing all posture fields](/images/admin/admin-policies-expanded.png) --- ## Managing policies Policies are managed from the **Vouch admin dashboard** by organization administrators. ### Activating a pre-configured policy 1. Open the Vouch admin dashboard and navigate to **Policies**. 2. You will see the six pre-configured policies listed with toggle controls. 3. Toggle a policy to **Active** to begin enforcement. 4. The policy takes effect immediately for all subsequent logins. ![Device Posture Policies page showing built-in policies and custom policy controls](/images/admin/admin-policies.png) ### Creating a custom policy 1. In the Policies page, click **+ New** under Custom Policies. 2. Enter a name and description for the policy. 3. Write a CEL expression in the rule editor. 4. The editor validates your expression in real time and tests it against your device's posture data. 5. Save and activate the policy. ![Custom policy form with a validated CEL expression](/images/admin/admin-policies-custom-new.png) Once saved, custom policies appear alongside the built-in policies with controls to toggle, edit, or delete them: ![Policies page showing a saved custom policy with toggle, edit, and delete controls](/images/admin/admin-policies-custom-saved.png) ### Policy limits - A maximum of **5 policies** can be active at the same time. - All active policies are evaluated using **AND logic** — every active policy must pass for login to succeed. - Policies apply to all members of the organization. There is no per-user or per-group policy targeting. ### Deactivating a policy Toggle a policy to **Inactive** from the Policies page. The policy is retained but no longer evaluated during login. This is useful for temporarily relaxing requirements during an incident or rollout. --- ## Practical examples ### Example 1: Basic security baseline Activate three pre-configured policies to establish a minimum security standard: 1. **Disk encryption** — Protects data at rest on lost or stolen devices. 2. **Firewall** — Prevents unauthorized network access to local services. 3. **Screen lock** — Protects unattended machines. This is a good starting point for most teams. Developers who fail any check see specific remediation instructions for their operating system. ### Example 2: Regulated environment For teams subject to SOC 2, HIPAA, or similar compliance frameworks, activate all six pre-configured policies: 1. Disk encryption 2. Firewall 3. Screen lock 4. Endpoint protection (EDR) 5. Platform integrity 6. OS recency This ensures every developer machine meets a comprehensive security baseline before it can obtain credentials for production infrastructure. ### Example 3: Contractor restrictions If contractors use personal machines that may not have your corporate EDR, create a custom policy that requires MDM enrollment instead: ```cel mdm.size() > 0 ``` This verifies the device is managed by your organization without requiring a specific EDR product. ### Example 4: Enforce recent reboots for kernel updates After a critical kernel vulnerability (like a zero-day), temporarily add a custom policy requiring a recent reboot: ```cel uptime_secs < 259200 ``` This requires all machines to have rebooted within the last 3 days (259,200 seconds), ensuring kernel patches are loaded. Deactivate the policy once the patch cycle is complete. ### Example 5: Platform-specific requirements Create a custom policy that applies different rules per operating system: ```cel (os == "darwin" && sip_enabled && disk_encryption_enabled) || (os == "linux" && access_control_enforcing && disk_encryption_enabled) || (os == "windows" && secure_boot_enabled && tpm_present && disk_encryption_enabled) ``` This enforces platform-appropriate integrity checks: SIP on macOS, SELinux/AppArmor on Linux, and Secure Boot + TPM on Windows — plus disk encryption on all platforms. --- ## What developers see When a posture policy fails, the Vouch CLI displays a clear error message with OS-specific remediation instructions: ``` $ vouch login 🔑 Touch your YubiKey... ✓ Identity verified ✗ Device posture check failed Policy: Disk encryption required Status: disk encryption is not enabled To fix this on macOS: System Settings → Privacy & Security → FileVault → Turn On Contact your administrator if you believe this is an error. ``` The error message includes: - Which policy failed - The current device state - Step-by-step remediation instructions specific to the developer's operating system Developers can use `vouch posture` to inspect their device's posture at any time without attempting a login. --- ## FAQ ### Does posture collection slow down login? No. Posture collection runs in parallel with the FIDO2 assertion and has a 2-second timeout. If collection takes longer than 2 seconds, login proceeds without posture data — but if active policies exist, this triggers fail-closed enforcement and access is denied. ### Can developers bypass posture checks? No. Posture data is evaluated server-side. The CLI cannot skip collection (the server enforces fail-closed), and the posture data cannot be spoofed because it is sent alongside the FIDO2 hardware assertion over TLS. ### Do I need to update the CLI? Yes. Developers must be running a version of the Vouch CLI that supports posture collection (v2026.3.11 or later). Older CLI versions do not send posture data, and active policies will deny access due to fail-closed enforcement. ### What if a developer runs Linux without a desktop environment? Signal detection is best-effort. On headless Linux machines, screen lock detection returns false (no desktop environment to lock). If you activate the screen lock policy, consider creating a custom policy that exempts headless environments: ```cel screen_lock_enabled || !tty ``` ### Can I test policies before enforcing them? Yes. The admin dashboard's **Validate** feature lets you check CEL syntax and dry-run expressions against sample posture data. You can also ask developers to run `vouch posture --format json` and share the output to verify their machines would pass before you activate a policy. --- # Threat Model Source: https://vouch.sh/docs/threat-model/ This threat model documents the threats Vouch is designed to address, the assumptions the design relies on, and the mitigations in place. It follows the [STRIDE](https://en.wikipedia.org/wiki/STRIDE_(security)) framework and is structured after the [AWS Threat Composer](https://awslabs.github.io/threat-composer/) methodology. For background on Vouch's security controls, see the [Security Model](/docs/security/) page. For system design details, see the [Architecture Overview](/docs/architecture/) page. --- ## System description Vouch is a credential broker that replaces long-lived developer secrets (AWS access keys, SSH private keys, GitHub PATs) with short-lived, hardware-backed credentials. The system has three components: - **Vouch CLI + Agent** — runs on the developer's machine, holds session state in memory, and serves credentials to tools via standard protocols (credential helper, SSH agent). - **Vouch Server** — validates FIDO2 assertions, issues OIDC tokens, signs SSH certificates, and brokers credentials from external services. - **External services** — AWS STS, GitHub Apps, SSH hosts, container registries, and other services that consume Vouch-issued credentials. --- ## Dataflow ``` ┌──────────┐ FIDO2 assertion ┌──────────────┐ kms:Sign (ES256) ┌─────────┐ │ YubiKey │─-─────────────────►│ Vouch Server │──────────────────-►│ AWS KMS │ └──────────┘ │ │◄──────────────────-┤ │ │ │ JWT signature └─────────┘ ┌──────────┐ session token │ │ │Vouch CLI │◄────────────────-──┤ │ kms:Sign (Ed25519) │ + Agent │ (DPoP-bound) │ │──────────────────-►┌─────────┐ │ │ │ │◄──────────────────-┤ AWS KMS │ │ │ OIDC ID token │ │ SSH cert signature└─────────┘ │ │──────────────────-►│ │ │ │ STS credentials │ │ GitHub App key │ │◄── ── ── ── ── ──--┤ │──────────────────-►┌─────────┐ │ │ (via AWS STS) │ │◄─────────────────-─┤ GitHub │ │ │ │ │ installation token│ API │ │ │ SSH cert request │ │ └─────────┘ │ │──────────────────-►│ │ │ │ signed SSH cert │ │ │ │◄──────────────────-┤ │ └──────────┘ └──────────────┘ │ │ short-lived credential ▼ ┌──────────────────┐ │ Tool (aws, ssh, │ │ git, docker) │ └──────────────────┘ ``` Data flows: 1. **Login** — YubiKey signs FIDO2 assertion → CLI sends to server over TLS 1.3 → server validates against enrolled public key → server evaluates [device posture policies](/docs/device-posture/) (if active) → returns DPoP-bound session token to agent (held in memory). 2. **AWS credential** — CLI presents session token + DPoP proof → server issues OIDC ID token (signed via KMS ES256) → CLI calls AWS STS `AssumeRoleWithWebIdentity` → STS returns temporary credentials. 3. **SSH certificate** — CLI sends signing request with session token → server delegates to KMS Ed25519 CA → returns signed SSH certificate → agent serves via SSH agent protocol. 4. **GitHub token** — CLI requests token with session token → server exchanges GitHub App credentials for installation access token → returns short-lived token to CLI. All CLI ↔ server traffic uses TLS 1.3 with HTTP Message Signatures ([RFC 9421](https://datatracker.ietf.org/doc/html/rfc9421)) for request-level integrity. No credentials are written to disk at any point. --- ## Assets | Asset | Location | Sensitivity | Protection | |---|---|---|---| | **FIDO2 public keys** | Server database | Low — cannot impersonate users | Document-level encryption (HPKE) | | **User metadata** (email, org) | Server database | Medium — PII | Document-level encryption (HPKE) + HMAC blind indexes | | **OIDC signing key** (ES256) | AWS KMS | Critical — issuance authority | KMS access policy, non-extractable | | **SSH CA key** (Ed25519) | AWS KMS | Critical — certificate authority | KMS access policy, non-extractable | | **Document encryption key** (P-384) | Encrypted by KMS, decrypted at runtime | Critical — protects data at rest | NitroTPM attestation binds decryption to attested instances | | **HMAC key** | AWS KMS | High — index integrity | KMS HMAC operations, key never leaves KMS | | **Session tokens** | Agent process memory | High — grants credential access | Never written to disk, DPoP-bound | | **Audit logs** | Server database | Medium — forensic evidence | Document-level encryption, exported to external SIEM | | **SCIM tokens** | Server database (hashed) | High — provisioning authority | Stored as hashes, not reversible | --- ## Threat actors | Actor | Description | Capability | |---|---|---| | **External attacker** | An adversary with no prior access to the organization's systems. Operates over the network. | Phishing, credential stuffing, man-in-the-middle attacks, domain spoofing, supply chain attacks on public packages. | | **Malicious insider** | An authenticated employee or contractor who abuses legitimate access. | Valid Vouch session, access to internal systems, knowledge of organizational structure and tooling. | | **Compromised endpoint** | Malware or an attacker with code execution on a developer's workstation. | Can read process memory, intercept IPC, access filesystem, and make network requests as the local user. | | **Compromised server** | An attacker who has gained access to the Vouch server infrastructure. | Can issue sessions, sign tokens, and read enrolled public keys and audit logs. | | **Supply chain attacker** | An adversary who tampers with Vouch binaries, dependencies, or distribution channels. | Can inject malicious code into CLI binaries or modify package repositories. | --- ## Trust boundaries ``` ┌─────────────────────────────────────────────────────────────────────┐ │ Developer Workstation │ │ │ │ ┌───────────┐ Unix socket ┌────────────┐ │ │ │ Vouch CLI │◄─────────────────►│ Vouch Agent│ │ │ └─────┬─────┘ (owner-only) └─────┬──────┘ │ │ │ │ │ │ ┌─────┴─────┐ ┌─────┴─────-─┐ │ │ │ YubiKey │ │ In-memory │ │ │ │ (FIDO2) │ │ credentials │ │ │ └───────────┘ └────────────-┘ │ │ │ └──────────────────────────┬──────────────────────────────────────────┘ │ TLS 1.3 ─────────────┼──────────── Network boundary │ ┌──────────────────────────┴──────────────────────────────────────────┐ │ Vouch Server │ │ │ │ ┌──────────────┐ ┌──────────────┐ ┌────────────────────┐ │ │ │ FIDO2 RP │ │ OIDC Provider│ │ SSH CA (Ed25519) │ │ │ │ (assertion │ │ (ES256 via │ │ (signing via │ │ │ │ validation) │ │ AWS KMS) │ │ AWS KMS) │ │ │ └──────────────┘ └──────────────┘ └────────────────────┘ │ │ │ │ ┌──────────────┐ ┌──────────────┐ │ │ │ User store │ │ Audit log │ │ │ │ (public keys,│ │ (auth events,│ │ │ │ metadata) │ │ issuance) │ │ │ └──────────────┘ └──────────────┘ │ │ │ └──────────────────────────┬──────────────────────────────────────────┘ │ TLS 1.3 ─────────────┼──────────── Service boundary │ ┌──────────────────────────┴──────────────────────────────────────────┐ │ External Services │ │ │ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌────────────────┐ │ │ │ AWS STS │ │ GitHub │ │ SSH Hosts│ │ Container │ │ │ │ │ │ Apps API │ │ │ │ Registries │ │ │ └──────────┘ └──────────┘ └──────────┘ └────────────────┘ │ │ │ └─────────────────────────────────────────────────────────────────────┘ ``` Three trust boundaries separate the system: 1. **Hardware boundary** — The YubiKey's secure element. Private keys are generated on-device and cannot be extracted. 2. **Workstation boundary** — The developer's machine. The agent process, Unix socket, and in-memory credentials are protected by OS-level user isolation. 3. **Network boundary** — All communication between CLI and server, and between server and external services, uses TLS 1.3. --- ## Assumptions These assumptions underpin the threat model. If an assumption is violated, the corresponding threats may not be adequately mitigated. | ID | Assumption | Linked threats | Linked mitigations | |---|---|---|---| | **A1** | The YubiKey secure element correctly implements FIDO2 and does not leak private key material. | T-S1, T-S2 | FIDO2 origin binding, FIDO2 user verification (PIN + touch) | | **A2** | The operating system enforces Unix socket file permissions and peer credential APIs (`SO_PEERCRED` / `getpeereid`), preventing other users from accessing the agent socket. | T-I1, T-E1 | Unix socket permissions, peer credential verification (T-E1 mitigation) | | **A3** | TLS 1.3 is not broken — an attacker cannot decrypt or tamper with data in transit. | T-T1, T-I2 | TLS 1.3 transport encryption | | **A4** | AWS STS, GitHub, and other external services correctly validate OIDC tokens and enforce their own access controls. | T-E2 | OIDC audience restriction | | **A5** | The developer's workstation has not been fully compromised at the kernel level (no rootkit). User-space isolation is intact. | T-I1, T-E1 | In-memory only credentials, Unix socket permissions | | **A6** | SCIM de-provisioning events are delivered promptly by the identity provider. | T-S3 | SCIM de-provisioning | | **A7** | The Vouch server infrastructure is hardened and access-controlled (encrypted at rest, network isolation, audited access). | T-T2, T-E3 | Infrastructure hardening, audit log export | | **A8** | Developers keep their YubiKey PINs secret and report lost or stolen keys promptly. | T-S2 | FIDO2 user verification (PIN + touch) | | **A9** | AWS KMS correctly protects signing key material and enforces access controls. NitroTPM attestation correctly binds decryption to attested instances. | T-T2, T-E3 | KMS-managed signing keys, NitroTPM attestation, document-level encryption | --- ## Threats Threats are organized using the [STRIDE](https://en.wikipedia.org/wiki/STRIDE_(security)) categories. Each threat follows the [AWS Threat Composer grammar](https://github.com/awslabs/threat-composer): **a [threat source] with [prerequisites] can [threat action], leading to [threat impact], negatively impacting [impacted assets]**. ### Spoofing <div class="threat-table"> | ID | Threat | STRIDE | Severity | Priority | |---|---|---|---|---| | **T-S1** | An **external attacker** who controls a lookalike domain can **stand up a phishing site** to capture developer credentials, leading to **unauthorized access** to the developer's accounts, negatively impacting **session tokens**. | Spoofing | High | Low | | **T-S2** | An **external attacker** with physical access to a stolen YubiKey and knowledge of the PIN can **authenticate as the enrolled user**, leading to **unauthorized credential issuance** for the session lifetime, negatively impacting **session tokens**. | Spoofing | High | Medium | | **T-S3** | A **former employee** whose SCIM de-provisioning is delayed can **continue to use an active session**, leading to **unauthorized access** to organizational resources after offboarding, negatively impacting **session tokens**. | Spoofing | Medium | Low | </div> **Mitigations:** - **T-S1**: FIDO2 origin binding prevents the YubiKey from signing assertions for unregistered domains. Even if a developer visits a phishing site, the authenticator will not produce a valid assertion. → [FIDO2 security properties](/docs/security/#fido2-security-properties) - **T-S2**: YubiKey PINs provide a second factor — physical possession alone is insufficient. Keys should be reported lost immediately, and the enrolled credential should be removed from the user's account. Session lifetime (8 hours) limits the window. - **T-S3**: SCIM integration enables automated de-provisioning. Sessions can also be revoked server-side. Outstanding short-lived credentials (≤1 hour) expire naturally. --- ### Tampering <div class="threat-table"> | ID | Threat | STRIDE | Severity | Priority | |---|---|---|---|---| | **T-T1** | An **external attacker** in a network position (e.g., compromised Wi-Fi) can **intercept and modify requests** between the CLI and server, leading to **session hijacking or credential injection**, negatively impacting **session tokens**. | Tampering | High | Low | | **T-T2** | A **compromised server** operator can **modify the OIDC signing keys or SSH CA key**, leading to **issuance of fraudulent credentials** accepted by external services, negatively impacting **OIDC signing key** and **SSH CA key**. | Tampering | Critical | Low | | **T-T3** | A **supply chain attacker** can **tamper with Vouch CLI binaries** during build or distribution, leading to **malicious code execution** on developer workstations, negatively impacting **session tokens**. | Tampering | Critical | Medium | </div> **Mitigations:** - **T-T1**: All CLI-to-server communication uses TLS 1.3. HTTP Message Signatures ([RFC 9421](https://datatracker.ietf.org/doc/html/rfc9421)) provide request-level integrity — the CLI signs every authenticated request using the FAPI key pair, and the server rejects any request with an invalid or missing signature. DPoP ([RFC 9449](https://datatracker.ietf.org/doc/html/rfc9449)) binds tokens to the client's key pair — intercepted tokens cannot be used from a different machine. PAR ([RFC 9126](https://datatracker.ietf.org/doc/html/rfc9126)) transmits authorization parameters server-side, keeping sensitive data out of URLs and browser history. - **T-T2**: Signing keys (OIDC ES256 and SSH CA Ed25519) are managed by AWS KMS and cannot be extracted — server compromise does not expose signing key material. The document encryption private key is only decryptable on NitroTPM-attested EC2 instances, preventing extraction even with full server access. The server does not store external service credentials — it brokers them on demand. Infrastructure controls (network isolation, access auditing) provide defense in depth. → [Shared responsibility](/docs/security/#shared-responsibility) - **T-T3**: Release binaries include [SLSA Level 3](https://slsa.dev/) provenance attestations and SHA256 checksums. Package manager installs (Homebrew, APT, DNF) verify signatures automatically. → [Supply chain security](/docs/security/#supply-chain-security) --- ### Repudiation <div class="threat-table"> | ID | Threat | STRIDE | Severity | Priority | |---|---|---|---|---| | **T-R1** | A **malicious insider** can **deny performing an action** (e.g., accessing a production resource) if audit logs are insufficient, leading to **inability to attribute actions** during incident response, negatively impacting **audit logs**. | Repudiation | Medium | Low | | **T-R2** | A **compromised server** can **tamper with or delete audit logs**, leading to **loss of forensic evidence**, negatively impacting **audit logs**. | Repudiation | High | Medium | </div> **Mitigations:** - **T-R1**: Every credential issuance is tied to a hardware-verified FIDO2 identity. The Vouch server logs all authentication events and credential exchanges. AWS CloudTrail records STS credential usage with the Vouch-issued identity as the principal. - **T-R2**: Audit logs should be exported to an immutable, external log store (e.g., AWS CloudWatch, a SIEM) so that server compromise cannot erase the trail. → [Shared responsibility](/docs/security/#shared-responsibility) --- ### Information disclosure <div class="threat-table"> | ID | Threat | STRIDE | Severity | Priority | |---|---|---|---|---| | **T-I1** | A **compromised endpoint** with code execution as the local user can **read the Vouch agent's process memory**, leading to **theft of the active session token and cached credentials**, negatively impacting **session tokens**. | Information disclosure | High | Medium | | **T-I2** | An **external attacker** who compromises a network intermediary can **observe credential exchange traffic**, leading to **exposure of tokens or credentials**, negatively impacting **session tokens**. | Information disclosure | High | Low | | **T-I3** | An **external attacker** can **enumerate the OIDC discovery endpoint** (`/.well-known/openid-configuration`) to **learn the server's signing keys and supported configuration**, leading to **information useful for targeted attacks**, negatively impacting **OIDC signing key** (public component only). | Information disclosure | Low | Low | </div> **Mitigations:** - **T-I1**: DPoP binds tokens to the CLI's key pair — stolen tokens cannot be used from a different machine. Credentials are never written to disk (no `~/.aws/credentials`, no `~/.ssh/id_*`). Session lifetime is limited to 8 hours, and AWS STS credentials expire within 1 hour. Full endpoint compromise with kernel access is out of scope (see [assumption A5](#assumptions)). - **T-I2**: TLS 1.3 encrypts all traffic in transit. DPoP provides an additional layer — even if a token is somehow intercepted, it cannot be replayed from another client. - **T-I3**: OIDC discovery is public by design (required for AWS OIDC federation). The exposed information (issuer URL, JWKS, supported algorithms) does not enable impersonation. Private keys are never exposed through these endpoints. --- ### Denial of service <div class="threat-table"> | ID | Threat | STRIDE | Severity | Priority | |---|---|---|---|---| | **T-D1** | An **external attacker** can **flood the Vouch server** with authentication requests, leading to **developers being unable to obtain credentials**, negatively impacting **session tokens**. | Denial of service | Medium | Low | | **T-D2** | An **external attacker** can **disrupt network connectivity** between the CLI and the Vouch server, leading to **inability to establish new sessions or obtain fresh credentials**, negatively impacting **session tokens**. | Denial of service | Medium | Low | </div> **Mitigations:** - **T-D1**: The Vouch server implements rate limiting and is deployed behind infrastructure-level DDoS protection. Authentication requires a valid FIDO2 assertion, making automated abuse expensive. - **T-D2**: Cached credentials remain valid for their remaining lifetime (up to 8 hours for sessions, 1 hour for AWS STS). Developers can continue working with existing credentials during an outage. → [Availability and Failure Modes](/docs/availability/) --- ### Elevation of privilege <div class="threat-table"> | ID | Threat | STRIDE | Severity | Priority | |---|---|---|---|---| | **T-E1** | A **compromised endpoint** can **access the Unix domain socket** and **use the active session to request credentials for any role the user is authorized for**, leading to **unauthorized access to cloud resources** within the user's permission set, negatively impacting **session tokens**. | Elevation of privilege | High | Medium | | **T-E2** | A **malicious insider** can **use their valid Vouch session to access resources beyond their intended scope** if IAM roles are overly permissive, leading to **unauthorized access to production systems or sensitive data**, negatively impacting **session tokens**. | Elevation of privilege | High | Medium | | **T-E3** | A **compromised server** can **issue sessions for any enrolled user**, leading to **impersonation of any developer** and access to their authorized resources, negatively impacting **OIDC signing key**, **SSH CA key**, **user metadata**, and **audit logs**. | Elevation of privilege | Critical | Low | </div> **Mitigations:** - **T-E1**: The Unix socket is restricted to the owning user by filesystem permissions. Additionally, the agent verifies peer credentials (`SO_PEERCRED` / `getpeereid`) on every connection to confirm the connecting process has the same UID — rejected connections are audit-logged for forensic visibility. On startup, the agent validates that its socket directory (`$XDG_RUNTIME_DIR/vouch/`, or `~/.cache/vouch/` where `XDG_RUNTIME_DIR` is unset) is not a symlink and is owned by the current user, preventing directory hijacking. DPoP prevents extracted tokens from being used on a different machine. Credential scope is limited to the user's authorized roles — the attacker cannot escalate beyond what the user could already access. This threat is bounded by session lifetime (8 hours) and credential lifetime (≤1 hour). - **T-E2**: IAM roles should follow least-privilege principles. Vouch enables fine-grained role mapping per user via OIDC claims. CloudTrail provides full attribution of which user assumed which role. → [Shared responsibility](/docs/security/#shared-responsibility) - **T-E3**: Signing keys are in AWS KMS (non-extractable). The document encryption private key requires NitroTPM attestation for decryption — an attacker with disk or database access alone cannot decrypt user data. Server infrastructure is hardened with network isolation, encrypted storage, audited access, and minimal attack surface. The server does not store external credentials — it brokers them — so compromise enables credential issuance (while the attacker maintains access) but not extraction of stored secrets. --- ## Mitigation summary | Control | Threats addressed | Layer | |---|---|---| | **FIDO2 origin binding** | T-S1 (phishing) | Hardware | | **FIDO2 user verification (PIN + touch)** | T-S2 (stolen key) | Hardware | | **DPoP sender-constrained tokens** | T-T1, T-I1, T-I2, T-E1 (token theft, replay) | Protocol | | **PAR + signed JWTs** | T-T1 (parameter injection) | Protocol | | **TLS 1.3** | T-T1, T-I2 (network interception) | Transport | | **In-memory only credentials** | T-I1 (disk exfiltration) | Application | | **Short credential lifetimes** | T-S3, T-I1, T-E1 (blast radius) | Application | | **SCIM de-provisioning** | T-S3 (offboarding) | Identity | | **OIDC audience restriction** | T-E2 (cross-service abuse) | Protocol | | **SLSA Level 3 provenance** | T-T3 (supply chain) | Build | | **Audit logging + CloudTrail** | T-R1, T-R2 (repudiation) | Operational | | **Rate limiting + DDoS protection** | T-D1 (server flood) | Infrastructure | | **IPC peer credential verification** | T-E1, T-I1 (cross-user socket access) | Application | | **Directory symlink and ownership validation** | T-E1 (directory hijacking) | Application | | **Credential caching** | T-D2 (outage resilience) | Application | | **KMS-managed signing keys** | T-T2, T-E3 (key extraction) | Cryptographic | | **NitroTPM attestation** | T-T2, T-E3 (runtime key protection) | Infrastructure | | **Document-level encryption (HPKE)** | T-E3, T-R2 (data-at-rest protection) | Application | | **HMAC blind indexes** | T-I3 (database-level identifier protection) | Application | | **HTTP message signatures (RFC 9421)** | T-T1 (request tampering) | Protocol | | **Device posture policies (CEL)** | T-E1, T-E2 (compromised endpoint, insider abuse) | Application | --- ## Out of scope The following threats are explicitly out of scope for this threat model: | Threat | Rationale | |---|---| | **Kernel-level endpoint compromise** | If an attacker has root/kernel access, all user-space isolation (process memory, socket permissions) is bypassed. Endpoint detection and response (EDR) tools are the appropriate mitigation layer. | | **Vulnerabilities in external services** | AWS STS, GitHub APIs, container registries, and SSH implementations have their own security models. Vouch trusts their documented behavior. | | **Cryptographic breaks** | If ECDSA (P-256), Ed25519, or TLS 1.3 are broken, the impact extends far beyond Vouch. | | **Physical coercion** | An attacker who can physically compel a developer to authenticate is outside the scope of a technical threat model. | | **YubiKey hardware vulnerabilities** | Vouch trusts the FIDO2 implementation of enrolled authenticators. Hardware side-channel attacks on the YubiKey secure element are outside scope. | --- ## Validation - **Automated scanning** -- Dependency auditing (`cargo deny`), SAST, and supply chain verification run on every commit. - **SLSA provenance** -- Release binaries include Level 3 provenance attestations, verifiable against the source repository. - **FIDO2 conformance** -- WebAuthn assertion validation is tested against the FIDO Alliance conformance test vectors. - **Client diagnostics** -- `vouch doctor` performs runtime checks (connectivity, agent health, credential helper configuration) to validate the local setup. ## Review schedule This threat model is reviewed quarterly and after any significant architecture change. The revision history below tracks updates. --- ## Revision history | Date | Change | |---|---| | 2026-03-23 | Added HTTP Message Signatures (RFC 9421) as a mitigation for request tampering (T-T1). Added device posture policies (CEL) as a mitigation for compromised endpoints and insider abuse (T-E1, T-E2). Updated login dataflow to include device posture evaluation step. Updated mitigation summary table. | | 2026-03-02 | Updated TLS requirement to 1.3 (TLS 1.2 removed). Added KMS signing architecture, NitroTPM attestation, and document-level encryption. Added assets inventory, validation, and review schedule sections. Aligned to AWS Threat Composer methodology: added dataflow diagram, impacted assets to all threat statements, priority metadata, and assumption-to-mitigation links. Updated mitigation summary table. | | 2026-02-28 | Initial threat model published on vouch.sh, structured using STRIDE and the AWS Threat Composer methodology. | --- # Availability and Failure Modes Source: https://vouch.sh/docs/availability/ Vouch sits in the critical path for developer credentials. This page documents what happens when the server is unreachable, when a session expires, or when individual integrations fail. --- ## Normal operation During normal operation with an active session (up to 8 hours after `vouch login`): | Integration | Credential source | Lifetime | |---|---|---| | SSH | Certificate cached in agent memory | 8 hours | | AWS (`credential_process`) | STS credentials fetched on demand, cached | 1 hour | | GitHub | Installation token fetched on demand | 1 hour | | Docker (ECR) | Registry token fetched on demand | 12 hours | | Docker (GHCR) | GitHub token fetched on demand | 1 hour | | CodeCommit | SigV4 signature computed on demand | Per-request | | CodeArtifact | Authorization token fetched on demand | 12 hours | | Cargo | Token fetched on demand | Session lifetime | --- ## Offline behavior ### Server unreachable during active session If the Vouch server becomes unreachable while you have an active session: | What works | Why | |---|---| | **SSH connections** | The SSH certificate is cached in the agent's memory. It remains valid until it expires (up to 8 hours from login). No server contact is needed. | | **AWS commands** | Cached STS credentials remain valid until their 1-hour expiry. If cached credentials exist, `credential_process` returns them without contacting the server. | | **Docker pulls** (with cached token) | ECR tokens last up to 12 hours and are cached locally by Docker. | | What fails | Why | |---|---| | **New AWS credential requests** (after cache expires) | `credential_process` calls `vouch credential aws`, which needs to exchange the session for fresh STS credentials via the server. | | **New GitHub token requests** | GitHub installation tokens are fetched through the server. | | **New CodeArtifact tokens** | Token exchange requires server communication. | | **`vouch login`** | A new login always requires the server for FIDO2 validation. | **Key point:** An active session with cached credentials continues working without server contact. The impact of a server outage depends on when cached credentials expire. In the worst case, you have up to 1 hour of AWS access and up to 8 hours of SSH access after the server goes down. ### Server unreachable with no active session If the Vouch server is unreachable and you have no active session (e.g., at the start of a workday), you cannot authenticate. No new credentials can be obtained. **Mitigation:** Break-glass access. Maintain a separate emergency access path (e.g., an IAM user with MFA in a sealed envelope, or AWS root account credentials in a hardware security module) for situations where Vouch is unavailable and critical access is needed. --- ## Session expiry When your 8-hour session expires: - **SSH connections** in progress continue until they are closed (the certificate was already presented during connection setup). - **New SSH connections** fail because the certificate has expired. - **AWS commands** continue working until the cached STS credentials expire (up to 1 hour after the last credential fetch). - **New credential requests** of all types fail until you run `vouch login` again. This is by design. Short session lifetimes limit the window of exposure if a machine is compromised. --- ## Credential helper failure modes ### `credential_process` (AWS) The AWS CLI and SDKs call `vouch credential aws` via the `credential_process` configuration. If this command fails: - The AWS CLI prints an error: `Error when retrieving credentials from custom-process`. - The command does not fall back to other credential sources in the same profile. However, if you have other AWS profiles configured (e.g., a fallback profile with static keys), you can switch profiles. - **Important:** `credential_process` errors do not cache. Each AWS command retries the credential process independently. ### SSH agent If the Vouch agent is not running: - `ssh` falls back to the next available authentication method (other SSH agents, keys in `~/.ssh/`, password authentication) depending on your SSH configuration. - If no fallback is configured, the connection fails with `Permission denied`. ### Git credential helper If the Vouch Git credential helper fails (for GitHub or CodeCommit): - Git prompts for a username and password, or fails with `Authentication failed` depending on the remote configuration. - This does not affect other Git remotes that do not use Vouch. --- ## De-provisioning timeline When a user is de-provisioned (via SCIM or manual removal): | Time | Effect | |---|---| | **Immediately** | Active sessions are revoked on the server. New credential requests fail. | | **Within 1 hour** | Cached AWS STS credentials expire. AWS access stops. | | **Within 8 hours** | SSH certificate expires. SSH access stops. | | **Within 12 hours** | Cached ECR/CodeArtifact tokens expire. | The maximum exposure window after de-provisioning is the longest credential lifetime (currently 12 hours for ECR tokens). For most integrations, access ends within 1 hour. --- ## Status and monitoring Check the Vouch server's operational status: - **Status page:** Contact your Vouch server administrator for status page URL. - **CLI health check:** `vouch status` reports whether the agent is running and whether the current session is valid. --- ## Planning for failure ### Recommendations 1. **Establish break-glass procedures** before rolling out Vouch. Document an emergency access path that does not depend on Vouch. 2. **Stagger session starts** across the team. If everyone logs in at 9 AM, everyone's sessions expire at 5 PM. Consider encouraging re-login before critical deployments. 3. **Monitor `vouch login` failures** as an early warning of server issues. 4. **Keep the Vouch agent running** to preserve cached credentials across CLI invocations. On macOS, use `brew services`. On Linux, use systemd. 5. **Test credential expiry** in a non-production environment so the team knows what failure looks like before it happens in production. --- # Authenticate to AWS CodeArtifact without Stored Tokens Source: https://vouch.sh/docs/codeartifact/ Vouch authenticates to [AWS CodeArtifact](https://docs.aws.amazon.com/codeartifact/latest/ug/welcome.html) using hardware-backed IAM credentials. After a single `vouch login`, Cargo, pip, npm, pnpm, and uv can pull and publish packages without manual token management. {{< tldr >}} - **Prerequisites:** [Getting Started](/docs/getting-started/) → [AWS integration](/docs/aws/) → this page. - **Admin, once:** add `codeartifact:GetAuthorizationToken`, `codeartifact:GetRepositoryEndpoint`, `codeartifact:ReadFromRepository`, and `sts:GetServiceBearerToken` to the Vouch IAM role. - **Each developer:** `vouch setup codeartifact --tool <cargo|pip|npm|pnpm|uv> --repository <REPO>`, then use the package manager normally. {{< /tldr >}} ## Prerequisites {{< role admin >}} Before developers can configure the AWS CodeArtifact integration: - The **[AWS integration](/docs/aws/)** must be configured (OIDC provider and IAM role) - An **AWS CodeArtifact domain and repository** must exist in your AWS account - The IAM role must have `codeartifact:GetAuthorizationToken`, `codeartifact:GetRepositoryEndpoint`, `codeartifact:ReadFromRepository`, and `sts:GetServiceBearerToken` permissions --- ## Step 1 -- Configure the Vouch CLI {{< role developer >}} Run the setup command to configure Vouch for your AWS CodeArtifact repository: ```bash vouch setup codeartifact --tool cargo --repository my-repo [--domain my-domain] [--domain-owner 123456789012] [--region us-east-1] [--domain-profile my-profile] ``` | Flag | Description | |---|---| | `--tool` | Package manager to configure: `cargo`, `pip`, `npm`, `pnpm`, or `uv` (required) | | `--repository` | The AWS CodeArtifact repository name (required) | | `--domain` | The AWS CodeArtifact domain name (optional if a domain profile is configured) | | `--domain-owner` | AWS account ID that owns the domain (optional if a domain profile is configured) | | `--region` | AWS region (optional if a domain profile is configured) | | `--domain-profile` | Named domain profile to use or create (see [Profiles](#profiles) below) | | `--profile` | AWS profile in `~/.aws/config` whose role mints tokens for this domain | This configures the appropriate credential helper for your package manager and writes the necessary configuration files. --- ## Step 2 -- Use your package manager normally {{< role developer >}} {{< session-note >}} #### Cargo ```bash # Build a project that depends on private crates cargo build --registry codeartifact-my-repo # Publish a crate to your AWS CodeArtifact registry cargo publish --registry codeartifact-my-repo ``` The setup command creates the registry as `codeartifact-<repository>` in `~/.cargo/config.toml`. Cargo tokens are fetched dynamically on each operation via the credential provider. No token refresh is needed. #### pip ```bash # Install a package from your AWS CodeArtifact repository pip install my-package --index-url https://my-domain-123456789012.d.codeartifact.us-east-1.amazonaws.com/pypi/my-repo/simple/ # Install from requirements.txt pip install -r requirements.txt ``` pip tokens are fetched dynamically via the keyring subprocess protocol: setup writes `keyring-provider = subprocess` and the index URL to `pip.conf`, and installs a `keyring` shim in `~/.local/bin/` that pip calls for the token. No token refresh is needed. #### npm ```bash # Install packages npm install # Publish a package npm publish ``` npm uses a static token written to `.npmrc`. The token is automatically refreshed each time you run `vouch login`, so you do not need to re-run setup commands. #### pnpm ```bash vouch setup codeartifact --tool pnpm --repository my-repo ``` pnpm supports [tokenHelper](https://pnpm.io/npmrc#tokenhelper), which lets an external program supply authentication tokens dynamically. Vouch installs a `vouch-pnpm-tokenhelper` symlink in `~/.local/bin/` and configures `.npmrc` to use it. Tokens are fetched on demand -- no expiry, no manual refresh. ```bash # Install packages pnpm install # Publish a package pnpm publish ``` #### uv ```bash vouch setup codeartifact --tool uv --repository my-repo ``` [uv](https://docs.astral.sh/uv/) supports the keyring subprocess protocol for dynamic credential fetching. Vouch installs a `keyring` symlink in `~/.local/bin/` and configures `~/.config/uv/uv.toml` with `keyring-provider = "subprocess"` and a CodeArtifact index entry. ```bash # Install packages uv pip install my-package # Sync a project uv sync ``` uv does not read `pip.conf`. If you also use pip, run `vouch setup codeartifact --tool pip` separately. --- ## Supported package managers | Package Manager | Protocol | Authentication Method | Token Model | |---|---|---|---| | **Cargo** | `sparse+https` | Bearer token via credential provider | Dynamic (fetched on demand) | | **pip** | HTTPS | Token via keyring subprocess | Dynamic (fetched on demand) | | **uv** | HTTPS | Token via keyring subprocess | Dynamic (fetched on demand) | | **pnpm** | HTTPS | Token via `tokenHelper` | Dynamic (fetched on demand) | | **npm** | HTTPS | Bearer token via `.npmrc` | Static (embedded in `.npmrc`, auto-refreshed on login) | **Dynamic tokens** (Cargo, pip, uv, pnpm) are fetched transparently on each operation and do not expire during normal use. **npm** uses a static token written to `.npmrc`, but it is automatically refreshed each time you run `vouch login` -- no manual token rotation needed. --- ## Profiles Vouch supports named domain profiles for AWS CodeArtifact, allowing you to store domain, domain owner, and region settings and reuse them across commands. Domain profiles are stored in `~/.config/vouch/config.json`. Domain profiles (`--domain-profile`) are distinct from AWS profiles (`--profile`): a domain profile names a saved CodeArtifact domain bundle in Vouch's config, while `--profile` selects the AWS profile in `~/.aws/config` whose IAM role mints the tokens. ### Default profile When you run `vouch setup codeartifact` with `--domain`, `--domain-owner`, and `--region`, these values are saved to the default domain profile. Subsequent commands can omit these flags: ```bash # First time: specify all values (saved to default profile) vouch setup codeartifact --tool cargo --domain my-domain --domain-owner 123456789012 --repository my-repo --region us-east-1 # Later: only --tool and --repository are needed vouch setup codeartifact --tool pip --repository my-pypi-repo ``` ### Named profiles Use `--domain-profile` to create and manage separate configurations for different AWS CodeArtifact domains or accounts: ```bash # Create a domain profile for the shared artifacts account vouch setup codeartifact --tool cargo --domain shared-packages --domain-owner 111111111111 --repository cargo-store --domain-profile shared # Create a domain profile for the team account vouch setup codeartifact --tool cargo --domain team-packages --domain-owner 222222222222 --repository team-cargo --domain-profile team ``` Named domain profiles are referenced by other commands using the `--domain-profile` flag. --- ## Environment variables You can inject a `CODEARTIFACT_AUTH_TOKEN` environment variable into your shell or a subprocess using `vouch env` or `vouch exec`. This is useful for tools that read the token from the environment (such as Maven or custom scripts). ### `vouch env` Output the token as a shell export statement: ```bash eval "$(vouch env --type codeartifact [--codeartifact-domain <DOMAIN>] [--codeartifact-domain-owner <ACCOUNT_ID>] [--codeartifact-region <REGION>] [--codeartifact-profile <PROFILE>] [--shell <SHELL>])" ``` This sets `CODEARTIFACT_AUTH_TOKEN` in your current shell. ### `vouch exec` Run a command with the token injected: ```bash vouch exec --type codeartifact [--codeartifact-domain <DOMAIN>] [--codeartifact-domain-owner <ACCOUNT_ID>] [--codeartifact-region <REGION>] [--codeartifact-profile <PROFILE>] -- mvn deploy ``` | Flag | Description | |---|---| | `--codeartifact-domain` | AWS CodeArtifact domain name (optional if a domain profile is configured) | | `--codeartifact-domain-owner` | AWS account ID that owns the domain (optional if a domain profile is configured) | | `--codeartifact-region` | AWS region (optional if a domain profile is configured) | | `--codeartifact-profile` | Named CodeArtifact domain profile to use | --- ## Cross-partition support All AWS partitions are supported -- standard (`aws`), China (`aws-cn`), GovCloud (`aws-us-gov`), and European Sovereign Cloud (`aws-eusc`). Vouch derives the partition-specific CodeArtifact endpoint from the partition of your IAM role's ARN (configured via the [AWS integration](/docs/aws/)); pass the partition's region via `--region` during setup. --- ## Troubleshooting ### "Access denied" when fetching packages - Verify your IAM role has the following permissions: - `codeartifact:GetAuthorizationToken` - `codeartifact:GetRepositoryEndpoint` - `codeartifact:ReadFromRepository` - `sts:GetServiceBearerToken` - Confirm the AWS CodeArtifact domain and repository names are correct. - Check that you have an active Vouch session: `vouch login`. ### "Token is expired" - Run `vouch login` to refresh your session. For npm, this also automatically refreshes the static token in `.npmrc`. - For **Cargo/pip/pnpm/uv**: Dynamic tokens are fetched on demand, so expiry usually indicates the Vouch session itself has ended. ### Wrong domain or repository - Run `vouch setup codeartifact` again with the correct `--tool` and `--repository` flags. - If using profiles, check `~/.config/vouch/config.json` for the stored domain and region values. - Check your package manager's configuration files for conflicting settings. ### Package manager not using Vouch - Ensure no environment variables (e.g., `CODEARTIFACT_AUTH_TOKEN`) are overriding the credential helper. - Verify the package manager configuration points to the correct AWS CodeArtifact endpoint. --- ## Maven For Maven projects, use `vouch credential codeartifact` or `vouch exec` to obtain a token: ```bash # Option 1: Set the token in your shell export CODEARTIFACT_AUTH_TOKEN=$(vouch credential codeartifact) # Option 2: Use vouch exec to inject the token into Maven vouch exec --type codeartifact -- mvn deploy -s settings.xml ``` If you need to specify the domain explicitly: ```bash export CODEARTIFACT_AUTH_TOKEN=$(vouch credential codeartifact --domain my-domain --domain-owner 123456789012) ``` In your `settings.xml`, reference the environment variable as the password: ```xml <server> <id>codeartifact</id> <username>aws</username> <password>${env.CODEARTIFACT_AUTH_TOKEN}</password> </server> ``` --- ## Cross-Account Access If your AWS CodeArtifact domain is in a different AWS account, use named profiles to manage access: ```bash # Set up a Vouch AWS profile for the artifacts account vouch setup aws \ --role arn:aws:iam::ARTIFACTS_ACCOUNT:role/CodeArtifactReader \ --profile vouch-artifacts # Create a CodeArtifact domain profile that uses the artifacts account vouch setup codeartifact \ --tool npm \ --domain shared-packages \ --domain-owner ARTIFACTS_ACCOUNT \ --repository npm-store \ --domain-profile artifacts \ --profile vouch-artifacts # Use the domain profile when fetching credentials vouch credential codeartifact --domain-profile artifacts ``` --- ## How it works 1. **Package manager requests a token** -- When a package manager needs to authenticate to an AWS CodeArtifact repository, the Vouch credential helper intercepts the request. 2. **OIDC to STS** -- Vouch exchanges your active hardware-backed session for temporary AWS STS credentials via `AssumeRoleWithWebIdentity`. 3. **STS to AWS CodeArtifact** -- Vouch calls `codeartifact:GetAuthorizationToken` with the STS credentials to obtain an AWS CodeArtifact authorization token. 4. **Package manager authenticates** -- The token is returned to the package manager and used for the current operation. Tokens are short-lived and, except for npm's static `.npmrc` entry, never written to disk. --- ## Token Lifetime AWS CodeArtifact authorization tokens are valid for up to **12 hours** by default. For Cargo, pip, pnpm, and uv, Vouch fetches tokens dynamically on each operation, so expiry is transparent. For npm, the static token in `.npmrc` is automatically refreshed each time you run `vouch login`. If your Vouch session (8 hours) has expired, run `vouch login` first -- this refreshes both your session and any npm tokens. --- # Authenticate to Kubernetes with OIDC Source: https://vouch.sh/docs/kubernetes/ Vouch acts as an OIDC provider for your Kubernetes clusters. After a YubiKey tap, the CLI fetches an OIDC ID token and presents it to the API server -- no cloud-specific plugins and no static tokens. > **Using Amazon EKS?** EKS has its own token mechanism based on AWS IAM. See [Amazon EKS](/docs/eks/) for EKS-specific setup. {{< tldr >}} - **Prerequisites:** [Getting Started](/docs/getting-started/) → this page. - **Admin, once:** [configure the API server](#configuring-the-api-server) to trust Vouch as an OIDC issuer, and add [RBAC bindings](#rbac-configuration). - **Each developer:** `vouch setup k8s --cluster my-cluster --server https://k8s.example.com:6443`, then `kubectl` just works. {{< /tldr >}} ## Prerequisites You will also need: - **kubectl** installed (`kubectl version --client`). - **A Kubernetes cluster** with OIDC authentication configured on the API server (see below). --- ## Configuring the API server {{< role admin >}} The Kubernetes API server must be configured to trust the Vouch server as an OIDC provider. Add the following flags to your `kube-apiserver` configuration: ``` --oidc-issuer-url=https://us.vouch.sh --oidc-client-id=kubernetes --oidc-username-claim=sub ``` | Flag | Description | |---|---| | `--oidc-issuer-url` | The Vouch server URL. The API server fetches `/.well-known/openid-configuration` from this URL to discover the JWKS endpoint. | | `--oidc-client-id` | The expected `aud` (audience) claim in the ID token. Must match the `--audience` flag used with `vouch credential k8s` (default: `kubernetes`). | | `--oidc-username-claim` | The token claim to use as the Kubernetes username. Set to `sub` to use the developer's email address. | | `--oidc-groups-claim` | (Optional) The token claim to use for group membership in RBAC rules. | | `--oidc-username-prefix` | (Optional) Prefix added to usernames to avoid collisions with other authentication methods (e.g., `vouch:`). | How you set these flags depends on your Kubernetes distribution: - **kubeadm**: Add to `ClusterConfiguration.apiServer.extraArgs` in the kubeadm config. - **k3s**: Pass as arguments to `k3s server`, e.g., `k3s server --kube-apiserver-arg="oidc-issuer-url=https://us.vouch.sh"`. - **GKE**: Use [GKE Identity Service](https://cloud.google.com/kubernetes-engine/docs/how-to/oidc) to configure OIDC. - **AKS**: Use [AKS OIDC configuration](https://learn.microsoft.com/en-us/azure/aks/use-oidc-issuer). > **Important:** The API server must be able to reach `https://us.vouch.sh/.well-known/openid-configuration` and the JWKS endpoint to validate tokens. Ensure your network policies allow outbound HTTPS from the control plane to the Vouch server. --- ## Setup {{< role developer >}} Configure `kubectl` to use your Vouch-backed OIDC credentials: ```bash vouch setup k8s \ --cluster my-cluster \ --server https://k8s.example.com:6443 \ --certificate-authority /path/to/ca.pem ``` Required flags: | Flag | Description | |---|---| | `--cluster` | A name for this cluster (used in kubeconfig entries and as a cache key) | | `--server` | The Kubernetes API server URL | Optional flags: | Flag | Description | |---|---| | `--certificate-authority` | Path to the cluster's CA certificate file (PEM format). The certificate is base64-encoded and embedded in the kubeconfig. | | `--audience` | OIDC audience — must match `--oidc-client-id` on the API server (default: `kubernetes`) | | `--kubeconfig` | Path to kubeconfig file (defaults to `~/.kube/config`) | This command writes or updates your kubeconfig with: - A **cluster** entry named after the `--cluster` value with the server URL and CA certificate. - A **user** entry named `vouch-k8s-{cluster}` configured to call `vouch credential k8s` as an exec-based credential plugin. - A **context** named `{cluster}-vouch` linking the cluster and user entries. ### Verify the kubeconfig Check that the context is set and working: ```bash kubectl config use-context my-cluster-vouch kubectl get pods ``` --- ## Usage {{< role developer >}} {{< session-note >}} Daily usage: ```bash # Switch to your Vouch K8s context kubectl config use-context my-cluster-vouch # Use kubectl as normal kubectl get pods kubectl get namespaces kubectl logs deployment/my-app ``` The exec credential plugin fetches a fresh OIDC token for each `kubectl` invocation. --- ## RBAC configuration {{< role admin >}} With OIDC authentication, the Kubernetes username is the developer's email address (from the `sub` claim). Use standard Kubernetes RBAC to map users to permissions. ### Cluster-wide admin access ```yaml apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: name: vouch-cluster-admin subjects: - kind: User name: alice@example.com apiGroup: rbac.authorization.k8s.io roleRef: kind: ClusterRole name: cluster-admin apiGroup: rbac.authorization.k8s.io ``` ### Namespace-scoped access ```yaml apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: name: vouch-developer-edit namespace: staging subjects: - kind: User name: bob@example.com apiGroup: rbac.authorization.k8s.io roleRef: kind: ClusterRole name: edit apiGroup: rbac.authorization.k8s.io ``` ### Group-based access If you configure `--oidc-groups-claim` on the API server, you can assign permissions by group: ```yaml apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: name: vouch-team-viewers subjects: - kind: Group name: engineering@example.com apiGroup: rbac.authorization.k8s.io roleRef: kind: ClusterRole name: view apiGroup: rbac.authorization.k8s.io ``` --- ## How it works ``` vouch login --> vouch credential k8s --> kubectl ``` 1. **`vouch login`** — The developer authenticates with their YubiKey and receives an OIDC session from the Vouch server. 2. **`vouch credential k8s`** — The CLI requests an OIDC ID token with the audience set to match the cluster's `--oidc-client-id` (default: `kubernetes`). It outputs a Kubernetes [`ExecCredential`](https://kubernetes.io/docs/reference/config-api/client-authentication.v1/) JSON object containing the token and its expiration -- no static kubeconfig tokens to rotate. 3. **`kubectl`** — The Kubernetes client sends the token to the API server. The API server validates it against the Vouch server's OIDC discovery endpoint (`/.well-known/openid-configuration`) and applies RBAC rules based on the token claims. --- ## Troubleshooting ### "error: You must be logged in to the server (Unauthorized)" - Confirm you have an active Vouch session: `vouch status`. - Verify the API server has OIDC configured and can reach the Vouch server's OIDC discovery endpoint. - Check that the `--oidc-client-id` on the API server matches the `--audience` used during setup (default: `kubernetes`). - Inspect the token: `vouch credential k8s --cluster my-cluster` and decode the JWT to check the `aud` and `iss` claims. ### "Unable to connect to the server" - Verify the API server URL: `kubectl config view --minify -o jsonpath='{.clusters[0].cluster.server}'`. - Check that the CA certificate is correct if connecting to a cluster with a private CA. - Ensure your network allows connections to the API server endpoint. ### "No active session" - Run `vouch login` to authenticate with your YubiKey. ### Token audience mismatch - The `--audience` flag in `vouch setup k8s` and `vouch credential k8s` must match the `--oidc-client-id` flag on the kube-apiserver. The default for both is `kubernetes`. - If you used a custom audience during setup, verify with: `kubectl config view --raw -o jsonpath='{.users[?(@.name=="vouch-k8s-my-cluster")].user.exec.args}'`. ### Diagnosing configuration issues - Run `vouch doctor` to check your Vouch configuration. - Check API server logs for OIDC validation errors — common issues include the API server being unable to fetch the JWKS endpoint or a clock skew causing token validation failures. --- # SAML Identity Providers Source: https://vouch.sh/docs/saml/ Most organizations already have a SAML-based identity provider for single sign-on. Vouch supports **SAML 2.0** as a first-class authentication protocol alongside OIDC, so you can use your existing IdP without changes. From a developer's perspective, there is no difference -- enrollment and login work the same way regardless of whether your organization uses SAML or OIDC. The browser-based sign-in flow routes through your IdP, and the CLI handles everything else. --- ## Supported identity providers Vouch has been tested with: - **Microsoft Entra ID** (formerly Azure AD) - **Google Workspace** Any SAML 2.0 compliant identity provider that supports HTTP-POST or HTTP-Redirect bindings should work. --- ## How it works 1. A developer runs `vouch enroll` or `vouch login`. 2. The browser opens the Vouch server, which detects the configured SAML identity provider. 3. The developer is redirected to the IdP's login page (Google Workspace, Entra ID, etc.). 4. After authentication, the IdP sends a signed SAML assertion back to Vouch's Assertion Consumer Service (ACS) endpoint. 5. Vouch validates the assertion signature, extracts the user's identity, and issues a session. 6. The CLI picks up the session and credential helpers work normally. The SAML flow uses the same browser-based redirect pattern as OIDC. Developers do not need to know which protocol their organization uses. --- ## Configuring your identity provider To connect your SAML identity provider to Vouch, you need two pieces of information from your Vouch server: | Field | Value | |---|---| | **SP Metadata URL** | `https://<your-vouch-server>/saml/metadata` | | **ACS URL** | `https://<your-vouch-server>/saml/acs` | The SP metadata URL provides a machine-readable XML document containing the entity ID, ACS endpoint, and signing certificate. Most identity providers can import this directly. ### Okta 1. In the Okta Admin Console, go to **Applications > Create App Integration**. 2. Select **SAML 2.0** and click **Next**. 3. Enter an app name (e.g., "Vouch"). 4. Set the **Single sign-on URL** to your ACS URL (`https://<your-vouch-server>/saml/acs`). 5. Set the **Audience URI (SP Entity ID)** to your Vouch server URL (`https://<your-vouch-server>`). 6. Under **Attribute Statements**, map `email` to `user.email`. 7. Assign users or groups to the application. ### Microsoft Entra ID 1. In the Azure portal, go to **Entra ID > Enterprise applications > New application**. 2. Select **Create your own application** and choose "Integrate any other application you don't find in the gallery (Non-gallery)". 3. Under **Single sign-on > SAML**, click **Upload metadata file** and point it at `https://<your-vouch-server>/saml/metadata`. Alternatively, set the fields manually: - **Identifier (Entity ID):** `https://<your-vouch-server>` - **Reply URL (ACS URL):** `https://<your-vouch-server>/saml/acs` 4. Under **Attributes & Claims**, ensure `emailaddress` maps to `user.mail`. 5. Assign users or groups to the application. ### Google Workspace 1. In the Google Admin console, go to **Apps > Web and mobile apps > Add app > Add custom SAML app**. 2. Enter an app name (e.g., "Vouch"). 3. On the **Service provider details** page: - **ACS URL:** `https://<your-vouch-server>/saml/acs` - **Entity ID:** `https://<your-vouch-server>` 4. Add an attribute mapping: **Primary email** mapped to `email`. 5. Turn on the app for the relevant organizational units. --- ## Developer experience From the developer's perspective, SAML and OIDC work identically: ```bash # Enrollment (one-time) vouch enroll --server https://<your-vouch-server> # Daily login vouch login ``` The browser opens, the developer signs in through the IdP, and the CLI receives a session. All credential helpers (SSH, AWS, GitHub, etc.) work the same way regardless of the underlying authentication protocol. --- ## FAQ ### Can I use both SAML and OIDC? Each Vouch organization is configured with a single identity provider. If your IdP supports both protocols, choose whichever your organization standardizes on. ### Do developers need to know which protocol is configured? No. The CLI and browser-based flow are identical for both SAML and OIDC. Developers do not need to take any different actions. ### Which SAML bindings are supported? Vouch supports **HTTP-POST** and **HTTP-Redirect** bindings for authentication requests. --- # Connect to EC2 Instances without SSH Port 22 Source: https://vouch.sh/docs/ssm/ AWS Systems Manager Session Manager connects to EC2 instances without opening SSH ports, and every session is logged in CloudTrail. With Vouch, the underlying AWS credentials are hardware-verified and short-lived. {{< tldr >}} - **Prerequisites:** [Getting Started](/docs/getting-started/) → [AWS integration](/docs/aws/) → this page. - **Admin, once:** add [`ssm:StartSession` permissions](#iam-permissions) to the Vouch IAM role and give instances an SSM instance profile. - **Each developer:** `vouch setup ssm`, then `ssh i-0abc123def456` just works. {{< /tldr >}} ## Prerequisites {{< role admin >}} Before developers can start AWS SSM sessions: - The **[AWS integration](/docs/aws/)** must be configured (OIDC provider and IAM role) - EC2 instances need the **AWS SSM Agent** installed and an instance profile that allows AWS SSM connections --- ## Step 1 -- Start a session {{< role developer >}} {{< session-note >}} Install the **[Session Manager plugin](https://docs.aws.amazon.com/systems-manager/latest/userguide/session-manager-working-with-install-plugin.html)** for the AWS CLI, then connect to an instance: ```bash aws ssm start-session \ --target i-0abc123def456 \ --profile vouch ``` This opens an interactive shell session on the target instance without SSH. --- ## Step 2 -- SSH over AWS SSM {{< role developer >}} You can also use AWS SSM as a transport for standard SSH connections, so familiar SSH tooling (scp, rsync, port forwarding) works without direct TCP connections. ### Automated setup (recommended) The `vouch setup ssm` command configures your SSH client automatically: ```bash vouch setup ssm ``` | Flag | Description | |---|---| | `--profile` | AWS profile to use (defaults to auto-detected vouch profile) | | `--region` | AWS region to use in the ProxyCommand | | `--hosts` | Host patterns to match (default: `i-* mi-*`) | | `--force` | Overwrite any existing SSM configuration in `~/.ssh/config` | To specify a profile and region explicitly: ```bash vouch setup ssm --profile vouch --region us-east-1 ``` This adds the following to your `~/.ssh/config`: ``` Host i-* mi-* ProxyCommand sh -c "aws ssm start-session --target %h --document-name AWS-StartSSHSession --parameters 'portNumber=%p' --profile vouch --region us-east-1" ``` ### Manual setup Or add it to your `~/.ssh/config` yourself: ``` Host i-* mi-* ProxyCommand sh -c "aws ssm start-session --target %h --document-name AWS-StartSSHSession --parameters 'portNumber=%p' --profile vouch --region <YOUR_REGION>" ``` Replace `<YOUR_REGION>` with your AWS region (e.g., `us-east-1`). ### Connect Then connect with SSH as usual: ```bash ssh i-0abc123def456 ``` The session is authenticated with both your Vouch SSH certificate and your hardware-backed AWS credentials. --- ## Port forwarding {{< role developer >}} AWS SSM supports port forwarding to access services on private instances -- for example, reaching RDS through a bastion instance without exposing it to the internet: ```bash # Forward local port 5432 to an RDS instance through an EC2 bastion aws ssm start-session \ --target i-0abc123def456 \ --document-name AWS-StartPortForwardingSessionToRemoteHost \ --parameters '{"host":["mydb.cluster-abc123.us-east-1.rds.amazonaws.com"],"portNumber":["5432"],"localPortNumber":["5432"]}' \ --profile vouch ``` Then connect to `localhost:5432` with your database client. --- ## IAM permissions {{< role admin >}} The IAM role assumed by Vouch needs AWS SSM session permissions: ```json { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "ssm:StartSession", "ssm:TerminateSession", "ssm:ResumeSession" ], "Resource": [ "arn:aws:ec2:us-east-1:123456789012:instance/*", "arn:aws:ssm:us-east-1::document/AWS-StartSSHSession", "arn:aws:ssm:us-east-1::document/AWS-StartPortForwardingSessionToRemoteHost" ] } ] } ``` You can restrict access to specific instances using resource ARNs or tag-based conditions. --- ## Session identity and audit When Vouch exchanges an OIDC token for STS credentials, the user's email and domain are embedded as session tags. These appear in CloudTrail under `userIdentity.sessionContext.webIdFederationData`. With [Session Manager logging](https://docs.aws.amazon.com/systems-manager/latest/userguide/session-manager-logging.html) enabled, every command executed during a session is also recorded. --- ## How it works 1. **`vouch login`** -- The developer authenticates with their YubiKey and receives an OIDC ID token. 2. **`credential_process`** -- The AWS CLI calls Vouch to exchange the OIDC token for temporary STS credentials. 3. **`aws ssm start-session`** -- The AWS CLI uses the STS credentials to start a session with the target instance. 4. **CloudTrail** -- Every session start is recorded with the Vouch user's identity via STS session tags. ``` vouch login → credential_process → STS → AWS SSM start-session → CloudTrail ``` --- ## Troubleshooting ### "SessionManagerPlugin is not found" The Session Manager plugin is not installed or not in your PATH. Install it from the [AWS documentation](https://docs.aws.amazon.com/systems-manager/latest/userguide/session-manager-working-with-install-plugin.html). ### "TargetNotConnected" The target instance does not have a running AWS SSM agent or cannot reach the AWS Systems Manager endpoint. Verify: - The instance has an IAM instance profile with `AmazonSSMManagedInstanceCore` permissions. - The AWS SSM agent is running: `sudo systemctl status amazon-ssm-agent`. - The instance can reach the AWS SSM endpoint (either through a NAT gateway or VPC endpoint). ### "Access denied" when starting a session - Verify your IAM role has `ssm:StartSession` permission for the target instance. - Check that the instance ARN matches the resource constraints in your IAM policy. - Ensure you have an active Vouch session: `vouch login`. ### `vouch setup ssm` reports existing SSM configuration If the command detects an existing SSM block in your `~/.ssh/config`, it will not overwrite it by default. Use the `--force` flag to replace the existing configuration: ```bash vouch setup ssm --force ``` ### `vouch doctor` reports SSM issues Run `vouch doctor` to diagnose SSM configuration problems. If issues are found, run `vouch setup ssm` to reconfigure your SSH client automatically. --- # Migration Guide Source: https://vouch.sh/docs/migration/ Migrating to Vouch does not have to be all-or-nothing. You can install Vouch alongside your existing credentials and migrate one integration at a time. This guide covers the migration mechanics: the recommended order, per-integration checklists, revoking old credentials, and rolling back. > Planning the overall rollout -- foundation setup, service enablement, team onboarding, offboarding? Start with the [Team Rollout playbook](/docs/rollout/) and come back here for the migration details. --- ## Phase 1 -- Install and enroll Install the Vouch CLI alongside your existing credential setup. Nothing changes yet. 1. **Install the CLI** on each developer's machine. See [Getting Started](/docs/getting-started/). 2. **Enroll YubiKeys** with the Vouch server. Each developer runs `vouch enroll`. 3. **Test login.** Each developer runs `vouch login` and verifies they can authenticate. At this point, Vouch is installed but no integrations are active. Existing SSH keys, AWS access keys, and GitHub PATs continue to work as before. --- ## Phase 2 -- Migrate integrations one at a time Pick one integration to migrate first. AWS is recommended because `credential_process` works alongside existing credentials without conflict. ### Migration order (recommended) | Order | Integration | Why first/last | |---|---|---| | 1 | **AWS** | `credential_process` adds a new profile. Existing profiles and access keys are unaffected. | | 2 | **SSH** | Vouch SSH certificates work alongside existing SSH keys. The SSH agent falls back to keys if the certificate is unavailable. | | 3 | **GitHub** | Git credential helpers can be stacked. Vouch adds a new helper without removing existing ones. | | 4 | **Docker** | Docker credential helpers can be configured per registry. Migrate one registry at a time. | | 5 | **CodeCommit** | Requires AWS integration to be working first. | | 6 | **CodeArtifact** | Requires AWS integration to be working first. | | 7 | **EKS** | Requires AWS integration and `kubectl` configuration. | | 8 | **Databases** | Requires AWS integration and application-level changes for IAM auth. | ### Per-integration checklist For each integration: - [ ] **Configure the integration** using `vouch setup <integration>`. - [ ] **Test with one developer** before rolling out to the team. - [ ] **Verify the tool works** with Vouch credentials (e.g., `aws s3 ls --profile vouch`, `ssh user@server`, `git push`). - [ ] **Run for one week** with both Vouch and static credentials available. - [ ] **Migrate the rest of the team** once the pilot developer confirms it works. --- ## Phase 3 -- Revoke old credentials After each integration is working with Vouch for the entire team, revoke the static credentials it replaced: ### AWS access keys ```bash # List existing access keys aws iam list-access-keys --user-name alice # Deactivate first (in case you need to re-enable) aws iam update-access-key --user-name alice --access-key-id AKIAXXXXXXXX --status Inactive # Delete after confirming everything works aws iam delete-access-key --user-name alice --access-key-id AKIAXXXXXXXX ``` Check `~/.aws/credentials` on each developer's machine and remove static entries. ### SSH keys 1. Remove old public keys from `~/.ssh/authorized_keys` on servers (or from your configuration management tool). 2. Keep the Vouch CA public key as the only trusted signer in `sshd_config`. 3. Developers can keep their SSH key files locally as a fallback, or remove them. ### GitHub PATs 1. Navigate to GitHub **Settings > Developer settings > Personal access tokens**. 2. Revoke tokens that were used for repository access. 3. Verify that `git push` still works with the Vouch credential helper. ### Docker credentials 1. Check `~/.docker/config.json` for stored registry credentials. 2. Remove entries for registries now handled by Vouch. --- ## CI/CD considerations CI/CD pipelines typically do not have YubiKeys. They will continue to use their existing credential mechanisms: | CI/CD pattern | Recommendation | |---|---| | **GitHub Actions** | Use OIDC federation with GitHub's built-in OIDC provider (`token.actions.githubusercontent.com`). This is separate from Vouch and provides the same STS-based authentication for pipelines. | | **AWS CodeBuild / CodePipeline** | Use IAM roles attached to the build environment. No static keys needed. | | **Jenkins / GitLab CI** | Use IAM roles if running on EC2, or [Vouch CI/CD integration](/docs/cicd/) for human approval gates. | | **Static keys in CI/CD** | If your pipeline currently uses static AWS keys, keep them for now. Replace them with OIDC federation (GitHub Actions) or IAM roles (EC2-based runners) as a separate project. | Vouch's [CI/CD integration](/docs/cicd/) is designed for human approval gates (e.g., requiring a YubiKey tap before a production deployment), not for replacing machine credentials in automated pipelines. --- ## Rollback plan If you need to revert to static credentials for any integration: ### AWS 1. Re-enable or re-create IAM access keys for affected users. 2. Update `~/.aws/credentials` with the static keys. 3. Remove or comment out the `credential_process` line in `~/.aws/config` for the Vouch profile. ### SSH 1. Re-add public keys to `~/.ssh/authorized_keys` on servers. 2. Ensure `IdentityFile` entries in `~/.ssh/config` point to the static keys. 3. SSH falls back to keys automatically if the Vouch certificate is unavailable. ### GitHub 1. Generate a new GitHub PAT. 2. Update the Git credential helper or set `GIT_ASKPASS` to use the PAT. 3. Remove the Vouch credential helper entry from `~/.gitconfig`. ### Docker 1. Run `docker login <registry>` with static credentials. 2. Remove the Vouch credential helper from `~/.docker/config.json`. --- ## Verification checklist After completing migration for all integrations: - [ ] All developers can `vouch login` and access all required services. - [ ] No static AWS access keys remain active in IAM. - [ ] No static SSH keys remain in `authorized_keys` (only the Vouch CA is trusted). - [ ] No GitHub PATs remain active. - [ ] CI/CD pipelines continue to function with their own credential mechanisms. - [ ] SCIM provisioning is configured for automated onboarding/offboarding (recommended for teams > 15 people). - [ ] Break-glass procedures are documented for emergency access without Vouch. See [Availability](/docs/availability/). --- # Bridge Human Identity with SPIFFE Workload Identity Source: https://vouch.sh/docs/spiffe/ [SPIFFE](https://spiffe.io/) gives every workload a cryptographic identity, but it does not address *who deployed the workload* or *who authorized the action*. Because Vouch is a standards-compliant OIDC provider, you can configure [SPIRE](https://spiffe.io/docs/latest/spire-about/spire-concepts/) (the SPIFFE reference implementation) to trust Vouch-issued tokens -- bridging human and workload identity in a single architecture. ## How it works Vouch and SPIRE operate at different layers of the identity stack: ``` Human layer (Vouch) Workload layer (SPIFFE/SPIRE) ───────────────────── ───────────────────────────── YubiKey tap Workload attestation → Vouch OIDC ID token → X.509-SVID or JWT-SVID → kubectl, AWS, SSH, etc. → mTLS, service-to-service auth ``` The integration points: 1. **SPIRE trusts Vouch as an OIDC issuer** — SPIRE validates Vouch tokens using the `/.well-known/openid-configuration` and JWKS endpoints to make authorization decisions based on hardware-verified human identity. 2. **Workloads get SPIFFE SVIDs** — SPIRE issues short-lived X.509 or JWT credentials to workloads via the Workload API, independent of any human session. 3. **Both coexist in the same infrastructure** — Humans authenticate with `vouch login` + YubiKey; services authenticate with SPIFFE SVIDs. Downstream systems (Kubernetes, AWS, databases) can accept both. --- ## Prerequisites - **Vouch CLI installed and enrolled** — Complete the [Getting Started](/docs/getting-started/) guide. - **SPIRE Server and Agent deployed** — See the [SPIRE Getting Started guide](https://spiffe.io/docs/latest/try/getting-started-linux/) or the [Kubernetes quickstart](https://spiffe.io/docs/latest/try/getting-started-k8s/). - **`spire-server` and `spire-agent` CLI tools** available on your path. --- ## Configure SPIRE to trust Vouch tokens This is the integration itself: SPIRE validates Vouch OIDC tokens so that human identity can inform workload registration and authorization decisions. ### Register Vouch as a federated trust domain Add a federation block to your SPIRE Server config so SPIRE automatically fetches and refreshes Vouch's signing keys: ```hcl # spire-server.conf server { trust_domain = "example.org" # ... } plugins { # Existing plugins... KeyManager "disk" { plugin_data { keys_path = "/opt/spire/data/keys.json" } } } # Federation with Vouch OIDC provider federation { bundle_endpoint { address = "0.0.0.0" port = 8443 } federates_with "vouch.sh" { bundle_endpoint_url = "https://us.vouch.sh" bundle_endpoint_profile "https_web" {} } } ``` The `https_web` profile tells SPIRE to authenticate the endpoint using its public TLS certificate (standard web PKI). SPIRE fetches the JWKS from `https://us.vouch.sh/oauth/jwks` and automatically refreshes it as keys rotate. **For air-gapped environments**, fetch the keys manually and import them: ```bash curl -s https://us.vouch.sh/oauth/jwks -o vouch-jwks.json spire-server bundle set \ -id spiffe://vouch.sh \ -format jwks \ -path vouch-jwks.json ``` > **Note:** The manual approach requires re-running these commands whenever Vouch rotates its signing keys. Prefer the automatic federation config above unless your SPIRE Server cannot reach `https://us.vouch.sh`. ### Create workload registration entries with deployer identity With federation in place, you can register workloads and tag them with the Vouch-authenticated deployer's identity. This creates an audit trail from the human who deployed a workload to the SPIFFE identity the workload runs with: ```bash # Register a backend API workload # The "deployer" selector records who authorized this registration spire-server entry create \ -spiffeID spiffe://example.org/backend-api \ -parentID spiffe://example.org/spire-agent \ -selector k8s:ns:production \ -selector k8s:sa:backend-api \ -metadata "deployer:alice@example.com" ``` ### Validate Vouch tokens in a custom attestor For advanced use cases, you can write a [custom workload attestor plugin](https://spiffe.io/docs/latest/extending/extending/) that validates a Vouch OIDC token presented by a workload during attestation. This lets workloads bootstrap their SPIFFE identity using a short-lived Vouch token: ```bash # A workload requests a Vouch token with a SPIFFE-specific audience vouch credential k8s --audience spiffe://example.org # The custom attestor validates the token against Vouch's JWKS # and maps the `sub` claim to a SPIFFE ID ``` | Vouch token claim | SPIRE mapping | |---|---| | `iss` | Must match `https://us.vouch.sh` | | `sub` | Maps to deployer identity metadata | | `aud` | Must match the trust domain or a configured audience | | `exp` | Token must not be expired | | `amr` | Can require `["hwk", "pin"]` for hardware attestation | --- ## Patterns Once SPIRE trusts Vouch tokens, the common architectures are standard SPIFFE/SPIRE deployments with Vouch supplying the human identity layer. The SPIRE mechanics are covered by the [SPIRE documentation](https://spiffe.io/docs/latest/); what Vouch adds to each: - **Kubernetes with human + service identity** -- Developers reach the API server with Vouch OIDC (see the [Kubernetes guide](/docs/kubernetes/)); pods authenticate to each other with X.509-SVIDs issued by SPIRE. The layers are complementary: Vouch covers human-to-cluster, SPIRE covers pod-to-pod mTLS. - **Multi-cloud service mesh** -- SPIRE servers in each cloud federate via bundle exchange so services authenticate across trust domains, while operators use the same `vouch login` session for access to every environment. - **Zero-trust CI/CD with human approval** -- a self-hosted runner attests its own identity via SPIFFE SVID, and the deployment additionally requires a Vouch OIDC token minted by a human with a YubiKey -- see [CI/CD approval gates](/docs/cicd/) for the Vouch half of that pattern. --- ## SPIFFE concepts reference | Concept | Description | |---|---| | **SPIFFE ID** | A URI that uniquely identifies a workload: `spiffe://trust-domain/path` | | **SVID** | SPIFFE Verifiable Identity Document — an X.509 certificate or JWT that encodes a SPIFFE ID | | **Trust domain** | The root of trust for a SPIFFE deployment (e.g., `example.org`) | | **Workload API** | Local API (Unix socket) that workloads call to get their SVIDs and trust bundles | | **SPIRE Server** | Central component that manages identities and issues SVIDs | | **SPIRE Agent** | Per-node component that attests workloads and exposes the Workload API | | **Federation** | Cross-trust-domain authentication via bundle exchange | For full details, see the [SPIFFE specification](https://github.com/spiffe/spiffe/tree/main/standards) and [SPIRE documentation](https://spiffe.io/docs/latest/). --- ## Troubleshooting ### SPIRE cannot reach the Vouch OIDC discovery endpoint - Verify the SPIRE Server can make outbound HTTPS requests to `https://us.vouch.sh/.well-known/openid-configuration`. - Check network policies, firewall rules, and DNS resolution from the SPIRE Server pod or host. - Test connectivity: `curl -s https://us.vouch.sh/.well-known/openid-configuration | jq .` ### SVID validation failures - Ensure the trust bundles are exchanged correctly between federated SPIRE servers. Check with `spire-server bundle show`. - Verify the workload registration entries match the actual pod selectors: `spire-server entry show`. - Check that the SPIRE Agent is running on the node where the workload is scheduled. ### Token audience mismatch - The `aud` claim in Vouch tokens must match what SPIRE expects. When using `vouch credential k8s`, the default audience is `kubernetes`. For SPIFFE integration, specify a custom audience: `vouch credential k8s --audience spiffe://example.org`. - Verify with: `vouch credential k8s --audience spiffe://example.org | jq -r '.status.token' | step crypto jwt inspect --insecure | jq '.payload.aud'` ### Clock skew causing JWT validation errors - SPIRE validates `exp` and `nbf` claims in Vouch tokens. Ensure clocks are synchronized across all nodes using NTP. - Vouch tokens are short-lived — even a few minutes of clock skew can cause validation failures. - Check the SPIRE Server logs: `kubectl logs -n spire-system deployment/spire-server` ### "No identity issued" from Workload API - Confirm the SPIRE Agent is running: `spire-agent healthcheck -socketPath /run/spire/sockets/agent.sock`. - Verify that a registration entry exists for the workload: `spire-server entry show -selector k8s:ns:YOUR_NAMESPACE -selector k8s:sa:YOUR_SERVICE_ACCOUNT`. - Check that the workload's service account and namespace match the registered selectors exactly. --- # Short-Lived Credentials for the Claude & OpenAI APIs Source: https://vouch.sh/docs/ai-api-keys/ Both [Anthropic](https://platform.claude.com/docs/en/manage-claude/workload-identity-federation) and [OpenAI](https://developers.openai.com/api/docs/guides/workload-identity-federation/aws) support **Workload Identity Federation (WIF)**: a workload presents a short-lived OIDC token from an issuer you operate, and the provider exchanges it for a minutes-long access token — no static API key. In production the workload is usually a GitHub Actions runner, an AWS Lambda, or a GKE pod, and the right issuer is the native OIDC token those platforms already mint. This page covers a narrower case: **local development and one-off scripts on a developer laptop**, where the alternative is pasting an `sk-ant-...` or `sk-...` key into a `.env` file and forgetting about it. Vouch is a standards-compliant OIDC issuer, so you can point Anthropic and OpenAI at it like any other IdP and exchange a Vouch-issued JWT for a short-lived provider token — no static key on disk. ``` vouch login → Vouch OIDC JWT → jwt-bearer exchange → short-lived provider token → Claude / OpenAI API ``` {{< tldr >}} - **Prerequisites:** [Getting Started](/docs/getting-started/) → this page. - **Admin, once:** register Vouch as an OIDC identity provider in the [Anthropic](#claude-api-anthropic) (Steps 1-3) / [OpenAI](#openai-api) (Step 1) console. - **Each developer:** `vouch setup anthropic` / `vouch setup openai` once, then `vouch credential anthropic` / `vouch credential openai` -- SDKs work without an `sk-` key. {{< /tldr >}} > Vouch must be reachable from the public internet so the provider can fetch the JWKS endpoint to validate token signatures. See [Architecture](/docs/architecture/) for network requirements. --- ## Claude API (Anthropic) Anthropic's WIF accepts any standards-compliant OIDC issuer. You register Vouch as a **federation issuer**, point a **federation rule** at a **service account**, and exchange Vouch tokens for short-lived `sk-ant-oat01-...` tokens. ### Step 1 -- Register Vouch as a federation issuer {{< role admin >}} In the [Claude Console](https://platform.claude.com/), go to **Settings → Workload identity → Issuers** and select **Create issuer**. Fill in the form: - **Name** -- a lowercase identifier shown in rules and audit logs. `vouch` is a good default. - **Issuer URL (iss claim)** -- `https://us.vouch.sh`. This must match the `iss` in Vouch-minted tokens exactly. - **JWKS source** -- **OIDC discovery**. Vouch serves `/.well-known/openid-configuration` at the issuer URL, which advertises the `jwks_uri` Anthropic fetches signing keys from. - **Discovery base URL** -- leave blank. Vouch publishes discovery at the issuer URL. - **CA certificate (PEM)** -- leave blank. Vouch terminates TLS with a publicly trusted certificate. - **Token validation → Enforce single-use tokens (JTI replay protection)** -- leave **on** (the default). Vouch mints a unique `jti` per ID token. - **Token validation → Maximum token lifetime** -- leave at **1 hour** (the default). Vouch ID tokens are short-lived and well under this ceiling. ### Step 2 -- Create a service account and workspace {{< role admin >}} Go to **Settings → Service accounts → Create service account** (for example, `local-dev`) and note its ID (`svac_...`). `vouch setup anthropic` in Step 4 requires a workspace ID (`wrkspc_...`), and the organization's **Default** workspace does not expose one. Go to **Settings → Workspaces → Create workspace**, add the service account to it, and note the new workspace's ID. ### Step 3 -- Create a federation rule {{< role admin >}} Back on **Workload identity → Federation rules**, select **Create rule**. The form has four sections: **Basic info** - **Rule name** + optional **Description**. - **Issuer** -- pick the Vouch issuer registered in Step 1. **Match configuration** Keep the default **Pattern match** mode. Vouch tokens carry the logged-in identity in `sub` (the user's email); additional claims (`hd`, `email_verified`) are exact strings or booleans that **Additional claim conditions** handles natively. - **Subject pattern** -- the developer's email, e.g. `developer@example.com`. Leave blank if you are matching on a claim condition only. - **Expected audience** -- `https://us.vouch.sh`. Vouch's default `aud` is the issuer URL, so matching that string here means no extra `--audience` flag in Step 4. Anthropic enforces audience even though the field is labeled optional; mismatches are rejected with `jwt_audience_mismatch`. - **Additional claim conditions** -- for a company-wide allow, set claim key `hd` and expected value `example.com` (your Vouch hosted-domain). > **Avoid CEL expression mode.** In CEL the Expected audience field is hidden and not enforced, and the CEL evaluator handles `claims.aud` in ways that don't match Vouch's tokens reliably (`==` against a string-typed `aud` and `in` against a list-typed `aud` have both been observed to fail). Stay on Pattern match. **Target** - **Service account** -- the `svac_...` from Step 2. **Authorization** - **Workspaces** -- pick the workspace from Step 2. Leave "Enable in all workspaces" off. - **OAuth scope** -- e.g. `workspace:developer`. - **Token lifetime** -- 10 minutes is the default; shorter is better. Note the rule ID (`fdrl_...`) -- you will need it in Step 4. ### Step 4 -- Get a token {{< role developer >}} {#claude-get-a-token} > **Requires Vouch v2026.5.4 or later.** On earlier versions, drive the exchange directly with the [official Anthropic SDK's](https://platform.claude.com/docs/en/manage-claude/workload-identity-federation) `ANTHROPIC_FEDERATION_*` environment variables and `ANTHROPIC_IDENTITY_TOKEN_FILE`, kept current from the AWS-flow ID token returned by [`/v1/credentials/aws/token`](/docs/aws/). Record the federation parameters once, then mint short-lived tokens on demand: ```bash vouch setup anthropic \ --federation-rule-id fdrl_... \ --organization-id 00000000-0000-0000-0000-000000000000 \ --service-account-id svac_... \ --workspace-id wrkspc_... vouch login # once per session vouch credential anthropic # prints a short-lived sk-ant-oat01-... token ``` No `--audience` flag is needed unless the rule's Expected audience differs from Vouch's default (see [Audience matching](#audience-matching)). `vouch setup anthropic` persists these parameters in `~/.config/vouch/config.json` for subsequent `vouch credential anthropic` invocations. `vouch credential anthropic` mints a fresh OIDC ID token from your active Vouch session, exchanges it via the [RFC 7523](https://www.rfc-editor.org/rfc/rfc7523) `jwt-bearer` grant, and caches the returned `sk-ant-oat01-...` until just before it expires (the same caching as [`vouch credential aws`](/docs/cli-reference/)). Use it inline: ```bash curl -sS https://api.anthropic.com/v1/messages \ -H "authorization: Bearer $(vouch credential anthropic)" \ -H "anthropic-version: 2023-06-01" \ -H "content-type: application/json" \ -d '{"model":"claude-sonnet-4-6","max_tokens":1024,"messages":[{"role":"user","content":"Hello, Claude"}]}' ``` For scripts and SDKs that read `ANTHROPIC_AUTH_TOKEN` from the environment, two helpers wrap the credential call: ```bash eval "$(vouch env --type anthropic)" # exports ANTHROPIC_AUTH_TOKEN in the current shell vouch exec --type anthropic -- python app.py # runs a command with ANTHROPIC_AUTH_TOKEN set ``` Use `ANTHROPIC_AUTH_TOKEN`, not `ANTHROPIC_API_KEY` — the API-key variable is reserved for static `sk-ant-api...` keys and rejects federated `sk-ant-oat01-...` tokens. Make sure no `ANTHROPIC_API_KEY` is set in your shell profile or it will silently shadow the federated token. --- ## OpenAI API OpenAI's WIF similarly exchanges an OIDC JWT for a short-lived OpenAI access token. ### Step 1 -- Register Vouch as the OIDC issuer {{< role admin >}} In your OpenAI organization's workload identity settings, configure: | Field | Value | |---|---| | Issuer URL | `https://us.vouch.sh` | | Audience | The audience your workload requests (see [Audience matching](#audience-matching) below) | | Subject mapping | Map the Vouch `sub` claim to the OpenAI service account | ### Step 2 -- Get a token {{< role developer >}} > **Requires Vouch v2026.5.4 or later** (see the [Claude API note above](#claude-get-a-token) for the pre-v2026.5.4 SDK workaround). ```bash vouch setup openai \ --identity-provider-id wip_... \ --service-account-id sa_... \ --audience https://api.openai.com/v1 # set to whatever audience OpenAI configured vouch login vouch credential openai # prints a short-lived OpenAI access token ``` Use it inline the same way: ```bash curl -sS https://api.openai.com/v1/responses \ -H "authorization: Bearer $(vouch credential openai)" \ -H "content-type: application/json" \ -d '{"model":"gpt-5","input":"hello"}' ``` Static keys shadow federation here too: as with [`ANTHROPIC_API_KEY` above](#claude-get-a-token), unset any `OPENAI_API_KEY`. --- ## Audience matching {#audience-matching} Vouch mints federation assertions with `aud` set to its issuer URL (`https://us.vouch.sh`) by default. The simplest setup is to match that exact string on the provider side, which is what the Anthropic walkthrough above does. If a provider requires a specific audience (OpenAI configures the audience server-side when you register the issuer), pin that exact string on both sides: - On the **rule** (Anthropic) or **identity provider** (OpenAI): set the audience to the value the provider expects. - On **Vouch**: pass `--audience <same value>` to `vouch setup anthropic` / `vouch setup openai`. ## Beyond local development For workloads that run somewhere other than a developer laptop, prefer the native OIDC issuer of the platform they run on — GitHub Actions, AWS, GCP, and Kubernetes all mint their own workload tokens that Anthropic and OpenAI accept directly. For an unattended job that does need to federate through Vouch (a scheduled task on a server you control, for example), use Vouch's [client-credentials flow](/docs/applications/#client-credentials-machine-to-machine) to obtain a Vouch token without an interactive login, then run the same federation exchange. ## Background WIF is workload-shaped, not workforce-shaped — there is no console login flow for an end user. It's a way to authenticate *something running somewhere* without giving it a static API key. Internally, Vouch mints audience-scoped tokens via [RFC 8707 resource indicators](https://datatracker.ietf.org/doc/html/rfc8707) or [RFC 8693 token exchange](/docs/applications/#service-to-service-m2m-authentication); the `--audience` flag is the surface for that. ## Troubleshooting **Provider cannot fetch JWKS / signature validation fails.** The issuer URL must be `https://us.vouch.sh` and reachable from the public internet. Confirm `https://us.vouch.sh/.well-known/openid-configuration` resolves and that the `jwks_uri` it advertises is reachable. **Rule does not match.** The provider matches against the **ID token** that `vouch credential anthropic` / `vouch credential openai` mints internally (its `sub` is your email). Note that `vouch credential token` prints the RFC 9068 *access* token whose `sub` is a stable user UUID — useful for debugging Vouch-protected APIs, but not what the federation rule sees. The Authentication events tab in the Claude Console shows the decoded JWT for any failed exchange. **Audience mismatch.** If the provider rejects the audience, mint an audience-scoped token (see [Audience matching](#audience-matching)) so `aud` equals the value the provider expects. **A static key keeps winning.** `ANTHROPIC_API_KEY` / `OPENAI_API_KEY` take precedence over federation. Unset them everywhere the workload runs (shell profile, `.env`, CI secrets). ## Related guides - [Amazon Bedrock](/docs/bedrock/) -- access Claude models *through AWS* with Vouch STS credentials. - [AWS](/docs/aws/) -- the OIDC federation pattern Vouch uses for AWS STS. - [Architecture](/docs/architecture/) -- how Vouch issues and signs OIDC tokens. --- # Connect to RDS and Aurora without Database Passwords Source: https://vouch.sh/docs/databases/ [IAM database authentication](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/UsingWithRDS.IAMDBAuth.html) replaces static database passwords with short-lived tokens generated from IAM credentials -- with Vouch, hardware-backed ones. {{< tldr >}} - **Prerequisites:** [Getting Started](/docs/getting-started/) → [AWS integration](/docs/aws/) → this page. - **Admin, once:** grant [`rds-db:connect`](#required-iam-permissions) on the Vouch IAM role and enable IAM auth on the database user. - **Each developer:** `vouch exec --type rds --rds-hostname <host> --rds-username <user> -- psql`, then `psql` just works — the 15-minute token is injected automatically. {{< /tldr >}} ## RDS / Aurora PostgreSQL {{< role developer >}} #### Vouch CLI `vouch exec` generates the token and injects PostgreSQL environment variables (`PGPASSWORD`, `PGHOST`, `PGPORT`, `PGUSER`, `PGSSLMODE=require`) automatically: ```bash vouch exec --type rds \ --rds-hostname mydb.cluster-abc123.us-east-1.rds.amazonaws.com \ --rds-username mydbuser \ -- psql -d mydb ``` Or set them in your current shell: ```bash eval "$(vouch env --type rds \ --rds-hostname mydb.cluster-abc123.us-east-1.rds.amazonaws.com \ --rds-username mydbuser)" psql -d mydb ``` To generate just the token (for scripts or non-PostgreSQL clients): ```bash TOKEN=$(vouch credential rds \ --hostname mydb.cluster-abc123.us-east-1.rds.amazonaws.com \ --username mydbuser) ``` #### AWS CLI Or use the AWS CLI with Vouch's `credential_process` integration: ```bash # Generate an IAM auth token (valid for 15 minutes) TOKEN=$(aws rds generate-db-auth-token \ --hostname mydb.cluster-abc123.us-east-1.rds.amazonaws.com \ --port 5432 \ --username mydbuser \ --profile vouch) # Connect with psql PGPASSWORD="$TOKEN" psql \ -h mydb.cluster-abc123.us-east-1.rds.amazonaws.com \ -p 5432 \ -U mydbuser \ -d mydb \ "sslmode=require" ``` {{< role admin >}} **Database setup:** grant the PostgreSQL user the `rds_iam` role: ```sql GRANT rds_iam TO mydbuser; ``` --- ## RDS / Aurora MySQL {{< role developer >}} MySQL requires `--enable-cleartext-plugin` because the IAM token is sent as a cleartext password over TLS. #### Vouch CLI Generate the token and pass it to the MySQL client: ```bash TOKEN=$(vouch credential rds \ --hostname mydb.cluster-abc123.us-east-1.rds.amazonaws.com \ --username mydbuser \ --port 3306) mysql -h mydb.cluster-abc123.us-east-1.rds.amazonaws.com \ -P 3306 \ -u mydbuser \ --password="$TOKEN" \ --ssl-mode=REQUIRED \ --enable-cleartext-plugin ``` > **Note:** `vouch exec` and `vouch env --type rds` inject PostgreSQL-style variables; MySQL users should use `vouch credential rds` and pass the token manually. #### AWS CLI ```bash # Generate an IAM auth token TOKEN=$(aws rds generate-db-auth-token \ --hostname mydb.cluster-abc123.us-east-1.rds.amazonaws.com \ --port 3306 \ --username mydbuser \ --profile vouch) # Connect with mysql (note: --enable-cleartext-plugin is required) mysql -h mydb.cluster-abc123.us-east-1.rds.amazonaws.com \ -P 3306 \ -u mydbuser \ --password="$TOKEN" \ --ssl-mode=REQUIRED \ --enable-cleartext-plugin ``` {{< role admin >}} **Database setup:** Create the user with the `AWSAuthenticationPlugin`: ```sql CREATE USER 'mydbuser'@'%' IDENTIFIED WITH AWSAuthenticationPlugin AS 'RDS'; ``` --- ## Amazon Redshift {{< role developer >}} Redshift issues temporary credentials with a configurable lifetime (15--60 minutes); Vouch supports provisioned clusters and Redshift Serverless workgroups. ### Using Vouch CLI (provisioned cluster) `vouch exec` generates credentials and injects PostgreSQL environment variables (`PGPASSWORD`, `PGUSER`, `PGSSLMODE=require`) automatically: ```bash vouch exec --type redshift \ --redshift-cluster-id my-cluster \ --redshift-db-name mydb \ -- psql -h my-cluster.abc123.us-east-1.redshift.amazonaws.com -p 5439 ``` Or set them in your current shell: ```bash eval "$(vouch env --type redshift \ --redshift-cluster-id my-cluster \ --redshift-db-name mydb)" psql -h my-cluster.abc123.us-east-1.redshift.amazonaws.com -p 5439 ``` To generate just the credentials: ```bash vouch credential redshift --cluster-id my-cluster --db-name mydb ``` The `--duration` flag controls credential lifetime for provisioned clusters (900--3600 seconds, default: 900): ```bash vouch credential redshift --cluster-id my-cluster --duration 3600 ``` ### Using Vouch CLI (Redshift Serverless) ```bash vouch exec --type redshift \ --redshift-workgroup my-workgroup \ --redshift-db-name mydb \ -- psql -h my-workgroup.123456789012.us-east-1.redshift-serverless.amazonaws.com -p 5439 ``` Or generate credentials directly: ```bash vouch credential redshift --workgroup my-workgroup --db-name mydb ``` ### Using AWS CLI ```bash # Get temporary Redshift credentials CREDS=$(aws redshift get-cluster-credentials \ --cluster-identifier my-cluster \ --db-user mydbuser \ --db-name mydb \ --duration-seconds 3600 \ --profile vouch) # Extract and connect DB_USER=$(echo "$CREDS" | jq -r '.DbUser') DB_PASS=$(echo "$CREDS" | jq -r '.DbPassword') PGPASSWORD="$DB_PASS" psql \ -h my-cluster.abc123.us-east-1.redshift.amazonaws.com \ -p 5439 \ -U "$DB_USER" \ -d mydb ``` --- ## Required IAM permissions {{< role admin >}} Your Vouch IAM role needs permission to generate database auth tokens and credentials. **RDS / Aurora:** ```json { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": "rds-db:connect", "Resource": "arn:aws:rds-db:us-east-1:123456789012:dbuser:cluster-ABC123/mydbuser" } ] } ``` **Redshift (provisioned clusters):** ```json { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": "redshift:GetClusterCredentialsWithIAM", "Resource": "arn:aws:redshift:us-east-1:123456789012:dbname:my-cluster/*" } ] } ``` **Redshift Serverless:** ```json { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": "redshift-serverless:GetCredentials", "Resource": "arn:aws:redshift-serverless:us-east-1:123456789012:workgroup/*" } ] } ``` --- ## How it works 1. **`vouch login`** -- The developer authenticates with their YubiKey. 2. **`vouch credential rds`** or **`vouch credential redshift`** -- Vouch generates a short-lived auth token or temporary credentials. 3. **Database client** -- The token is passed as the password to `psql`, `mysql`, or another client; `vouch exec` handles this automatically. ``` vouch login → vouch credential rds|redshift → database client vouch login → vouch exec --type rds|redshift -- psql ``` --- ## Troubleshooting ### "PAM authentication failed for user" Ensure IAM database authentication is enabled on the RDS instance and the database user has the `rds_iam` role (PostgreSQL) or was created with `AWSAuthenticationPlugin` (MySQL). ### Token expired RDS/Aurora auth tokens are valid for 15 minutes. Generate a fresh token before connecting. The token is only used to establish the connection -- active sessions are not affected by expiry. ### SSL required error IAM database authentication requires SSL/TLS. Use `sslmode=require` for PostgreSQL or `--ssl-mode=REQUIRED` for MySQL. --- # Frequently Asked Questions Source: https://vouch.sh/docs/faq/ ## Hardware keys ### Which YubiKeys are supported? Any **FIDO2-compatible** security key works with Vouch. Recommended models: - **YubiKey 5 series** (5 NFC, 5C, 5C NFC, 5Ci, 5C Nano, 5 Nano) -- Recommended. Supports FIDO2, USB-A or USB-C. - **YubiKey 5 FIPS series** -- Same as above, with FIPS 140-2 validation. - **YubiKey Bio** -- Supports FIDO2 with fingerprint biometrics instead of PIN. - **Security Key by Yubico** (NFC or C NFC) -- Budget option. Supports FIDO2 but lacks other YubiKey features (PIV, OpenPGP). Other FIDO2-compliant keys (e.g., Google Titan, Feitian, SoloKeys) may work but have not been tested. The key must support the `hmac-secret` extension and resident credentials. ### What happens if I lose my YubiKey? 1. **Report it immediately** to your organization administrator so they can remove the key from your account. 2. Your active session (if any) continues until it expires (up to 8 hours), but no new sessions can be created with the lost key. 3. Enroll a new YubiKey by running `vouch enroll` again. 4. If you have a backup key already enrolled, use that key to log in while you replace the lost one. ### Can I enroll multiple YubiKeys? Yes. Run `vouch enroll` with each key. This is recommended so you have a backup in case one key is lost or damaged. All enrolled keys can be used interchangeably for `vouch login`. ### Can I restrict which YubiKey models are accepted? Yes. The Vouch server supports AAGUID-based policies that control which authenticator models are accepted during enrollment and login. Set the `VOUCH_ALLOWED_AAGUIDS` environment variable on the server: - **`fips-only`** — Only FIPS-certified YubiKey models are accepted. - **`yubikey-5`** — Any YubiKey 5 series model is accepted. - **Comma-separated UUIDs** — An explicit allowlist of authenticator AAGUIDs (e.g., `cb69481e-8ff7-4039-93ec-0a2729a154a8,ee882879-721c-4913-9775-3dfcce97072a`). - **Unset or empty** — Any FIDO2 hardware key is accepted (default). Additionally, setting `VOUCH_REQUIRE_ATTESTATION_CERT=true` rejects self-attestation and requires authenticators to provide a full attestation certificate chain. The server validates the chain against pinned [Yubico root CA certificates](https://developers.yubico.com/PKI/), cryptographically proving the key is a genuine Yubico device. YubiKeys with packed attestation satisfy this requirement; platform authenticators and software-based keys typically do not. ### Can I use the same YubiKey across multiple Vouch organizations? Yes. A single YubiKey can hold multiple FIDO2 credentials. Enroll the key with each organization's Vouch server and it will work with all of them. --- ## Sessions and credentials ### How long does a session last? Sessions last **8 hours** from the time you run `vouch login`. After 8 hours, the session expires and you need to log in again. There is no way to extend a session -- you must re-authenticate with your YubiKey. ### How long do AWS credentials last? AWS STS credentials obtained through Vouch are valid for up to **1 hour**. The Vouch agent caches them, and when they expire, a new set is fetched automatically (as long as your session is active). You do not need to take any action. ### What happens when my session expires mid-task? - **SSH connections** already established continue to work. New connections will fail. - **AWS commands** fail with a credential error. Run `vouch login` and retry. - **Git operations** fail if they require authentication. Run `vouch login` and retry. - **Long-running processes** (e.g., `terraform apply`, `cdk deploy`) that started with valid credentials will continue until they need to refresh credentials. If a refresh fails mid-operation, the process may fail partially. ### Are credentials written to disk? No. All credentials are held in the Vouch agent's process memory. The session token, SSH certificate, and cached STS credentials are never written to a file. If the agent process stops, all credentials are lost. ### Does Vouch work with `aws-vault`? Vouch replaces the need for `aws-vault`. Both tools solve the same problem (avoiding static AWS credentials), but they work differently. You should use one or the other, not both. If you are currently using `aws-vault`, see the [Migration Guide](/docs/migration/) for switching to Vouch. --- ## Platform support ### Does Vouch work on Windows? Windows support is limited. The following commands work on Windows: - `vouch enroll` - `vouch login` - `vouch credential aws` - `vouch credential github` The SSH agent and SSH certificate integration are **not available** on Windows. The background agent service is also not available -- credentials are obtained directly by each command invocation. ### Does Vouch work on Linux? Yes. Vouch supports Debian/Ubuntu (APT) and Fedora/RHEL (DNF). The agent runs as a systemd user service. See [Getting Started](/docs/getting-started/) for installation instructions. ### Does Vouch work in WSL? WSL (Windows Subsystem for Linux) works the same as native Linux. Install the Linux version of the CLI and use USB passthrough for YubiKey access. ### Does Vouch work in containers? Vouch is designed for developer workstations, not containers. For containers in CI/CD pipelines, use the pipeline's native credential mechanism (e.g., GitHub Actions OIDC, IAM roles for ECS tasks). See [CI/CD Integration](/docs/cicd/) for human approval gates. --- ## AWS ### Does Vouch use AWS STS? Is there a cost? Yes, Vouch calls [AssumeRoleWithWebIdentity](https://docs.aws.amazon.com/STS/latest/APIReference/API_AssumeRoleWithWebIdentity.html) to obtain temporary credentials. **AWS STS is free** -- there is no charge for STS API calls. ### What appears in CloudTrail? Every API call made with Vouch credentials appears in CloudTrail with the assumed role session name set to the developer's email address: ``` arn:aws:sts::123456789012:assumed-role/VouchDeveloper/alice@example.com ``` This provides full per-user attribution for AWS API activity. ### Can I use Vouch with multiple AWS accounts? Yes. See [Multi-Account AWS Strategy](/docs/aws-multi-account/) for deployment patterns using CloudFormation StackSets or Terraform modules. ### Does Vouch support AWS GovCloud? Vouch supports standard and China partitions. GovCloud support depends on the integration -- check the specific integration documentation page for cross-partition details. --- ## Identity and access ### Which identity providers does Vouch support? Vouch authenticates users through your organization's OIDC or SAML 2.0 identity provider. It has been tested with **Google Workspace** and **Microsoft Entra ID** (see [SAML](/docs/saml/)). For automated user provisioning (SCIM), Vouch works with Google Workspace, Okta, Azure AD (Entra ID), and OneLogin. ### What happens when someone leaves the company? Deactivate them in your identity provider ([SCIM](/docs/scim/) revokes their Vouch sessions automatically; without SCIM, an admin removes them from the dashboard) and outstanding short-lived credentials expire on their own. The full sequence and expiry timeline is in [When someone leaves](/docs/rollout/#when-someone-leaves). ### Can I restrict which team members can assume specific AWS roles? Yes. Use IAM trust policy conditions to restrict role assumption by email address or domain. See [Restricting access](/docs/aws/#tips-for-restricting-access) in the AWS documentation. --- ## Security ### Is Vouch open source? Yes. Vouch is fully open source under the Apache-2.0 / MIT dual license. ### What data does the Vouch server store? The server stores enrolled FIDO2 public keys, user metadata (email, organization membership), and audit logs. It does not store AWS credentials, SSH private keys, GitHub tokens, or any other secrets. See [Security](/docs/security/) for details. ### What happens if the Vouch server is compromised? An attacker with server access could issue sessions for any enrolled user, which could be used to broker credentials from external services (AWS, GitHub, etc.). However, the server does not store any credentials itself -- there is no credential vault to extract. See the [threat model](/docs/security/#threat-model) for the full analysis. ### Are credentials encrypted in transit? Yes. All communication between the CLI and server uses TLS 1.3. See [Security](/docs/security/#encryption) for details. --- ## Updating Vouch ### How do I update the Vouch CLI? Update using the same package manager you used to install: - **macOS:** `brew upgrade vouch-sh/tap/vouch && brew services restart vouch` - **Debian/Ubuntu:** `sudo apt update && sudo apt upgrade vouch` - **Fedora/RHEL:** `sudo dnf upgrade vouch` - **Windows:** `winget upgrade SmokeTurner.Vouch` After upgrading, restart the agent so the new version takes effect. Check your version with `vouch --version`. ### Do I need to re-enroll after updating? No. Enrollment is stored on the server and on your YubiKey. Updating the CLI does not affect your enrollment or active sessions. --- ## Network configuration ### Does Vouch work behind a corporate proxy? Vouch respects the standard `HTTPS_PROXY` and `NO_PROXY` environment variables. If your network requires an HTTPS proxy, set these before running Vouch commands: ```bash export HTTPS_PROXY=http://proxy.corp.example.com:8080 export NO_PROXY=localhost,127.0.0.1 ``` The Vouch agent inherits proxy settings from the environment it was started in. If you change proxy settings, restart the agent. ### Does Vouch work over a VPN? Yes. Vouch connects to the Vouch server over HTTPS (port 443). As long as your VPN allows outbound HTTPS traffic to `us.vouch.sh`, Vouch works normally. If your VPN uses split tunneling, ensure the Vouch server is routable. If you experience connection timeouts after connecting to a VPN, restart the Vouch agent. ### What firewall rules does Vouch need? Vouch requires outbound HTTPS (port 443) to the Vouch server (`us.vouch.sh`). No inbound ports are required. If your firewall performs TLS inspection, ensure it does not interfere with the FIDO2 WebAuthn flow during enrollment and login. --- ## Diagnostics ### What does `vouch doctor` check? `vouch doctor` checks the agent, your session, and every integration's configuration (SSH, AWS, Git, Docker, Cargo, EKS, SSM), reporting **OK**, **WARNING**, or **ERROR** with a fix for each. Run it as a first step when something is not working. The full check list is in the [CLI reference](/docs/cli-reference/#diagnostics). --- ## Troubleshooting ### "Error: agent not running" Start the Vouch agent: - **macOS:** `brew services start vouch` - **Linux:** `systemctl --user start vouch` ### "Error: no active session" Run `vouch login` and authenticate with your YubiKey. ### "Error: credential_process returned error" This typically means your Vouch session has expired or the agent is not running. Run `vouch login` to re-authenticate. ### Where can I get help? - **Documentation:** [vouch.sh/docs](/docs/) - **GitHub Issues:** [github.com/vouch-sh/vouch/issues](https://github.com/vouch-sh/vouch/issues) - **Security issues:** Email security@vouch.sh --- # Access Amazon Bedrock with Hardware-Verified Credentials Source: https://vouch.sh/docs/bedrock/ [Amazon Bedrock](https://docs.aws.amazon.com/bedrock/latest/userguide/what-is-bedrock.html) uses standard AWS SigV4 authentication. Vouch's `credential_process` provides STS credentials backed by FIDO2 verification, so every Bedrock API call is tied to a hardware-verified human identity, with no shared API keys. ``` YubiKey tap → FIDO2 → Vouch JWT → STS → Amazon Bedrock InvokeModel → CloudTrail ``` {{< tldr >}} - **Prerequisites:** [Getting Started](/docs/getting-started/) → [AWS integration](/docs/aws/) → this page. - **Admin, once:** grant `bedrock:InvokeModel` (scoped to approved model ARNs) on the Vouch IAM role. - **Each developer:** nothing new -- `aws bedrock-runtime invoke-model ... --profile vouch` works with the existing profile. {{< /tldr >}} --- ## Step 1 -- Use Amazon Bedrock with Vouch {{< role developer >}} Any tool that uses the AWS SDK for Amazon Bedrock will pick up Vouch credentials automatically: ```bash # AWS CLI aws bedrock-runtime invoke-model \ --model-id anthropic.claude-sonnet-4-20250514 \ --body '{"prompt": "Hello"}' \ --profile vouch \ output.json ``` ```python # Python (boto3) import boto3 session = boto3.Session(profile_name='vouch') bedrock = session.client('bedrock-runtime') response = bedrock.invoke_model( modelId='anthropic.claude-sonnet-4-20250514', body='{"prompt": "Hello"}' ) ``` --- ## Step 2 -- Restrict model access by IAM policy {{< role admin >}} Use IAM policies to control which foundation models each role can invoke: ```json { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "bedrock:InvokeModel", "bedrock:InvokeModelWithResponseStream" ], "Resource": [ "arn:aws:bedrock:us-east-1::foundation-model/anthropic.claude-sonnet-4-20250514", "arn:aws:bedrock:us-east-1::foundation-model/anthropic.claude-3-5-haiku-20241022" ] } ] } ``` Restricting to specific model ARNs prevents access to more expensive or higher-capability models without authorization. --- ## The audit chain CloudTrail records every Amazon Bedrock API call with the full identity chain. The `webIdFederationData` field includes the Vouch OIDC issuer and the user's email (from the `sub` claim). With [Amazon Bedrock model invocation logging](https://docs.aws.amazon.com/bedrock/latest/userguide/model-invocation-logging.html) enabled, you get token counts and costs attributed to hardware-verified identities. --- ## Agent delegation For automated agents that call Amazon Bedrock on behalf of users, use scoped JWTs with agent-specific `sub` claims and session policies that limit model access -- the human identity chain is preserved while the agent is restricted to only the models it needs. --- ## Related guides - [Claude & OpenAI APIs](/docs/ai-api-keys/) -- Access the Claude and OpenAI APIs directly (not through AWS) with short-lived tokens via Workload Identity Federation. - [AWS](/docs/aws/) -- The OIDC federation pattern Vouch uses for AWS STS credentials. --- # Use Terraform and CDK with Hardware-Verified Credentials Source: https://vouch.sh/docs/iac/ If a tool reads `~/.aws/config`, it already works with Vouch. The `credential_process` setting in your Vouch AWS profile is picked up by the AWS SDK, so every IaC tool that uses the SDK gets hardware-verified credentials automatically. #### AWS CDK ```bash cdk deploy --profile vouch ``` CDK has known issues with SSO credential discovery ([#23520](https://github.com/aws/aws-cdk/issues/23520), [#21328](https://github.com/aws/aws-cdk/issues/21328)) that `credential_process` avoids entirely. #### AWS SAM ```bash sam deploy --profile vouch ``` #### Terraform ```bash # Set the AWS profile for the session export AWS_PROFILE=vouch terraform plan terraform apply ``` This works for the AWS provider's authentication. Terraform Cloud registry auth is separate and not handled by Vouch. #### AWS Copilot ```bash export AWS_PROFILE=vouch copilot deploy ``` #### AWS Amplify ```bash export AWS_PROFILE=vouch amplify push ``` With Vouch, you can skip `amplify configure` entirely -- there is no need to generate long-lived IAM access keys for local development. The `credential_process` in your Vouch profile provides credentials on demand. #### Pulumi ```bash export AWS_PROFILE=vouch pulumi up ``` --- ## Tips ### Setting `AWS_PROFILE` vs `--profile` Some tools accept `--profile vouch` as a flag, while others only read the `AWS_PROFILE` environment variable. Setting the environment variable works universally: ```bash export AWS_PROFILE=vouch ``` Add this to your shell profile (`.bashrc`, `.zshrc`) to make it the default for all sessions. ### Multiple accounts If you deploy to multiple AWS accounts, set up separate Vouch profiles for each: ```bash vouch setup aws --role arn:aws:iam::111111111111:role/VouchDeveloper --profile vouch-dev vouch setup aws --role arn:aws:iam::222222222222:role/VouchDeveloper --profile vouch-prod ``` Then specify the profile per command: ```bash cdk deploy --profile vouch-dev cdk deploy --profile vouch-prod ``` --- # Add Human Approval Gates to CI/CD Pipelines Source: https://vouch.sh/docs/cicd/ Vouch's OIDC attests *human presence*, so production deployments can require an explicit YubiKey tap from an authorized deployer, with the deployer's identity embedded in the resulting AWS credentials via STS session tags. > **Fully automated pipelines:** If your pipeline does not need a human approval gate and should run unattended, use the [client credentials grant](/docs/applications/#client-credentials-machine-to-machine) instead -- CI/CD systems authenticate with a client ID and secret, no YubiKey tap. {{< tldr >}} - **Prerequisites:** [Getting Started](/docs/getting-started/) → [AWS integration](/docs/aws/) → this page. - **Admin, once (this whole page):** create the deployment role and add the token-exchange step to the workflow. - **Each deploy:** the pipeline waits for a JWT an authorized deployer mints locally with `vouch credential aws --role <ROLE_ARN>`. {{< /tldr >}} ## How it works 1. A deployer runs `vouch login` locally, then mints a short-lived JWT with `vouch credential aws --role <ROLE_ARN>`. 2. The JWT is passed to the pipeline as a workflow input, secret, or environment variable. 3. The pipeline exchanges it for AWS credentials via `AssumeRoleWithWebIdentity` and deploys with credentials tied to the deployer's hardware-verified identity. --- ## Step 1 -- Register Vouch as an IAM OIDC Provider {{< role admin >}} Create the OIDC provider in your AWS account if you have not already -- see the [AWS setup guide](/docs/aws/). --- ## Step 2 -- Create a Deployment Role {{< role admin >}} This is [the shared trust policy](/docs/aws/#shared-trust-policy) from the AWS guide, plus a `sub` condition restricting which Vouch users can assume the role: ```json { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "Federated": "arn:aws:iam::ACCOUNT_ID:oidc-provider/us.vouch.sh" }, "Action": [ "sts:AssumeRoleWithWebIdentity", "sts:SetSourceIdentity", "sts:TagSession" ], "Condition": { "StringEquals": { "us.vouch.sh:aud": "https://us.vouch.sh", "us.vouch.sh:sub": [ "deployer@example.com", "release-lead@example.com" ] }, "Bool": { "sts:RoleAuthorizedByIdp": "true" } } } ] } ``` --- ## Step 3 -- GitHub Actions Workflow {{< role admin >}} The workflow takes the deployer's JWT as an input: ```yaml name: Deploy to Production on: workflow_dispatch: inputs: vouch_token: description: 'Vouch JWT (from: vouch credential aws --role <ROLE_ARN>)' required: true type: string jobs: deploy: runs-on: ubuntu-latest permissions: id-token: write contents: read steps: - uses: actions/checkout@v4 - name: Exchange Vouch JWT for AWS credentials run: | CREDS=$(aws sts assume-role-with-web-identity \ --role-arn arn:aws:iam::ACCOUNT_ID:role/ProductionDeployRole \ --role-session-name "deploy-${{ github.run_id }}" \ --web-identity-token "${{ inputs.vouch_token }}" \ --output json) echo "AWS_ACCESS_KEY_ID=$(echo $CREDS | jq -r '.Credentials.AccessKeyId')" >> $GITHUB_ENV echo "AWS_SECRET_ACCESS_KEY=$(echo $CREDS | jq -r '.Credentials.SecretAccessKey')" >> $GITHUB_ENV echo "AWS_SESSION_TOKEN=$(echo $CREDS | jq -r '.Credentials.SessionToken')" >> $GITHUB_ENV - name: Deploy run: cdk deploy --require-approval never ``` --- ## Audit trail The deployer's `email` and `domain` are embedded as STS session tags and appear in CloudTrail under `userIdentity.sessionContext.webIdFederationData`, providing a clear chain from YubiKey tap to deployment action: ```json { "userIdentity": { "type": "AssumedRole", "principalId": "AROA...:deploy-12345", "sessionContext": { "webIdFederationData": { "federatedProvider": "arn:aws:iam::ACCOUNT:oidc-provider/us.vouch.sh", "attributes": { "email": "deployer@example.com", "domain": "example.com" } } } } } ``` --- ## Troubleshooting ### Token expired in pipeline Vouch JWTs have a limited lifetime -- generate the token shortly before triggering the workflow. If it expires during deployment, the deployer needs to re-authenticate and re-trigger. ### Access denied on AssumeRoleWithWebIdentity Check that the trust policy's `sub` condition includes the deployer's email and the `aud` matches `https://us.vouch.sh`. --- # Getting Started with Vouch Source: https://vouch.sh/docs/getting-started/ Vouch replaces static developer secrets (SSH keys, AWS access keys, GitHub PATs) with short-lived credentials derived from a [FIDO2/WebAuthn](https://fidoalliance.org/fido2/) hardware key assertion. This guide walks you through installing the CLI, enrolling your YubiKey, and performing your first login. ## Prerequisites - A **YubiKey 5 series** (or any compatible FIDO2 security key) - A **Vouch server instance**, such as https://us.vouch.sh > **Organization ownership:** The first person to log into Vouch from a Google Workspace domain automatically becomes the organization owner. The owner can configure integrations, manage team members, and connect services like GitHub and AWS for the rest of the team. --- ## Step 1 -- Install the CLI #### macOS Install with Homebrew: ``` brew install vouch-sh/tap/vouch ``` After installing, start the Vouch background service: ``` brew services start vouch ``` #### Debian / Ubuntu ```bash # Import GPG key curl -fsSL https://packages.vouch.sh/gpg/vouch.asc \ | gpg --dearmor \ | sudo tee /usr/share/keyrings/vouch-archive-keyring.gpg > /dev/null # Add repository echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/vouch-archive-keyring.gpg] https://packages.vouch.sh/apt stable main" \ | sudo tee /etc/apt/sources.list.d/vouch.list > /dev/null # Install sudo apt-get update && sudo apt-get install -y vouch ``` #### Fedora / RHEL ```bash sudo tee /etc/yum.repos.d/vouch.repo << 'EOF' [vouch] name=Vouch baseurl=https://packages.vouch.sh/rpm/$basearch/ gpgcheck=1 gpgkey=https://packages.vouch.sh/gpg/vouch.asc enabled=1 EOF sudo dnf install -y vouch ``` #### Windows Install with [winget](https://learn.microsoft.com/en-us/windows/package-manager/winget/): ``` winget install SmokeTurner.Vouch ``` > **Note:** Windows support is limited. The SSH agent and SSH integration are not available on Windows. Only basic authentication and credential exchange commands are supported: `enroll`, `login`, `credential aws`, and `credential github`. ### Verify the installation After installing, confirm the CLI is available: ``` vouch --version ``` --- ## Step 2 -- Enroll your YubiKey Enrollment registers your YubiKey with the Vouch server and links it to your identity. You only need to do this once per key. ``` vouch enroll --server https://us.vouch.sh ``` This command will: 1. Display a URL and a one-time code in your terminal (using the [RFC 8628 Device Authorization Grant](https://datatracker.ietf.org/doc/html/rfc8628) flow). 2. Open the URL in your browser (or you can navigate to it manually) and enter the one-time code. 3. Ask you to verify your identity through your organization's SSO provider. 4. Prompt you to register your YubiKey as a FIDO2 credential. 5. Set a PIN on the YubiKey if one has not been configured already. 6. Save the server configuration locally so future commands know where to authenticate. Once enrollment completes, the CLI prints a confirmation and you are ready to log in. You can manage your enrolled security keys at any time from the Vouch dashboard: ![Security Keys page showing an enrolled YubiKey](/images/admin/security-keys.png) <div class="checkpoint"> <p><strong>You are done with enrollment when...</strong></p> <ul> <li><code>vouch enroll</code> finishes without errors.</li> <li>Your YubiKey appears on the dashboard security keys page.</li> <li>Future <code>vouch</code> commands remember the server URL without another <code>--server</code> flag.</li> </ul> </div> --- ## Step 3 -- Daily login Each workday begins with a single `vouch login`. This authenticates you with your YubiKey and provisions short-lived credentials that last for 8 hours. ``` vouch login Enter PIN: **** Touch your YubiKey... Authenticated for 8 hours ``` That is it. After login, every integration -- SSH, AWS, Git -- uses the session credentials automatically. When the 8-hour window expires, run `vouch login` again. <div class="checkpoint"> <p><strong>You are done with login when...</strong></p> <ul> <li>The command reports that you are authenticated.</li> <li><code>vouch status</code> shows an active session and the remaining session time.</li> <li>You do not need to touch the YubiKey again until the session expires or policy requires it.</li> </ul> </div> --- ## Step 4 -- Use your first credential With an active session, your tools work without extra wrappers. Try the integration your administrator has already configured: ``` # SSH just works ssh user@server # AWS credentials are available through the configured profile aws sts get-caller-identity --profile vouch # Git prompts Vouch's credential helper when needed git ls-remote https://github.com/example/private-repo.git ``` Vouch provides credentials on demand to each tool through the lightweight integrations configured by your organization. ### What just started working? One YubiKey tap gives you credentials that cascade across your entire toolchain: | Command | Service | |---|---| | `ssh` | Servers (certificate auth) | | `git push` | GitHub | | `aws s3 ls` | AWS CLI | | `cdk deploy` | Infrastructure as Code | | `terraform apply` | Infrastructure as Code | | `docker push` | ECR / GHCR | | `helm push` | OCI Charts | | `kubectl` | EKS | These tools read your AWS config, SSH config, Git credential helper, or Docker config -- no additional wrapper commands are needed after setup. <div class="checkpoint"> <p><strong>You are done with first use when...</strong></p> <ul> <li>At least one tool successfully uses credentials from the active Vouch session.</li> <li>AWS commands show your assumed role, SSH accepts your Vouch certificate, or Git uses the Vouch credential helper.</li> <li>No long-lived AWS keys, SSH private keys, GitHub PATs, or registry passwords were created for the test.</li> </ul> </div> --- ## Step 5 -- Choose your next integration Before a tool can use Vouch, your organization needs to configure the matching integration. Choose the guide that matches the credential you want to replace next. <div class="journey-grid"> <div class="journey-card"> <h3>Cloud and infrastructure</h3> <p>Start here for AWS, servers, Kubernetes, databases, and infrastructure tooling.</p> <p><a href="/docs/aws/">AWS</a> · <a href="/docs/ssh/">SSH</a> · <a href="/docs/eks/">EKS</a> · <a href="/docs/kubernetes/">Kubernetes</a> · <a href="/docs/databases/">Databases</a> · <a href="/docs/iac/">IaC</a></p> </div> <div class="journey-card"> <h3>Code and packages</h3> <p>Use Vouch for source control, containers, package repositories, and AWS developer services.</p> <p><a href="/docs/github/">GitHub</a> · <a href="/docs/docker/">Docker</a> · <a href="/docs/codeartifact/">CodeArtifact</a> · <a href="/docs/codecommit/">CodeCommit</a> · <a href="/docs/cargo/">Cargo</a></p> </div> <div class="journey-card"> <h3>Organization rollout</h3> <p>Roll Vouch out to your team, onboard users, and connect identity lifecycle controls.</p> <p><a href="/docs/rollout/">Team rollout</a> · <a href="/docs/startups/">Startup setup</a> · <a href="/docs/admin/">Admin dashboard</a> · <a href="/docs/scim/">SCIM</a> · <a href="/docs/migration/">Migration</a></p> </div> <div class="journey-card"> <h3>Security review</h3> <p>Evaluate the architecture, credential lifecycle, threat model, and failure modes.</p> <p><a href="/docs/security/">Security</a> · <a href="/docs/architecture/">Architecture</a> · <a href="/docs/threat-model/">Threat model</a> · <a href="/docs/availability/">Availability</a></p> </div> </div> --- ## Step 6 -- Onboard your team Once Vouch works for you, bringing the team onboard is one message: each person installs the CLI and enrolls with the same server, and anyone authenticating through your Google Workspace domain automatically joins your organization -- no invite codes, no admin approval. The **[Team Rollout playbook](/docs/rollout/)** is the guide for this phase. It has a copy-pasteable onboarding block for Slack, a per-service enablement checklist (AWS, EKS, CodeCommit, CodeArtifact, and more), when to adopt [SCIM](/docs/scim/) (15+ people), and the offboarding story. --- ## What happens when you login When you run `vouch login`, the following takes place behind the scenes: 1. **FIDO2 assertion** -- The CLI asks your YubiKey to sign a challenge from the Vouch server. This proves possession of the enrolled key and requires both your PIN and a physical touch. 2. **Identity verification** -- The server validates the signed assertion against the public key stored during enrollment. 3. **Credential issuance** -- On success, the server issues a session token and an **SSH certificate** signed by the Vouch CA, valid for 8 hours. 4. **On-demand credentials** -- AWS, Git, Docker, Cargo, and other credentials are obtained on-demand by their respective credential helpers when you use those tools. Each helper exchanges your active session for a short-lived, service-specific credential. 5. **Local caching** -- The CLI stores the session and SSH certificate in memory (via the Vouch agent) so subsequent commands can use them without additional YubiKey interaction. Because every credential is short-lived and bound to a hardware key, there are no long-lived secrets on disk that can be stolen or leaked. --- ## Related guides - [AWS Integration](/docs/aws/) -- Federate into AWS with OIDC for temporary STS credentials. - [SSH Certificates](/docs/ssh/) -- Connect to servers using short-lived SSH certificates. - [GitHub Integration](/docs/github/) -- Access private repositories with short-lived tokens. - [Security Model](/docs/security/) -- How Vouch protects credentials at every layer. - [FAQ](/docs/faq/) -- Common questions about supported hardware, session behavior, and platform support.