Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
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
3 changes: 3 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,9 @@ TRACECAT__BLOB_STORAGE_BUCKET_REGISTRY=tracecat-registry
TRACECAT__BLOB_STORAGE_BUCKET_AGENT=tracecat-agent
# Total request attempts (initial try + retries) for transient S3/MinIO failures.
TRACECAT__BLOB_STORAGE_MAX_ATTEMPTS=5
# Verify TLS certificates when connecting to blob storage (S3/MinIO).
# Set to false for self-hosted S3-compatible storage with self-signed/absent certs.
TRACECAT__BLOB_STORAGE_SSL_VERIFY=true

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Pass the TLS toggle into Docker Compose services

When a self-hosted operator copies .env.example and sets this to false, the provided Compose deployments still won't pass it into the containers: the blob-storage environment blocks in docker-compose.yml enumerate the endpoint and bucket vars but omit this new key (e.g. lines 78-84, 156-162, 237-243, 306-312, and 428-434). Since Compose .env values are only used for interpolation unless listed in a service environment block, tracecat/config.py falls back to true and the new TLS-disabling option has no effect in the documented Docker Compose path. Please thread ${TRACECAT__BLOB_STORAGE_SSL_VERIFY:-true} through each service that talks to blob storage.

Useful? React with 👍 / 👎.


# --- MinIO ---
MINIO_ROOT_USER=minio
Expand Down
67 changes: 66 additions & 1 deletion tests/unit/test_storage_blob.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,12 @@ async def test_get_storage_client_minio_uses_endpoint_and_env(self, monkeypatch)
"http://localhost:9002",
raising=False,
)
monkeypatch.setattr(
blob_module.config,
"TRACECAT__BLOB_STORAGE_SSL_VERIFY",
True,
raising=False,
)
monkeypatch.setenv("AWS_ACCESS_KEY_ID", "minioadmin")
monkeypatch.setenv("AWS_SECRET_ACCESS_KEY", "minioadmin")

Expand All @@ -85,6 +91,7 @@ async def test_get_storage_client_minio_uses_endpoint_and_env(self, monkeypatch)
"s3",
endpoint_url="http://localhost:9002",
config=blob_module._STORAGE_CLIENT_CONFIG,
verify=True,
aws_access_key_id="minioadmin",
aws_secret_access_key="minioadmin",
)
Expand All @@ -98,6 +105,12 @@ async def test_get_storage_client_s3_defaults(self, monkeypatch):
None,
raising=False,
)
monkeypatch.setattr(
blob_module.config,
"TRACECAT__BLOB_STORAGE_SSL_VERIFY",
True,
raising=False,
)

with patch("tracecat.storage.blob.aioboto3.Session") as mock_session_cls:
mock_session = mock_session_cls.return_value
Expand All @@ -107,9 +120,61 @@ async def test_get_storage_client_s3_defaults(self, monkeypatch):
async with get_storage_client() as client:
assert client is mock_client
mock_session.client.assert_called_once_with(
"s3", config=blob_module._STORAGE_CLIENT_CONFIG
"s3", config=blob_module._STORAGE_CLIENT_CONFIG, verify=True
)

@pytest.mark.anyio
async def test_get_storage_client_minio_ssl_verify_disabled(self, monkeypatch):
"""SSL verification can be disabled for self-signed MinIO/S3 endpoints."""
monkeypatch.setattr(
blob_module.config,
"TRACECAT__BLOB_STORAGE_ENDPOINT",
"https://minio.internal:9000",
raising=False,
)
monkeypatch.setattr(
blob_module.config,
"TRACECAT__BLOB_STORAGE_SSL_VERIFY",
False,
raising=False,
)
monkeypatch.setenv("AWS_ACCESS_KEY_ID", "minioadmin")
monkeypatch.setenv("AWS_SECRET_ACCESS_KEY", "minioadmin")

with patch("tracecat.storage.blob.aioboto3.Session") as mock_session_cls:
mock_session = mock_session_cls.return_value
mock_client = AsyncMock()
mock_session.client.return_value.__aenter__.return_value = mock_client

async with get_storage_client() as client:
assert client is mock_client
assert mock_session.client.call_args.kwargs["verify"] is False

@pytest.mark.anyio
async def test_get_storage_client_s3_ssl_verify_disabled(self, monkeypatch):
"""SSL verification flag is honored on the default AWS S3 path too."""
monkeypatch.setattr(
blob_module.config,
"TRACECAT__BLOB_STORAGE_ENDPOINT",
None,
raising=False,
)
monkeypatch.setattr(
blob_module.config,
"TRACECAT__BLOB_STORAGE_SSL_VERIFY",
False,
raising=False,
)

with patch("tracecat.storage.blob.aioboto3.Session") as mock_session_cls:
mock_session = mock_session_cls.return_value
mock_client = AsyncMock()
mock_session.client.return_value.__aenter__.return_value = mock_client

async with get_storage_client() as client:
assert client is mock_client
assert mock_session.client.call_args.kwargs["verify"] is False

@pytest.mark.anyio
@patch("tracecat.storage.blob.get_storage_client")
async def test_upload_file(self, mock_get_client):
Expand Down
8 changes: 8 additions & 0 deletions tracecat/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -456,6 +456,14 @@ def _parse_auth_types() -> set[AuthType]:
TRACECAT__BLOB_STORAGE_ENDPOINT = os.environ.get("TRACECAT__BLOB_STORAGE_ENDPOINT", "")
"""Endpoint URL for blob storage."""

TRACECAT__BLOB_STORAGE_SSL_VERIFY = (
os.environ.get("TRACECAT__BLOB_STORAGE_SSL_VERIFY", "true").lower() == "true"
)
"""Verify TLS certificates when connecting to blob storage (S3/MinIO).

Set to false for self-hosted S3-compatible storage that terminates TLS with a
self-signed or otherwise unverifiable certificate. Defaults to true."""

TRACECAT__BLOB_STORAGE_MAX_ATTEMPTS = int(
os.environ.get("TRACECAT__BLOB_STORAGE_MAX_ATTEMPTS") or 5
)
Expand Down
7 changes: 6 additions & 1 deletion tracecat/storage/blob.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ async def get_storage_client() -> AsyncIterator[S3Client]:
"s3",
endpoint_url=config.TRACECAT__BLOB_STORAGE_ENDPOINT,
config=_STORAGE_CLIENT_CONFIG,
verify=config.TRACECAT__BLOB_STORAGE_SSL_VERIFY,
Comment thread
cubic-dev-ai[bot] marked this conversation as resolved.
Outdated
# Defaults to minio default credentials. MUST REPLACE WITH PRODUCTION CREDENTIALS.
aws_access_key_id=os.environ.get(
"AWS_ACCESS_KEY_ID",
Expand All @@ -73,7 +74,11 @@ async def get_storage_client() -> AsyncIterator[S3Client]:
yield client
else:
# AWS S3 configuration - use AWS credentials from environment or default credential chain
async with session.client("s3", config=_STORAGE_CLIENT_CONFIG) as client:
async with session.client(
"s3",
config=_STORAGE_CLIENT_CONFIG,
verify=config.TRACECAT__BLOB_STORAGE_SSL_VERIFY,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Preserve AWS_CA_BUNDLE when TLS verification is enabled

When TRACECAT__BLOB_STORAGE_SSL_VERIFY is left at its default, this now passes verify=True explicitly instead of leaving botocore's verify argument as None. In botocore 1.40.61, Session.create_client only reads the configured ca_bundle/AWS_CA_BUNDLE when verify is None, so deployments that trust a private S3/MinIO CA via AWS_CA_BUNDLE will start failing TLS validation even though they did not opt out of verification. Please preserve the default None behavior and only pass False when verification is disabled, or otherwise thread through a bundle path.

Useful? React with 👍 / 👎.

) as client:
yield client


Expand Down