Skip to content

Support per-server custom CA trust for HTTP MCP servers - #71229

Merged
pelikhan merged 4 commits into
mainfrom
pelikhan-fuzzy-parakeet
Oct 22, 2026
Merged

pelikhan merged 4 commits into
mainfrom
pelikhan-fuzzy-parakeet

Conversation

@pelikhan

Copy link
Copy Markdown
Collaborator

Summary

Fixes #68033.

  • Add author-facing mcp-servers.<name>.ca-cert-file to the frontmatter/MCP schemas, shared typed configuration, parser, validation, and gateway rendering (caCertFile). Reject non-HTTPS/non-HTTP servers, malformed paths, unsupported expressions, traversal, and gateway versions older than v0.4.35.
  • Resolve repository-relative, absolute, ${{ github.workspace }}/, and ${{ runner.temp }}/ paths after reviewed setup steps. Resolve symlinks, require readable regular files, bind-mount each PEM read-only at a server-specific gateway path, and pass mount arguments directly to Docker without shell interpolation. Works independently of custom sandbox.mcp.mounts.
  • Keep the CA scoped to that server's upstream transport. System trust, hostname verification, other MCP servers, and model-provider trust remain unchanged. No global trust-store updates or verification bypasses. PEM validation stays with MCPG; certificate contents are not logged.
  • Document reviewed secret-to-file provisioning and supported paths. Local gh aw mcp inspect explicitly rejects this gateway-only configuration rather than silently ignoring it.

Gateway compatibility

No version, digest, or workflow-lock upgrade is included. Upstream CA implementation commit 5f5248620cbe7fb1ee155bc3a7b3646d474721a6 is already in both v0.4.35 and the current default v0.4.36. This PR does not depend on #71219 and does not compete with its v0.4.37 upgrade. #69436 was used only as historical proposal context, not treated as shipped functionality.

Coverage and validation

  • Focused Go tests passed across pkg/cli, pkg/parser, and pkg/workflow: valid/invalid CA configuration, typed round-trip, schema compatibility, unsupported local inspection, old gateway rejection, and compiled gateway output for Copilot, Claude, Codex, and Gemini.
  • 181 launcher/helper tests passed, including repository-relative/absolute/workspace/temp paths, symlinks, spaces, file-only read-only mount arguments, distinct per-server destinations, no changes for unconfigured servers, missing/directory/invalid paths, and invalid transport/runtime roots.
  • Four opt-in integration cases passed against a native build of released MCPG v0.4.36: two independent private CAs connect; wrong-CA and unconfigured servers do not inherit trust; hostname mismatch remains rejected; malformed, empty, and private-key PEMs fail without disclosing file contents. The native harness emulates the generated bind source/target mapping; it does not claim to exercise Docker mount enforcement.
  • make build, make fmt, make lint-cjs, JavaScript typecheck, schema freshness, make check-workflow-drift, and serialized golangci-lint run --allow-serial-runners --timeout 10m ./pkg/cli ./pkg/parser ./pkg/types ./pkg/workflow passed (0 Go lint issues).
  • Ran make agent-report-progress once: impacted Go unit tests and impacted JavaScript tests passed. The complete gate remains blocked by pre-existing custom-linter diagnostics on unchanged lines in touched files (including existing builder writes/deferred closes). New diagnostics were fixed; a diff-line comparison of the rerun reports 0 diagnostics on introduced/changed lines, with 45 existing findings on unchanged lines in touched files. Shared Go lint contention was resolved using the linter's serialized-runner option; no shared locks were removed.

Reproduce native TLS coverage after building v0.4.36: GH_AW_MCPG_BINARY=/path/to/awmg npm run test:js -- mcp_server_ca.integration.test.cjs from actions/setup/js. On macOS, use a canonical non-symlink TMPDIR for the existing gateway log-directory safety tests.

Remaining validation limitation

A real Docker bind-mount end-to-end run could not start because the shared Docker daemon reports no space left on device. No shared images/containers were pruned. Native gateway TLS behavior and generated mount/path handling were verified separately. No GitHub Actions workflow was dispatched.

Expose ca-cert-file in custom HTTPS MCP configuration and securely resolve and mount each PEM read-only for the gateway. Preserve system trust and reject unsupported configurations without global TLS changes.

Fixes #68033

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot AI max review requested due to automatic review settings October 22, 2026 21:52
@pelikhan

Copy link
Copy Markdown
Collaborator Author

/review

@github-actions

github-actions Bot commented Oct 22, 2026 •

Copy link
Copy Markdown
Contributor

🧠 Matt Pocock Skills Reviewer has completed the skills-based review. ✅

🧠 Reviewed using Matt Pocock's skills by Matt Pocock Skills Reviewer

@pelikhan

Copy link
Copy Markdown
Collaborator Author

/matt

@github-actions

github-actions Bot commented Oct 22, 2026 •

Copy link
Copy Markdown
Contributor

🧠 Matt Pocock Skills Reviewer has completed the skills-based review. ✅

🧠 Reviewed using Matt Pocock's skills by Matt Pocock Skills Reviewer

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Verdict: Comment

No actionable blocking issues found in the changed lines. The review was static; repository tests were not executed. The advisory first-pass reviewer was unavailable, so this assessment is based on the changed-line analysis.

🔎 Code quality review by PR Code Quality Reviewer · copilot · gpt50 · ~2 AIC · ⌖ ~1 AIC · ⊞ 1.6K
Comment /review to run again

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Skills-Based Review 🧠

Applied /tdd and /codebase-design to this per-server CA trust feature (fixes #68033). The implementation is careful and well-layered: Go-side path validation (no traversal, no shell metacharacters, no expressions beyond the two literal prefixes) happens at compile time; the JS launcher independently re-validates (realpath, regular-file check, readability, comma/newline rejection) before mounting, and the mount target is a deterministic per-server hash path so servers cannot see each other's CA. Good defense in depth across three layers (schema → Go parser → JS launcher).

📋 Key Themes & Highlights

Key Themes

  • One actionable gap: args.splice(1, 0, ...caMountArgs) in start_mcp_gateway.cjs relies on an un-tested assumption about insertion order relative to injectCustomGatewayEnvArgs/injectProxyEnvArgs and the rest of the parsed dockerCommand. See inline comment.

Positive Highlights

  • ✅ ValidateMCPCACertFile rejects .., shell substitutions, and non-literal expressions with a precise regex + traversal check — exactly the right place to stop injection before it reaches a heredoc.
  • ✅ prepareMCPServerCAs resolves symlinks via realpathSync, requires a regular readable file, and never leaks filesystem errors or certificate contents into thrown messages — matches the PR's stated non-goal of not disclosing file contents, and is backed by a real integration test against a native MCPG binary (mcp_server_ca.integration.test.cjs).
  • ✅ Version gating (versionAtLeast(..., "v0.4.35")) prevents silently-ignored config on older gateways, with a dedicated regression test (TestMCPCACertFileOldGateway).
  • ✅ gh aw mcp inspect explicitly rejects ca-cert-file instead of silently connecting without CA trust, avoiding a confusing local/CI behavior mismatch.

No blocking issues found; approving.

🧠 Reviewed using Matt Pocock's skills by Matt Pocock Skills Reviewer · copilot · sonnet50 · ~157 AIC · ⌖ ~16 AIC · ⊞ 10.2K
Comment /matt to run again

Comment thread actions/setup/js/start_mcp_gateway.cjs Resolved

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🔵 Needs a closer look

The security-sensitive Docker mount boundary lacks end-to-end validation and needs final human review.

2 open issues
What changed in this PR

Adds per-server HTTPS CA configuration to gh-aw’s MCP pipeline, addressing #68033 without changing the gateway version pin or global trust settings.

Changes:

  • Adds CA configuration to schemas, shared types, parsing, validation, and gateway rendering.
  • Resolves CA files at startup and creates server-specific, read-only Docker mounts.
  • Documents provisioning, rejects unsupported local inspection, and adds compiler, launcher, and native TLS coverage.
File Description
pkg/​workflow/​tools_types.go Serializes the typed CA field.
pkg/​workflow/​schemas/​mcp-gateway-config.schema.json Adds the gateway CA property.
pkg/​workflow/​mcp_setup_gateway.go Enforces gateway version compatibility.
pkg/​workflow/​mcp_property_validation.go Validates CA configuration requirements.
pkg/​workflow/​mcp_config_validation.go Recognizes the author-facing CA key.
pkg/​workflow/​mcp_config_custom.go Parses and renders CA paths.
pkg/​workflow/​mcp_ca_test.go Covers compilation, validation, and serialization.
pkg/​types/​README.md Documents the shared CA field.
pkg/​types/​mcp.go Adds the shared typed CA field.
pkg/​parser/​schemas/​mcp_config_schema.json Adds CA transport constraints.
pkg/​parser/​schemas/​main_workflow_schema.json Exposes the workflow CA option.
pkg/​parser/​mcp.go Integrates CA parsing.
pkg/​parser/​mcp_ca.go Validates paths and HTTPS requirements.
pkg/​parser/​mcp_ca_test.go Covers accepted and rejected configurations.
pkg/​cli/​mcp_inspect_mcp.go Rejects gateway-only CA configuration locally.
pkg/​cli/​mcp_inspect_ca_test.go Tests the inspection rejection.
docs/​src/​content/​docs/​reference/​tools.md Documents CA paths and provisioning.
docs/​src/​content/​docs/​reference/​mcp-gateway.md Describes the gateway CA field.
docs/​public/​schemas/​mcp-gateway-config.schema.json Publishes the gateway schema addition.
actions/​setup/​js/​start_mcp_gateway.cjs Integrates CA preparation and mount arguments.
actions/​setup/​js/​mcp_server_ca.test.cjs Tests path resolution and mount generation.
actions/​setup/​js/​mcp_server_ca.integration.test.cjs Adds native TLS isolation tests.
actions/​setup/​js/​mcp_server_ca.cjs Resolves files and prepares read-only mounts.

Review effort: Max

Comment thread pkg/workflow/tools_types.go Resolved
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Skills-Based Review 🧠

Applied /tdd and /grill-with-docs per pr-triage classification (new_feature). Approving — this is a thorough, security-conscious feature with one low-severity note left inline.

📋 Key Themes & Highlights

Key Themes

  • Defense in depth is real, not decorative: author-facing regex/path validation (pkg/parser/mcp_ca.go), JSON Schema conditionals, and runtime JS validation (mcp_server_ca.cjs) each independently reject unsafe input — no single layer is load-bearing.
  • Heredoc escaping verified correct: renderMCPScalarProperty's \${GITHUB_WORKSPACE} / \${RUNNER_TEMP} substitution looked suspicious at first (a raw \$ is not valid JSON), but I confirmed empirically that the unquoted bash heredoc in mcp_renderer.go strips the leading backslash before the gateway's JSON.parse runs, matching the existing ReplaceTemplateExpressionsWithEnvVars pattern (secret_extraction.go) and the TestMCPCACertFileCompilation assertion. This is correct and consistent, not a bug.
  • Version gating is sound: generateMCPGatewaySetup correctly fails fast with an actionable error when ca-cert-file is set but sandbox.mcp.version predates v0.4.35.
  • One speculative-generality nit (left inline, /codebase-design): the new ca_cert_file TOML property-order branch in determineMCPPropertyOrder has no current production caller using Format: "toml" for HTTP tools — worth a comment or a test confirming the intended future caller.

Positive Highlights

  • ✅ Excellent test coverage across all three validation layers, including adversarial cases ($(touch injected), path traversal, comma/newline injection, non-HTTPS URLs) — strong /tdd evidence.
  • ✅ Docs (tools.md) are clear, show both the direct-path and secret-provisioning patterns, and explicitly state what this feature does not do (no global trust store changes, no private keys) — strong /grill-with-docs evidence.
  • ✅ Docker --mount args are spliced into argv at a safe fixed index (args.splice(1, 0, ...)), never built via string concatenation — avoids shell-injection risk.
  • ✅ Honest PR description about the integration test's Docker-mount-enforcement limitation (environment constraint, already disclosed).

🧠 Reviewed using Matt Pocock's skills by Matt Pocock Skills Reviewer · copilot · sonnet50 · ~226 AIC · ⌖ ~16 AIC · ⊞ 10.2K
Comment /matt to run again

Comment thread pkg/workflow/mcp_config_custom.go Resolved
pelikhan and others added 2 commits October 22, 2026 15:21
Retain exact-version and SHA-256 requirements while allowing hash-locked packages to list multiple wheel artifacts. Cover malformed and unhashed entries with regression cases.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Preserve typed CA paths through raw configuration parsing and updates, exercise final Docker spawn arguments with proxy settings, and clarify workspace-relative paths and forward-looking TOML rendering.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
@pelikhan

Copy link
Copy Markdown
Collaborator Author

In response to #71229 (comment): This is a review completion notice and needs no separate code change. The associated launcher-argument coverage feedback has been addressed.

@pelikhan

Copy link
Copy Markdown
Collaborator Author

In response to #71229 (comment): The /matt command was already served. The resulting feedback has been addressed, including clarification that HTTP TOML rendering is forward-looking; no additional run was needed.

@pelikhan

Copy link
Copy Markdown
Collaborator Author

In response to #71229 (comment): This status notice has since been updated to confirm review completion and requests no separate code change. Its associated TOML-rendering feedback has been addressed.

@pelikhan

Copy link
Copy Markdown
Collaborator Author

In response to #71229 (comment): The /review command was already served. Its actionable feedback has been addressed in this batch; no additional review run or workflow dispatch was needed.

@pelikhan
pelikhan merged commit 43bf809 into main Oct 22, 2026
85 checks passed
@pelikhan
pelikhan deleted the pelikhan-fuzzy-parakeet branch October 22, 2026 22:34
@github-actions

Copy link
Copy Markdown
Contributor

🎉 This pull request is included in a new release.

Release: v0.93.1

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

2 participants