Disable connector transports without peer pinning

This commit is contained in:
2026-07-21 15:38:09 +02:00
parent 15ade8df75
commit d5d0df792b
8 changed files with 119 additions and 31 deletions

View File

@@ -98,24 +98,24 @@ class S3StorageBackend:
@property
def client(self):
try:
import boto3
except ModuleNotFoundError as exc:
raise StorageBackendError("boto3 is required for the S3 storage backend") from exc
try:
endpoint_url = validate_unpinned_sdk_http_url(
self.endpoint_url,
label="File storage S3 endpoint",
)
return boto3.client(
"s3",
endpoint_url=endpoint_url,
region_name=self.region_name,
aws_access_key_id=self.access_key_id,
aws_secret_access_key=self.secret_access_key,
)
except OutboundHttpError as exc:
raise StorageBackendError(str(exc)) from exc
try:
import boto3
except ModuleNotFoundError as exc:
raise StorageBackendError("boto3 is required for the S3 storage backend") from exc
return boto3.client(
"s3",
endpoint_url=endpoint_url,
region_name=self.region_name,
aws_access_key_id=self.access_key_id,
aws_secret_access_key=self.secret_access_key,
)
def put_bytes(self, key: str, data: bytes, *, content_type: str | None = None) -> None:
max_bytes = response_limit("file")

View File

@@ -15,7 +15,6 @@ from defusedxml import ElementTree as SafeElementTree
from govoplan_core.security.outbound_http import (
OutboundHttpError,
outbound_http_policy,
pinned_outbound_hostname,
validate_unpinned_sdk_http_url,
)
@@ -312,24 +311,25 @@ def _browse_s3(profile: ConnectorProfile, *, path: str, library_id: str | None,
def _s3_client(profile: ConnectorProfile) -> Any:
if profile.secret_ref:
raise ConnectorBrowseError("Secret-ref S3 credentials need a runtime secret resolver before live browsing")
try:
boto3 = import_module("boto3")
config_module = import_module("botocore.config")
except ImportError as exc:
raise ConnectorBrowseUnsupported("S3 connector browsing requires the optional boto3 dependency") from exc
kwargs: dict[str, object] = {}
if profile.endpoint_url:
try:
kwargs["endpoint_url"] = validate_unpinned_sdk_http_url(
endpoint_url = validate_unpinned_sdk_http_url(
profile.endpoint_url,
label="S3 connector endpoint",
)
except OutboundHttpError as exc:
raise ConnectorBrowseError(str(exc)) from exc
elif not outbound_http_policy().allow_private_networks:
else:
raise ConnectorBrowseError(
"S3 connector endpoint discovery cannot pin the SDK connection address while private-network access is disabled"
"S3 connector endpoint discovery uses an SDK transport that cannot guarantee connection-time DNS/IP "
"pinning; live S3 access is disabled until that transport supports pinning"
)
try:
boto3 = import_module("boto3")
config_module = import_module("botocore.config")
except ImportError as exc:
raise ConnectorBrowseUnsupported("S3 connector browsing requires the optional boto3 dependency") from exc
kwargs: dict[str, object] = {"endpoint_url": endpoint_url}
region = _metadata_string(profile, "region") or _metadata_string(profile, "aws_region")
if region:
kwargs["region_name"] = region

View File

@@ -110,7 +110,7 @@ def connector_provider_descriptors() -> tuple[ConnectorProviderDescriptor, ...]:
conflict_strategy="Managed import/sync uses existing files conflict_strategy handling after download.",
preview_strategy="Previews are generated from the frozen managed file after import or sync, not directly from the share.",
audit_events=("files.connector.imported", "files.connector.synced", "files.connector.accessed"),
notes="Requires the optional smbprotocol dependency and an smb://server[:port]/share[/path] profile endpoint.",
notes="Requires the optional smbprotocol dependency and an smb://server[:port]/share[/path] profile endpoint. Private-network mode requires an explicit IP endpoint until the transport supports connection-time DNS pinning.",
),
ConnectorProviderDescriptor(
provider="s3",
@@ -125,7 +125,7 @@ def connector_provider_descriptors() -> tuple[ConnectorProviderDescriptor, ...]:
conflict_strategy="Managed import/sync uses existing files conflict_strategy handling after object download.",
preview_strategy="Previews are generated from the frozen managed file after import or sync, not directly from the bucket.",
audit_events=("files.connector.imported", "files.connector.synced", "files.connector.accessed"),
notes="Configure endpoint_url for MinIO or other S3-compatible stores; use profile metadata for bucket, region, addressing_style, verify_tls, and ca_bundle.",
notes="Browse/import logic is implemented, but live boto3 access fails closed until its HTTP transport supports connection-time DNS/IP pinning and redirect revalidation.",
),
ConnectorProviderDescriptor(
provider="sharepoint",