Skip to content

ASI-07: Add mTLS and message signing for agent↔gateway communication #4678

Description

@lpcox

Problem

Per the OWASP Agentic Top 10 — ASI-07 (Insecure Multi-Agent Orchestration), communication between agents and tool gateways should be encrypted and signed to prevent tampering.

Current behavior: The MCP gateway communicates with the agent over plain HTTP on localhost:8080 with only an API key for authentication. There is no TLS, no message signing, and no replay protection.

Current Communication Architecture

From mcp_setup_generator.go and mcp_gateway_config.go in gh-aw:

Agent (Copilot/Claude/Codex)
  ↓ HTTP (plain text)
  ↓ Port 8080, host.docker.internal (or localhost)
  ↓ API key in header (MCP_GATEWAY_API_KEY, ~45 chars base64)
  ↓ --network host
MCP Gateway Container (gh-aw-mcpg)
  ↓ HTTP/stdio
  ↓ Per-server env vars (tokens, secrets)
MCP Servers (GitHub, Playwright, custom)

Gateway startup (mcp_setup_generator.go:L500-650):

docker run -i --rm --network host \
  -e MCP_GATEWAY_PORT=8080 \
  -e MCP_GATEWAY_DOMAIN=host.docker.internal \
  -e MCP_GATEWAY_API_KEY=<random-base64> \
  github/gh-aw-mcpg:vX.Y.Z

Secrets passed as env vars (mcp_environment.go:L61-201):

  • GITHUB_MCP_SERVER_TOKEN — GitHub API token
  • GH_AW_SAFE_OUTPUTS — safe outputs config
  • GH_AW_SAFE_OUTPUTS_API_KEY — safe outputs auth
  • GH_AW_MCP_SCRIPTS_API_KEY — MCP scripts auth
  • Custom HTTP MCP header secrets
  • ACTIONS_ID_TOKEN_REQUEST_* — OIDC tokens

All secrets are visible to any process inside the gateway container.

Parent Issue

Part of github/gh-aw#28770 (OWASP Agentic Top 10 Compliance Evaluation)

Proposed Solution

Phase 1: mTLS (Encrypted Channel)

  1. Certificate Generation — Generate ephemeral self-signed CA + client/server certs during the start-mcp-gateway step in gh-aw
  2. Gateway TLS Listener — MCPG accepts TLS connections on port 8080 with server cert, validates client cert
  3. Agent Configuration — Pass client cert + CA to agent engine so HTTP calls use mTLS
  4. Certificate Rotation — Certs are ephemeral per-run (generated at startup, destroyed at teardown)

Phase 2: Message Signing

  1. Request Signing — Agent signs each MCP request with an HMAC (shared secret derived from API key)
  2. Signature Verification — Gateway verifies HMAC before processing any request
  3. Replay Protection — Include timestamp + nonce in signed payload; reject messages older than 30s or with seen nonces
  4. Response Signing — Gateway signs responses so agent can verify integrity

Phase 3: Secret Isolation

  1. Per-Server Secret Scoping — Each MCP server only receives its own secrets (not all env vars)
  2. Secret Masking — Secrets are loaded from encrypted files rather than env vars where possible
  3. Audit Trail — Log (without values) which secrets were accessed by which server

Key MCPG Changes

  • Add TLS server configuration (cert/key loading, client CA verification)
  • Add HMAC signature verification middleware
  • Add nonce/timestamp replay protection
  • Add per-server secret scoping in server process spawning
  • Configuration: MCP_GATEWAY_TLS_CERT, MCP_GATEWAY_TLS_KEY, MCP_GATEWAY_CA_CERT, MCP_GATEWAY_HMAC_SECRET

Key gh-aw Changes (Companion)

Changes needed in github/gh-aw to support this:

  • mcp_setup_generator.go — generate ephemeral certs, pass to Docker via mounts
  • mcp_gateway_config.go — add TLS configuration fields
  • mcp_environment.go — add cert paths to env vars
  • Engine RenderMCPConfig() — include client cert paths in agent MCP config

Acceptance Criteria

  • Gateway accepts TLS connections with client cert verification
  • Ephemeral certs generated per workflow run
  • Request/response HMAC signing with replay protection
  • Per-server secret scoping (servers only see their own secrets)
  • Backward compatible — plain HTTP still works if TLS not configured (for local dev)
  • Integration tests for mTLS handshake and signature verification
  • Performance: < 5ms added latency per MCP call from TLS overhead

Metadata

Metadata

Assignees

Labels

Type

No type

Fields

No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions