Skip to content

Commit 2efca30

Browse files
ChrisJBurnsclaude
andauthored
Surface unauthenticated proxy default in logs and docs (#5488)
* Surface unauthenticated proxy default in logs and docs When an MCPServer or MCPRemoteProxy is deployed without OIDCConfigRef (or any other auth source), the proxy falls back to LocalUserMiddleware and forwards every request under a synthetic local-user identity with no credential check. This unauthenticated fallback is intentional, but it was silent: the only signal was a debug log, and the README claimed unconditional "identity enforcement per request" (GHSA-hfrv-94x5-85p2). Make the state visible without changing the default behavior: - Raise the no-auth fallback log in GetAuthenticationMiddleware from Debug to Warn, naming the consequence and how to enable auth. - Document the unauthenticated default on the OIDCConfigRef field of both MCPServer and MCPRemoteProxy; regenerate the CRD API reference. - Reword the README so identity enforcement is stated as conditional on configuring an authentication source. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * Correct crdref-gen working directory in operator rule The rule said to run `task crdref-gen` from `cmd/thv-operator/`, but the task resolves its config path relative to the repo root and fails from that directory. Document the correct invocation. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * Regenerate operator CRD manifests Propagate the OIDCConfigRef security note into the generated CRD YAML descriptions for MCPServer and MCPRemoteProxy. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 64e2e4d commit 2efca30

10 files changed

Lines changed: 68 additions & 8 deletions

File tree

.claude/rules/operator.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ See `cmd/thv-operator/DESIGN.md` for detailed decision guidelines.
3636

3737
- Always run `task operator-generate` after modifying CRD types
3838
- Always run `task operator-manifests` after adding kubebuilder markers
39-
- Always run `task crdref-gen` from `cmd/thv-operator/` after CRD changes to regenerate API docs (uses relative paths)
39+
- Always run `task crdref-gen` from the repo root after CRD changes to regenerate API docs (running it from `cmd/thv-operator/` fails — the task resolves the config path relative to the repo root)
4040
- Use `envtest` for integration testing, not real clusters
4141
- Chainsaw tests require a real Kubernetes cluster
4242
- Status writes must go through `controllerutil.MutateAndPatchStatus` — see the Status Writes section below
@@ -65,7 +65,7 @@ task operator-generate # Generate deepcopy, client code
6565
task operator-manifests # Generate CRD YAML, RBAC
6666
task operator-test # Run unit tests
6767
task operator-e2e-test # Run e2e tests
68-
task crdref-gen # Generate CRD API docs (run from cmd/thv-operator/)
68+
task crdref-gen # Generate CRD API docs (run from the repo root)
6969
```
7070

7171
## Spec / metadata patching

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414

1515
## Run any MCP server securely, instantly, anywhere.
1616

17-
ToolHive runs every MCP server in an isolated container, enforces identity and access policy per request, and gives platform teams the observability they need to put MCP in production.
17+
ToolHive runs every MCP server in an isolated container, enforces identity and access policy per request when configured with an authentication source, and gives platform teams the observability they need to put MCP in production.
1818

1919
## Why ToolHive?
2020

@@ -29,7 +29,7 @@ Here are some of the more common use cases for ToolHive:
2929
<tr valign="top">
3030
<td>Connect Claude Code, Cursor, GitHub Copilot, or your preferred client to MCP servers with a single click or command.<br><br>
3131
ToolHive wraps every MCP server in an isolated container with a minimal permission file (no local credentials) and uses semantic tool search to reduce your token usage by up to 85%.</td>
32-
<td>Put an end to shadow MCP use by your developers, and give your security team the audit logs and identity enforcement they require.<br><br>
32+
<td>Put an end to shadow MCP use by your developers, and give your security team the audit logs and configurable identity enforcement they require.<br><br>
3333
ToolHive includes a Kubernetes operator, so you can declare policies, integrate with your IdP and observability stack, emit OTel traces, and more … all with familiar tools and patterns.</td>
3434
<td>Most MCP solutions are SaaS, but your compliance requirements prohibit sensitive info from being processed by SaaS providers.<br><br>
3535
ToolHive is the exception that allows you to self-host your MCP registry, gateway, etc. You can pilot the entire platform, and when you’re ready to scale, Stacklok’s got the added capabilities and expert team ready!</td>

cmd/thv-operator/api/v1beta1/mcpremoteproxy_types.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,12 @@ type MCPRemoteProxySpec struct {
5757
// The referenced MCPOIDCConfig must exist in the same namespace as this MCPRemoteProxy.
5858
// Per-server overrides (audience, scopes) are specified here; shared provider config
5959
// lives in the MCPOIDCConfig resource.
60+
//
61+
// SECURITY: if this field is omitted and no other authentication source is configured,
62+
// the proxy runs UNAUTHENTICATED. It accepts every request that can reach its port and
63+
// forwards it to the remote MCP server under a synthetic local-user identity, with no
64+
// token or credential check. Set this field to enforce identity-based access control
65+
// per request.
6066
// +optional
6167
OIDCConfigRef *MCPOIDCConfigReference `json:"oidcConfigRef,omitempty"`
6268

cmd/thv-operator/api/v1beta1/mcpserver_types.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -290,6 +290,11 @@ type MCPServerSpec struct {
290290
// The referenced MCPOIDCConfig must exist in the same namespace as this MCPServer.
291291
// Per-server overrides (audience, scopes) are specified here; shared provider config
292292
// lives in the MCPOIDCConfig resource.
293+
//
294+
// SECURITY: if this field is omitted and no other authentication source is configured,
295+
// the proxy runs UNAUTHENTICATED. It accepts every request that can reach its port and
296+
// forwards it to the MCP server under a synthetic local-user identity, with no token or
297+
// credential check. Set this field to enforce identity-based access control per request.
293298
// +optional
294299
OIDCConfigRef *MCPOIDCConfigReference `json:"oidcConfigRef,omitempty"`
295300

deploy/charts/operator-crds/files/crds/toolhive.stacklok.dev_mcpremoteproxies.yaml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -291,6 +291,12 @@ spec:
291291
The referenced MCPOIDCConfig must exist in the same namespace as this MCPRemoteProxy.
292292
Per-server overrides (audience, scopes) are specified here; shared provider config
293293
lives in the MCPOIDCConfig resource.
294+
295+
SECURITY: if this field is omitted and no other authentication source is configured,
296+
the proxy runs UNAUTHENTICATED. It accepts every request that can reach its port and
297+
forwards it to the remote MCP server under a synthetic local-user identity, with no
298+
token or credential check. Set this field to enforce identity-based access control
299+
per request.
294300
properties:
295301
audience:
296302
description: |-
@@ -910,6 +916,12 @@ spec:
910916
The referenced MCPOIDCConfig must exist in the same namespace as this MCPRemoteProxy.
911917
Per-server overrides (audience, scopes) are specified here; shared provider config
912918
lives in the MCPOIDCConfig resource.
919+
920+
SECURITY: if this field is omitted and no other authentication source is configured,
921+
the proxy runs UNAUTHENTICATED. It accepts every request that can reach its port and
922+
forwards it to the remote MCP server under a synthetic local-user identity, with no
923+
token or credential check. Set this field to enforce identity-based access control
924+
per request.
913925
properties:
914926
audience:
915927
description: |-

deploy/charts/operator-crds/files/crds/toolhive.stacklok.dev_mcpservers.yaml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -288,6 +288,11 @@ spec:
288288
The referenced MCPOIDCConfig must exist in the same namespace as this MCPServer.
289289
Per-server overrides (audience, scopes) are specified here; shared provider config
290290
lives in the MCPOIDCConfig resource.
291+
292+
SECURITY: if this field is omitted and no other authentication source is configured,
293+
the proxy runs UNAUTHENTICATED. It accepts every request that can reach its port and
294+
forwards it to the MCP server under a synthetic local-user identity, with no token or
295+
credential check. Set this field to enforce identity-based access control per request.
291296
properties:
292297
audience:
293298
description: |-
@@ -1205,6 +1210,11 @@ spec:
12051210
The referenced MCPOIDCConfig must exist in the same namespace as this MCPServer.
12061211
Per-server overrides (audience, scopes) are specified here; shared provider config
12071212
lives in the MCPOIDCConfig resource.
1213+
1214+
SECURITY: if this field is omitted and no other authentication source is configured,
1215+
the proxy runs UNAUTHENTICATED. It accepts every request that can reach its port and
1216+
forwards it to the MCP server under a synthetic local-user identity, with no token or
1217+
credential check. Set this field to enforce identity-based access control per request.
12081218
properties:
12091219
audience:
12101220
description: |-

deploy/charts/operator-crds/templates/toolhive.stacklok.dev_mcpremoteproxies.yaml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -294,6 +294,12 @@ spec:
294294
The referenced MCPOIDCConfig must exist in the same namespace as this MCPRemoteProxy.
295295
Per-server overrides (audience, scopes) are specified here; shared provider config
296296
lives in the MCPOIDCConfig resource.
297+
298+
SECURITY: if this field is omitted and no other authentication source is configured,
299+
the proxy runs UNAUTHENTICATED. It accepts every request that can reach its port and
300+
forwards it to the remote MCP server under a synthetic local-user identity, with no
301+
token or credential check. Set this field to enforce identity-based access control
302+
per request.
297303
properties:
298304
audience:
299305
description: |-
@@ -913,6 +919,12 @@ spec:
913919
The referenced MCPOIDCConfig must exist in the same namespace as this MCPRemoteProxy.
914920
Per-server overrides (audience, scopes) are specified here; shared provider config
915921
lives in the MCPOIDCConfig resource.
922+
923+
SECURITY: if this field is omitted and no other authentication source is configured,
924+
the proxy runs UNAUTHENTICATED. It accepts every request that can reach its port and
925+
forwards it to the remote MCP server under a synthetic local-user identity, with no
926+
token or credential check. Set this field to enforce identity-based access control
927+
per request.
916928
properties:
917929
audience:
918930
description: |-

deploy/charts/operator-crds/templates/toolhive.stacklok.dev_mcpservers.yaml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -291,6 +291,11 @@ spec:
291291
The referenced MCPOIDCConfig must exist in the same namespace as this MCPServer.
292292
Per-server overrides (audience, scopes) are specified here; shared provider config
293293
lives in the MCPOIDCConfig resource.
294+
295+
SECURITY: if this field is omitted and no other authentication source is configured,
296+
the proxy runs UNAUTHENTICATED. It accepts every request that can reach its port and
297+
forwards it to the MCP server under a synthetic local-user identity, with no token or
298+
credential check. Set this field to enforce identity-based access control per request.
294299
properties:
295300
audience:
296301
description: |-
@@ -1208,6 +1213,11 @@ spec:
12081213
The referenced MCPOIDCConfig must exist in the same namespace as this MCPServer.
12091214
Per-server overrides (audience, scopes) are specified here; shared provider config
12101215
lives in the MCPOIDCConfig resource.
1216+
1217+
SECURITY: if this field is omitted and no other authentication source is configured,
1218+
the proxy runs UNAUTHENTICATED. It accepts every request that can reach its port and
1219+
forwards it to the MCP server under a synthetic local-user identity, with no token or
1220+
credential check. Set this field to enforce identity-based access control per request.
12111221
properties:
12121222
audience:
12131223
description: |-

docs/operator/crd-api.md

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pkg/auth/utils.go

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,10 @@ func ExtractBearerToken(r *http.Request) (string, error) {
6060
}
6161

6262
// GetAuthenticationMiddleware returns the appropriate authentication middleware based on the configuration.
63-
// If OIDC config is provided, it returns JWT middleware. Otherwise, it returns local user middleware.
63+
// If OIDC config is provided, it returns JWT validation middleware. Otherwise it returns local-user
64+
// middleware and logs a WARNING: with no OIDC config, the proxy accepts every request and forwards it
65+
// under a synthetic local-user identity with no token or credential check. This unauthenticated fallback
66+
// is intended for local/development use; configure an OIDC provider to enforce authentication per request.
6467
func GetAuthenticationMiddleware(ctx context.Context, oidcConfig *TokenValidatorConfig, opts ...TokenValidatorOption,
6568
) (func(http.Handler) http.Handler, http.Handler, error) {
6669
if oidcConfig != nil {
@@ -76,7 +79,9 @@ func GetAuthenticationMiddleware(ctx context.Context, oidcConfig *TokenValidator
7679
return jwtValidator.Middleware, authInfoHandler, nil
7780
}
7881

79-
slog.Debug("oidc validation disabled, using local user authentication")
82+
slog.Warn("no authentication configured: the proxy will accept every request unauthenticated and " +
83+
"assign each a synthetic local-user identity with no token or credential check; " +
84+
"configure an OIDC provider to enforce authentication per request")
8085

8186
// Get current OS user
8287
currentUser, err := user.Current()

0 commit comments

Comments
 (0)