Skip to content

Commit cc5858f

Browse files
committed
Merge branch 'adds-otel-kubernetes' of github.com:stacklok/docs-website into adds-otel-kubernetes
2 parents f486a8d + 5c3a0b5 commit cc5858f

File tree

8 files changed

+300
-7
lines changed

8 files changed

+300
-7
lines changed

docs/toolhive/guides-cli/install.mdx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ Before installing ToolHive, make sure your system meets these requirements:
1717
- **Container runtime**:
1818
- Docker / Docker Desktop
1919
- Podman / Podman Desktop
20+
- Colima with Docker runtime
2021
- Rancher Desktop with the dockerd/moby runtime (experimental)
2122

2223
ToolHive requires minimal CPU, memory, and disk space. The exact requirements

docs/toolhive/guides-cli/run-mcp-servers.mdx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -552,7 +552,7 @@ control your servers.
552552

553553
If a server fails to start:
554554

555-
1. Check if Docker/Podman is running
555+
1. Check if Docker, Podman, or Colima is running
556556
2. Verify you have internet access to pull images
557557
3. Check if the port is already in use
558558
4. Look at the error message for specific issues

docs/toolhive/guides-ui/install.mdx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ Before installing ToolHive, make sure your system meets these requirements:
2020
- **Container runtime**:
2121
- Docker / Docker Desktop
2222
- Podman / Podman Desktop
23+
- Colima with Docker runtime
2324
- Rancher Desktop with the dockerd/moby runtime (experimental)
2425

2526
ToolHive requires minimal CPU, memory, and disk space. The exact requirements
@@ -217,7 +218,7 @@ MCP servers. See [Run MCP servers](./run-mcp-servers.md) to get started.
217218
<summary>Connection Refused error on startup</summary>
218219

219220
If you see a "Connection Refused" error when starting ToolHive, your container
220-
runtime (Docker or Podman) is likely not installed, not running, or not
221+
runtime (Docker, Podman, or Colima) is likely not installed, not running, or not
221222
configured correctly.
222223

223224
Follow the instructions in the error message to install or start your container

docs/toolhive/reference/cli/thv.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ ToolHive (thv) is a lightweight, secure, and fast manager for MCP servers
1818
ToolHive (thv) is a lightweight, secure, and fast manager for MCP (Model Context Protocol) servers.
1919
It is written in Go and has extensive test coverage—including input validation—to ensure reliability and security.
2020

21-
Under the hood, ToolHive acts as a very thin client for the Docker/Podman Unix socket API.
21+
Under the hood, ToolHive acts as a very thin client for the Docker/Podman/Colima Unix socket API.
2222
This design choice allows it to remain both efficient and lightweight while still providing powerful,
2323
container-based isolation for running MCP servers.
2424

docs/toolhive/tutorials/quickstart-cli.mdx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,8 @@ or Cursor.
2323
Before starting this tutorial, make sure you have:
2424

2525
- [Docker](https://docs.docker.com/get-docker/) or
26-
[Podman](https://podman-desktop.io/downloads) installed and running
26+
[Podman](https://podman-desktop.io/downloads) or
27+
[Colima](https://github.com/abiosoft/colima) installed and running
2728
- A [supported MCP client](../reference/client-compatibility.mdx) like GitHub
2829
Copilot in VS Code, Cursor, Claude Code, and more
2930

@@ -305,7 +306,7 @@ server. Here are some next steps to explore:
305306

306307
If the server fails to start, check:
307308

308-
- Is Docker or Podman running?
309+
- Is Docker, Podman, or Colima running?
309310
- Do you have internet access to pull the container image?
310311
- Is the port already in use by another application?
311312

docs/toolhive/tutorials/quickstart-ui.mdx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,8 @@ Cursor.
2222
Before starting this tutorial, make sure you have:
2323

2424
- [Docker](https://docs.docker.com/get-docker/) or
25-
[Podman](https://podman-desktop.io/downloads) installed and running
25+
[Podman](https://podman-desktop.io/downloads) or
26+
[Colima](https://github.com/abiosoft/colima) installed and running
2627
- A [supported MCP client](../reference/client-compatibility.mdx) like GitHub
2728
Copilot in VS Code, Cursor, Claude Code, and more
2829

@@ -139,7 +140,7 @@ server. Here are some next steps to explore:
139140

140141
If the server fails to start, check:
141142

142-
- Is Docker or Podman running?
143+
- Is Docker, Podman, or Colima running?
143144
- Do you have internet access to pull the container image?
144145

145146
</details>
Lines changed: 288 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,288 @@
1+
---
2+
title: HashiCorp Vault integration
3+
description:
4+
Learn how to securely manage MCP server secrets using HashiCorp Vault with
5+
ToolHive Kubernetes Operator.
6+
---
7+
8+
This tutorial shows how to integrate HashiCorp Vault with the ToolHive
9+
Kubernetes Operator to securely manage secrets for your MCP servers. Using
10+
Vault's Agent Injector, you can automatically provision secrets into MCP server
11+
pods without exposing sensitive data in your Kubernetes manifests.
12+
13+
As an example, we'll be deploying a GitHub MCP server.
14+
15+
:::info[Prerequisites]
16+
17+
Before starting this tutorial, ensure you have:
18+
19+
- A Kubernetes cluster with the ToolHive Operator installed
20+
- kubectl configured to access your cluster
21+
- Helm 3.x installed
22+
- Basic familiarity with HashiCorp Vault concepts
23+
- A GitHub Personal Access Token (PAT)
24+
25+
If you need help installing the ToolHive Operator, see the
26+
[Kubernetes quickstart guide](./quickstart-k8s.mdx).
27+
28+
:::
29+
30+
## Overview
31+
32+
The integration works by using HashiCorp Vault's Agent Injector to automatically
33+
inject secrets into MCP server pods. When you add specific annotations to your
34+
MCPServer resource, the Vault Agent Injector:
35+
36+
1. Detects the annotations and injects a Vault Agent sidecar
37+
2. Authenticates with Vault using Kubernetes service account tokens of the
38+
`proxyrunner` pod
39+
3. Retrieves secrets from Vault and writes them to a shared volume
40+
4. Makes the secrets available as environment variables to your MCP server pod
41+
42+
## Step 1: Install and configure Vault
43+
44+
First, install Vault with the Agent Injector enabled in your Kubernetes cluster.
45+
46+
### Install Vault using Helm
47+
48+
Add the HashiCorp Helm repository and install Vault:
49+
50+
```bash
51+
# Add HashiCorp Helm repository
52+
helm repo add hashicorp https://helm.releases.hashicorp.com
53+
helm repo update
54+
55+
# Create vault namespace
56+
kubectl create namespace vault
57+
58+
# Install Vault with Agent Injector
59+
helm install vault hashicorp/vault \
60+
--namespace vault \
61+
--set "server.dev.enabled=true" \
62+
--set "server.dev.devRootToken=dev-only-token" \
63+
--set "injector.enabled=true"
64+
```
65+
66+
:::warning[Development setup only]
67+
68+
This tutorial uses Vault in development mode (`server.dev.enabled=true`) with a
69+
static root token for simplicity. **Do not use this configuration in
70+
production**. For production deployments, follow the [Vault production hardening
71+
guide][vault-hardening].
72+
73+
:::
74+
75+
Wait for the Vault pod to be ready:
76+
77+
```bash
78+
kubectl wait --for=condition=ready pod vault-0 \
79+
--namespace vault \
80+
--timeout=300s
81+
```
82+
83+
### Configure Vault authentication
84+
85+
Configure Vault to authenticate Kubernetes service accounts:
86+
87+
```bash
88+
# Get the Vault pod name
89+
VAULT_POD=$(kubectl get pods --namespace vault \
90+
-l app.kubernetes.io/name=vault \
91+
-o jsonpath="{.items[0].metadata.name}")
92+
93+
# Enable Kubernetes auth method
94+
kubectl exec --namespace vault "$VAULT_POD" -- \
95+
vault auth enable kubernetes
96+
97+
# Configure Kubernetes auth
98+
kubectl exec --namespace vault "$VAULT_POD" -- \
99+
vault write auth/kubernetes/config \
100+
kubernetes_host="https://kubernetes.default.svc:443" \
101+
kubernetes_ca_cert=@/var/run/secrets/kubernetes.io/serviceaccount/ca.crt \
102+
token_reviewer_jwt=@/var/run/secrets/kubernetes.io/serviceaccount/token
103+
```
104+
105+
### Set up secrets engine and policies
106+
107+
Enable a key-value secrets engine and create the necessary policies:
108+
109+
```bash
110+
# Enable KV secrets engine
111+
kubectl exec --namespace vault "$VAULT_POD" -- \
112+
vault secrets enable -path=workload-secrets kv-v2
113+
114+
# Create Vault policy for MCP workloads
115+
kubectl exec --namespace vault "$VAULT_POD" -- \
116+
sh -c 'vault policy write toolhive-workload-secrets - << EOF
117+
path "auth/token/lookup-self" { capabilities = ["read"] }
118+
path "auth/token/renew-self" { capabilities = ["update"] }
119+
path "workload-secrets/data/github-mcp/*" { capabilities = ["read"] }
120+
EOF'
121+
122+
# Create Kubernetes auth role
123+
kubectl exec --namespace vault "$VAULT_POD" -- \
124+
vault write auth/kubernetes/role/toolhive-mcp-workloads \
125+
bound_service_account_names="*-proxy-runner,mcp-*" \
126+
bound_service_account_namespaces="toolhive-system" \
127+
policies="toolhive-workload-secrets" \
128+
audience="https://kubernetes.default.svc.cluster.local" \
129+
ttl="1h" \
130+
max_ttl="4h"
131+
```
132+
133+
## Step 2: Store secrets in Vault
134+
135+
Create secrets for your MCP servers in Vault. This example shows how to store a
136+
GitHub personal access token:
137+
138+
```bash
139+
# Store GitHub MCP server configuration
140+
kubectl exec --namespace vault "$VAULT_POD" -- \
141+
vault kv put workload-secrets/github-mcp/config \
142+
token="ghp_your_github_token_here" \
143+
organization="your-org"
144+
```
145+
146+
You can verify the secret was stored correctly:
147+
148+
```bash
149+
kubectl exec --namespace vault "$VAULT_POD" -- \
150+
vault kv get workload-secrets/github-mcp/config
151+
```
152+
153+
## Step 3: Configure your MCPServer resource
154+
155+
Create an MCPServer resource with Vault annotations to enable automatic secret
156+
injection. The key is using the `podTemplateMetadataOverrides` field to add
157+
annotations to the proxy runner pods:
158+
159+
```yaml title="github-mcp-with-vault.yaml"
160+
apiVersion: toolhive.stacklok.dev/v1alpha1
161+
kind: MCPServer
162+
metadata:
163+
name: github-vault
164+
namespace: toolhive-system
165+
spec:
166+
image: ghcr.io/github/github-mcp-server:latest
167+
transport: stdio
168+
port: 9095
169+
permissionProfile:
170+
type: builtin
171+
name: network
172+
resources:
173+
limits:
174+
cpu: '100m'
175+
memory: '128Mi'
176+
requests:
177+
cpu: '50m'
178+
memory: '64Mi'
179+
resourceOverrides:
180+
proxyDeployment:
181+
podTemplateMetadataOverrides:
182+
annotations:
183+
# Enable Vault Agent injection
184+
vault.hashicorp.com/agent-inject: 'true'
185+
vault.hashicorp.com/role: 'toolhive-mcp-workloads'
186+
187+
# Inject GitHub configuration secret
188+
vault.hashicorp.com/agent-inject-secret-github-config: 'workload-secrets/data/github-mcp/config'
189+
vault.hashicorp.com/agent-inject-template-github-config: |
190+
{{- with secret "workload-secrets/data/github-mcp/config" -}}
191+
GITHUB_PERSONAL_ACCESS_TOKEN={{ .Data.data.token }}
192+
{{- end -}}
193+
```
194+
195+
### Understanding the annotations
196+
197+
The key annotations that enable Vault integration are:
198+
199+
- `vault.hashicorp.com/agent-inject: "true"` - Enables Vault Agent injection for
200+
this pod
201+
- `vault.hashicorp.com/role: "toolhive-mcp-workloads"` - Specifies the Vault
202+
role to use for authentication
203+
- `vault.hashicorp.com/agent-inject-secret-github-config` - Tells Vault to
204+
retrieve a secret and make it available as a file
205+
- `vault.hashicorp.com/agent-inject-template-github-config` - Uses a Vault
206+
template to format the secret as environment variables
207+
208+
When ToolHive detects the `vault.hashicorp.com/agent-inject` annotation, it
209+
automatically configures the proxy runner to read environment variables from the
210+
`/vault/secrets/` directory where the Vault Agent writes the rendered templates.
211+
212+
## Step 4: Deploy your MCPServer
213+
214+
Apply your MCPServer configuration:
215+
216+
```bash
217+
kubectl apply -f github-mcp-with-vault.yaml
218+
```
219+
220+
Monitor the deployment to ensure both the Vault Agent and ToolHive proxy runner
221+
start successfully:
222+
223+
```bash
224+
# Watch the pod start up
225+
kubectl get pods -n toolhive-system -w
226+
227+
# Get the pod name
228+
POD_NAME=$(kubectl get pods -n toolhive-system \
229+
-l app.kubernetes.io/instance=github-vault \
230+
-o jsonpath="{.items[0].metadata.name}")
231+
232+
# Check pod logs
233+
kubectl logs -n toolhive-system $POD_NAME -c vault-agent
234+
kubectl logs -n toolhive-system $POD_NAME -c toolhive
235+
```
236+
237+
You should see the Vault Agent successfully authenticate and retrieve secrets,
238+
and the ToolHive proxy runner start with the injected environment variables.
239+
240+
## Step 5: Verify the integration
241+
242+
Test that your MCP server has access to the secrets by checking the running pod:
243+
244+
```bash
245+
# Get the proxy pod name - note the instance name is the same
246+
# as the name of our MCPServer
247+
PROXY_POD_NAME=$(kubectl get pods -n toolhive-system \
248+
-l app.kubernetes.io/instance=github-vault \
249+
-o jsonpath="{.items[0].metadata.name}")
250+
251+
# Get the mcp server pod name - note the instance name is the same
252+
# as the name of our MCPServer
253+
MCP_POD_NAME=$(kubectl get pods -ntoolhive-system \
254+
-lapp=github-vault,toolhive-tool-type=mcp \
255+
-ojsonpath='{.items[0].metadata.name}')
256+
257+
# Verify the Vault Agent wrote the secret file
258+
kubectl exec -n toolhive-system "$PROXY_POD_NAME" -c toolhive -- \
259+
cat /vault/secrets/github-config
260+
261+
# Check that the environment variable is available to the MCP server
262+
kubectl get pod $MCP_POD_NAME -n toolhive-system -o jsonpath='{range .spec.containers[?(@.name=="mcp")].env[*]}{.name}{"="}{.value}{"\n"}{end}'
263+
```
264+
265+
## Security best practices
266+
267+
:::tip[Production recommendations]
268+
269+
- Use Vault in production mode with proper TLS certificates
270+
- Implement least-privilege policies for secret access
271+
- Enable audit logging in Vault
272+
- Regularly rotate Vault tokens and secrets
273+
- Monitor Vault Agent logs for authentication issues
274+
- Use namespace isolation for different environments
275+
276+
:::
277+
278+
## Related information
279+
280+
- [Kubernetes quickstart guide](./quickstart-k8s.mdx)
281+
- [Secrets management guide](../guides-cli/secrets-management.mdx)
282+
- [HashiCorp Vault documentation](https://developer.hashicorp.com/vault/docs)
283+
- [Vault Agent Injector documentation][vault-injector]
284+
285+
[vault-hardening]:
286+
https://developer.hashicorp.com/vault/tutorials/operations/production-hardening
287+
[vault-injector]:
288+
https://developer.hashicorp.com/vault/docs/platform/k8s/injector

sidebars.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,7 @@ const sidebars: SidebarsConfig = {
161161
label: 'Quickstart guides',
162162
},
163163
'toolhive/tutorials/custom-registry',
164+
'toolhive/tutorials/vault-integration',
164165
],
165166
},
166167

0 commit comments

Comments
 (0)