Skip to content

broker: passthrough unmatched hosts by default with per-vault deny opt-in#146

Merged
dangtony98 merged 7 commits intomainfrom
unmatched-host-policy
May 2, 2026
Merged

broker: passthrough unmatched hosts by default with per-vault deny opt-in#146
dangtony98 merged 7 commits intomainfrom
unmatched-host-policy

Conversation

@dangtony98
Copy link
Copy Markdown
Contributor

Summary

  • The MITM proxy used to reject every request whose host wasn't in the vault's service allowlist (403 no_match), so a fresh install denied all traffic until an operator hand-curated services. This flips the default: unmatched hosts now forward as plain proxy traffic, with credentials only injected when a service matches.
  • New per-vault unmatched_host_policy setting (passthrough default, deny opt-in) lets vault admins restore the strict allowlist behavior — exposed via GET/PATCH /v1/vaults/{name}/settings and a Strict deny mode toggle in the Settings tab.
  • Matched-host hot path takes zero added work; the policy lookup only fires on the unmatched-host branch and fails closed on store errors.

Implementation notes

  • Generic per-vault key/value table (vault_settings, migration 043) so future toggles fit without schema churn.
  • brokercore.UnmatchedHostPolicy typed enum + IsPassthrough flag on InjectResult; the existing Auth.Type=="passthrough" matched-service branch already produced the same shape, so forward.go only needed a one-liner to thread the flag into the audit log.
  • Auth-gate prelude shared by rename / delete / settings handlers extracted to resolveVaultForAdminOrOwner; setting read-validate-default extracted to readUnmatchedHostPolicy.
  • Skill docs (cmd/skill_{cli,http}.md) updated as a pair — agents now know 403 forbidden only fires under deny mode.

Operator-visible behavior change: existing vaults flip to passthrough on upgrade. Set unmatched_host_policy=deny per vault to restore the previous strict behavior.

Test plan

  • make test — all packages green, including new brokercore policy tests, mitm passthrough integration test, and settings-handler tests
  • go build ./... and tsc --noEmit clean
  • Smoke: ./agent-vault run --vault default with no services configured — curl --proxy https://localhost:14322 https://example.com succeeds (was 403)
  • Smoke: toggle Strict deny mode in the UI; the same curl now 403s with proposal_hint; toggle off and confirm passthrough resumes
  • Smoke: confirm request-log rows for passthrough requests show empty matched_service, status 200, and the new passthrough=true slog field

…t-in

The MITM proxy used to deny every request whose target host wasn't in the
vault's service allowlist, which made a fresh install reject all traffic
until an operator hand-curated services. Flip the default: unmatched
hosts now forward as plain proxy traffic, and a new per-vault
`unmatched_host_policy=deny` setting restores the strict allowlist for
operators who want it.

Adds a generic vault_settings key/value table (migration 043), a
GET/PATCH /v1/vaults/{name}/settings endpoint gated on vault admin or
instance owner, a Strict deny mode toggle in the vault Settings tab, and
paired updates to skill_cli.md / skill_http.md so agents understand 403
forbidden only fires under deny mode now. The matched-host hot path adds
zero work; the policy lookup only fires on the unmatched-host branch and
fails closed on store errors.
@mintlify
Copy link
Copy Markdown

mintlify Bot commented May 2, 2026

Preview deployment for your docs. Learn more about Mintlify Previews.

Project Status Preview Updated (UTC)
agent-vault 🟢 Ready View Preview May 2, 2026, 4:37 AM

💡 Tip: Enable Workflows to automatically generate PRs for you.

@infisical-review-police
Copy link
Copy Markdown

💬 Discussion in Slack: #pr-review-agent-vault-146-broker-passthrough-unmatched-hosts-by-default-with-per

Posted by Review Police — reviews, comments, new commits, and CI failures will stream into this channel.

Comment thread internal/brokercore/credential.go Outdated
Comment thread internal/server/handle_vaults.go
Comment thread web/src/pages/vault/SettingsTab.tsx
dangtony98 added 5 commits May 2, 2026 10:25
- credential.go: a transient GetBrokerConfig error was silently treated
  as "no row" and fell through to the unmatched-host policy, which under
  the new passthrough default would forward an upstream-credentialed
  request without injection — a fail-closed -> fail-open regression.
  Distinguish sql.ErrNoRows from real errors and fail closed on the
  latter, mirroring the policy-lookup branch a few lines below.
- handle_vaults.go (PATCH): echo the validated input on the write path
  instead of unconditionally re-reading. A transient read failure after
  a committed write would 500, which the optimistic UI rolls back —
  desyncing the displayed policy from the persisted one. The read still
  fires for no-op PATCHes.
- handle_vaults.go (GET): relax to vault-member scope so non-admin
  members see the real policy in the Settings tab. Previously the GET
  required admin/owner and the frontend silently fell back to
  passthrough on the resulting 403, misrepresenting an enforcement
  control. PATCH stays admin/owner-only.
- Tests: assert fail-closed on a non-ErrNoRows GetBrokerConfig error,
  PATCH-response echo, and the member-can-GET / member-cannot-PATCH
  split.
Pulls the unmatched-host policy toggle into the same bordered card as
the rename row, separated by a horizontal rule so the two controls share
visual width and the page reads as one config card. Description trimmed
to a single sentence — the wording belongs in the docs, not the chrome.
The rename row is bounded by its input's max-w-xs plus the Rename
button (~max-w-md total). Wrap the toggle row in a max-w-md inner box
so the description and Toggle sit within the same visual band, instead
of stretching to the full card width.
…-deny label

Wraps both the rename row and the strict-deny row in a single max-w-md
box so the Rename button and Toggle align flush at the right edge.
Switches the strict-deny heading to the same uppercase label style as
the FormField above it for visual consistency.
@dangtony98
Copy link
Copy Markdown
Contributor Author

@claude review

Copy link
Copy Markdown

@claude claude Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixes in cbc40cd look good — all three concerns addressed correctly with regression tests. Deferring to a human for final approval since this changes the default proxy behavior (existing vaults flip to passthrough on upgrade) and touches security-sensitive credential broker paths.

Extended reasoning...

Overview

PR flips the MITM proxy default from "deny unmatched hosts" to "passthrough", with a new per-vault unmatched_host_policy setting (passthrough/deny) exposed via GET/PATCH /v1/vaults/{name}/settings and a Strict deny mode toggle. New vault_settings table (migration 043), new InjectResult.Passthrough flag plumbed through the audit log, and an extracted resolveVaultForAdminOrOwner auth prelude shared with rename/delete handlers.

Security risks

This is a fail-closed → fail-open default change for a credential broker — exactly the class of decision that benefits from a human signing off. Specific risks I scrutinized:

  • Inject() error handling: now correctly distinguishes sql.ErrNoRows (fall through to policy) from real errors (ErrServiceNotFound), with a new regression test TestInject_GetBrokerConfigError_FailsClosed. Matches my prior concern.
  • Settings PATCH: write path echoes the validated input rather than re-reading post-write — eliminates the UI/DB desync window on transient read failures.
  • Settings GET: relaxed from admin-only to vault-member, so non-admin members see the real policy (toggle stays disabled via canManage); PATCH retains admin/owner gating. New test asserts both halves.
  • Substitution and credential resolution paths are untouched on the matched-host hot path; passthrough leaves Headers=nil so no header injection happens for unmatched hosts.

Level of scrutiny

High. The PR is operator-visible (default behavior flips on upgrade), modifies the MITM credential injection path, adds a new admin-gated mutation endpoint, and introduces a new auth helper that several handlers now depend on. Not a candidate for shadow approval even with my prior concerns resolved.

Other factors

  • All three of my previous review comments resolved with corresponding tests; no outstanding feedback in the timeline.
  • Test coverage looks proportional: brokercore unit tests for both policies, MITM integration test for passthrough forwarding, server tests for GET/PATCH including the non-admin member case.

@dangtony98 dangtony98 merged commit 7a107de into main May 2, 2026
10 checks passed
@dangtony98 dangtony98 deleted the unmatched-host-policy branch May 2, 2026 15:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant