feat: add EgressManager — per-service egress enforcement via host iptables
Unit Tests / test (push) Successful in 11m20s
Unit Tests / test (push) Successful in 11m20s
Routes outbound traffic from installed service containers through alternate exits (wireguard_ext, openvpn, tor) using host-side iptables fwmark policy-routing in a dedicated PIC_EGRESS chain. Marks 0x110/0x120/0x130 are distinct from ConnectivityManager's 0x10/0x20/0x30. Container IPs discovered at runtime via docker inspect. Wired into ServiceStoreManager install/remove lifecycle and managers.py singleton. 22 new tests. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -75,12 +75,13 @@ class ServiceStoreManager(BaseServiceManager):
|
||||
|
||||
def __init__(self, config_manager, caddy_manager, container_manager,
|
||||
data_dir: str = '', config_dir: str = '',
|
||||
service_composer=None):
|
||||
service_composer=None, egress_manager=None):
|
||||
super().__init__('service_store', data_dir, config_dir)
|
||||
self.config_manager = config_manager
|
||||
self.caddy_manager = caddy_manager
|
||||
self.container_manager = container_manager
|
||||
self.service_composer = service_composer
|
||||
self.egress_manager = egress_manager
|
||||
self.compose_override = os.environ.get(
|
||||
'COMPOSE_SERVICES_PATH', '/app/docker-compose.services.yml'
|
||||
)
|
||||
@@ -345,6 +346,12 @@ class ServiceStoreManager(BaseServiceManager):
|
||||
except Exception as e:
|
||||
logger.warning('install: caddy regenerate failed for %s (non-fatal): %s', service_id, e)
|
||||
|
||||
if self.egress_manager:
|
||||
try:
|
||||
self.egress_manager.apply_service(service_id)
|
||||
except Exception as exc:
|
||||
logger.warning('Egress apply failed for %s (non-fatal): %s', service_id, exc)
|
||||
|
||||
return {'ok': True}
|
||||
|
||||
def remove(self, service_id: str, purge_data: bool = False) -> dict:
|
||||
@@ -363,6 +370,12 @@ class ServiceStoreManager(BaseServiceManager):
|
||||
'error': f'Cannot remove {service_id}: required by {", ".join(sorted(dependents))}',
|
||||
}
|
||||
|
||||
if self.egress_manager:
|
||||
try:
|
||||
self.egress_manager.clear_service(service_id)
|
||||
except Exception as exc:
|
||||
logger.warning('Egress clear failed for %s (non-fatal): %s', service_id, exc)
|
||||
|
||||
# Stop and remove containers (best-effort)
|
||||
if self.service_composer is not None:
|
||||
try:
|
||||
|
||||
Reference in New Issue
Block a user