Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions docs/docs/documentation/components/proxies.md
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,36 @@ The proxy caches OAuth tokens and automatically refreshes them when they expire.
Always use environment variables for sensitive credentials. Never commit tokens or secrets directly in configuration files.
:::

## SSL Certificate Verification

By default, the proxy verifies SSL certificates when connecting to downstream agents over HTTPS. You can disable certificate verification for specific agents using the `ssl_verify` configuration option.

### When to Disable SSL Verification

Disable SSL verification (`ssl_verify: false`) only in these scenarios:

- **Development/Testing**: Connecting to agents with self-signed certificates in non-production environments
- **Internal Infrastructure**: Agents running on internal networks with private CA certificates not trusted by the system

:::warning[Security Warning]
Disabling SSL verification removes protection against man-in-the-middle attacks. Never disable SSL verification in production environments unless you fully understand the security implications.
:::

### Configuration

```yaml
proxied_agents:
- name: "production-agent"
url: "https://api.example.com/agent"
ssl_verify: true # Default - verify against system CAs

- name: "dev-agent-self-signed"
url: "https://dev.internal.example.com/agent"
ssl_verify: false # Disable verification for self-signed certs
```

The `ssl_verify` setting applies to both agent card fetching and task invocations for the configured agent.

## Custom HTTP Headers

The proxy supports custom HTTP headers for both agent card fetching and A2A task invocations. This is useful for scenarios like API versioning, tenant identification, custom authentication schemes, or any other header-based requirements.
Expand Down
6 changes: 6 additions & 0 deletions examples/a2a_proxy_example.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -60,3 +60,9 @@ apps:
# request_timeout_seconds: 180
# # use_agent_card_url: false # If true (default), uses URL from agent card for tasks.
# # If false, uses configured URL directly for all calls.

# Example 3: Agent with self-signed certificate (development/testing)
# - name: "DevAgent"
# url: "https://dev.internal.example.com/agent"
# ssl_verify: false # Disable SSL verification for self-signed certs
# # WARNING: Only use in development/testing environments
19 changes: 18 additions & 1 deletion src/solace_agent_mesh/agent/proxies/a2a/component.py
Original file line number Diff line number Diff line change
Expand Up @@ -627,8 +627,16 @@ async def _fetch_agent_card(
else:
log.debug("%s Fetching agent card without authentication", log_identifier)

ssl_verify = agent_config.get("ssl_verify", True)
if not ssl_verify:
log.warning(
"%s SSL verification disabled for agent '%s'. "
"This should only be used in development environments.",
log_identifier,
agent_name,
)
log.info("%s Fetching agent card from %s", log_identifier, agent_url)
async with httpx.AsyncClient(headers=headers) as client:
async with httpx.AsyncClient(headers=headers, verify=ssl_verify) as client:
resolver = A2ACardResolver(httpx_client=client, base_url=agent_url, agent_card_path=agent_card_path)
agent_card = await resolver.get_agent_card()
return agent_card
Expand Down Expand Up @@ -1284,6 +1292,14 @@ async def _get_or_create_a2a_client(

# Create a new httpx client with the specific timeout and custom headers for this agent
# httpx.Timeout requires explicit values for connect, read, write, and pool
ssl_verify = agent_config.get("ssl_verify", True)
if not ssl_verify:
log.warning(
"%s SSL verification disabled for agent '%s'. "
"This should only be used in development environments.",
self.log_identifier,
agent_name,
)
httpx_client_for_agent = httpx.AsyncClient(
timeout=httpx.Timeout(
connect=agent_timeout,
Expand All @@ -1292,6 +1308,7 @@ async def _get_or_create_a2a_client(
pool=agent_timeout,
),
headers=task_headers if task_headers else None,
verify=ssl_verify,
)

if task_headers:
Expand Down
5 changes: 5 additions & 0 deletions src/solace_agent_mesh/agent/proxies/a2a/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,11 @@ class A2AProxiedAgentConfig(ProxiedAgentConfig):
"task_headers cannot override authentication headers. For custom authentication, "
"omit the 'authentication' config and use task_headers to set auth headers directly.",
)
ssl_verify: bool = Field(
default=True,
description="SSL certificate verification. Set to False to disable "
"verification for self-signed certificates.",
)
convert_progress_updates: bool = Field(
default=True,
description="If true, converts TextPart messages in intermediate TaskStatusUpdateEvents "
Expand Down
99 changes: 95 additions & 4 deletions tests/unit/agent/proxies/a2a/test_config.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,102 @@
"""
Unit tests for A2A proxy configuration models with separate authentication.
"""
"""Unit tests for A2A proxy configuration models."""

import pytest
from pydantic import ValidationError

from solace_agent_mesh.agent.proxies.a2a.config import A2AProxiedAgentConfig
from solace_agent_mesh.agent.proxies.a2a.config import (
A2AProxiedAgentConfig,
A2AProxyAppConfig,
)


class TestA2AProxiedAgentConfigSSLVerify:
"""Tests for the ssl_verify configuration option."""

def test_ssl_verify_defaults_to_true(self):
"""ssl_verify should default to True when not specified."""
config = A2AProxiedAgentConfig(
name="test-agent",
url="https://example.com/agent",
)
assert config.ssl_verify is True

def test_ssl_verify_can_be_set_to_false(self):
"""ssl_verify can be explicitly set to False."""
config = A2AProxiedAgentConfig(
name="test-agent",
url="https://example.com/agent",
ssl_verify=False,
)
assert config.ssl_verify is False

def test_ssl_verify_can_be_set_to_true(self):
"""ssl_verify can be explicitly set to True."""
config = A2AProxiedAgentConfig(
name="test-agent",
url="https://example.com/agent",
ssl_verify=True,
)
assert config.ssl_verify is True

def test_ssl_verify_with_http_url(self):
"""ssl_verify setting is accepted even with HTTP URLs."""
config = A2AProxiedAgentConfig(
name="test-agent",
url="http://localhost:8080/agent",
ssl_verify=False,
)
assert config.ssl_verify is False

def test_ssl_verify_in_full_config(self):
"""ssl_verify works alongside other configuration options."""
config = A2AProxiedAgentConfig(
name="test-agent",
url="https://example.com/agent",
ssl_verify=False,
request_timeout_seconds=120,
use_auth_for_agent_card=True,
)
assert config.ssl_verify is False
assert config.request_timeout_seconds == 120
assert config.use_auth_for_agent_card is True


class TestA2AProxyAppConfigSSLVerify:
"""Tests for ssl_verify in the full app configuration."""

def test_proxied_agent_with_ssl_verify_false(self):
"""Full app config can include agents with ssl_verify=False."""
config = A2AProxyAppConfig(
namespace="test/namespace",
proxied_agents=[
{
"name": "secure-agent",
"url": "https://secure.example.com/agent",
"ssl_verify": True,
},
{
"name": "self-signed-agent",
"url": "https://self-signed.example.com/agent",
"ssl_verify": False,
},
],
)
assert len(config.proxied_agents) == 2
assert config.proxied_agents[0].ssl_verify is True
assert config.proxied_agents[1].ssl_verify is False

def test_proxied_agent_ssl_verify_defaults(self):
"""Agents without ssl_verify specified should default to True."""
config = A2AProxyAppConfig(
namespace="test/namespace",
proxied_agents=[
{
"name": "default-agent",
"url": "https://example.com/agent",
},
],
)
assert config.proxied_agents[0].ssl_verify is True


class TestSeparateAuthenticationFields:
Expand Down
Loading