From 9290a4bea36e6e7da16daba8db291caed49bf1ae Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 16 May 2026 22:05:27 +0000 Subject: [PATCH 1/3] Initial plan From a1b9945ab65fa1bb137e613890044fba6da0c264 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 16 May 2026 22:08:22 +0000 Subject: [PATCH 2/3] refactor(services): extract buildContainerSecurityHardening helper Extract duplicated security-hardening config (cap_drop, security_opt, mem_limit, memswap_limit, pids_limit, cpu_shares) from three sidecar service builders into a single `buildContainerSecurityHardening` helper in the new `src/services/service-security.ts` module. Each service spreads the result so resource limits remain service-specific while the security fields (cap_drop / security_opt) are guaranteed to stay in sync across all sidecars. Closes # --- src/services/api-proxy-service.ts | 13 +++------ src/services/cli-proxy-service.ts | 9 +++---- src/services/doh-proxy-service.ts | 9 +++---- src/services/service-security.ts | 45 +++++++++++++++++++++++++++++++ 4 files changed, 54 insertions(+), 22 deletions(-) create mode 100644 src/services/service-security.ts diff --git a/src/services/api-proxy-service.ts b/src/services/api-proxy-service.ts index deecd5bc1..780f76f5a 100644 --- a/src/services/api-proxy-service.ts +++ b/src/services/api-proxy-service.ts @@ -12,6 +12,7 @@ import { pickEnvVars } from '../env-utils'; import { COPILOT_PLACEHOLDER_TOKEN } from '../constants/placeholders'; import { NetworkConfig, ImageBuildConfig } from './squid-service'; import { applyHostPathPrefixToVolumes } from './host-path-prefix'; +import { buildContainerSecurityHardening } from './service-security'; interface ApiProxyBuildResult { /** The api-proxy service definition to add to Docker Compose services. */ @@ -208,16 +209,8 @@ export function buildApiProxyService(params: ApiProxyServiceParams): ApiProxyBui retries: 15, start_period: '30s', }, - // Security hardening: Drop all capabilities - cap_drop: ['ALL'], - security_opt: [ - 'no-new-privileges:true', - ], - // Resource limits to prevent DoS attacks - mem_limit: '512m', - memswap_limit: '512m', - pids_limit: 100, - cpu_shares: 512, + // Security hardening and resource limits to prevent DoS attacks + ...buildContainerSecurityHardening({ memLimit: '512m', pidsLimit: 100, cpuShares: 512 }), stop_grace_period: '2s', }; diff --git a/src/services/cli-proxy-service.ts b/src/services/cli-proxy-service.ts index 445b9bc47..b5428b567 100644 --- a/src/services/cli-proxy-service.ts +++ b/src/services/cli-proxy-service.ts @@ -6,6 +6,7 @@ import { logger } from '../logger'; import { WrapperConfig, CLI_PROXY_PORT } from '../types'; import { NetworkConfig, ImageBuildConfig } from './squid-service'; import { applyHostPathPrefixToVolumes } from './host-path-prefix'; +import { buildContainerSecurityHardening } from './service-security'; interface CliProxyBuildResult { /** The cli-proxy service definition to add to Docker Compose services. */ @@ -91,12 +92,8 @@ export function buildCliProxyService(params: CliProxyServiceParams): CliProxyBui condition: 'service_healthy', }, }, - cap_drop: ['ALL'], - security_opt: ['no-new-privileges:true'], - mem_limit: '256m', - memswap_limit: '256m', - pids_limit: 50, - cpu_shares: 256, + // Security hardening and resource limits to prevent DoS attacks + ...buildContainerSecurityHardening({ memLimit: '256m', pidsLimit: 50, cpuShares: 256 }), stop_grace_period: '2s', }; diff --git a/src/services/doh-proxy-service.ts b/src/services/doh-proxy-service.ts index 383e6b66e..da5cb3c52 100644 --- a/src/services/doh-proxy-service.ts +++ b/src/services/doh-proxy-service.ts @@ -2,6 +2,7 @@ import { DOH_PROXY_CONTAINER_NAME } from '../constants'; import { logger } from '../logger'; import { WrapperConfig } from '../types'; import { NetworkConfig } from './squid-service'; +import { buildContainerSecurityHardening } from './service-security'; interface DohProxyServiceParams { config: WrapperConfig; @@ -34,12 +35,8 @@ export function buildDohProxyService(params: DohProxyServiceParams): any { retries: 5, start_period: '2s', }, - // Security hardening: Drop all capabilities - cap_drop: ['ALL'], - security_opt: ['no-new-privileges:true'], - mem_limit: '128m', - memswap_limit: '128m', - pids_limit: 50, + // Security hardening and resource limits to prevent DoS attacks + ...buildContainerSecurityHardening({ memLimit: '128m', pidsLimit: 50 }), }; logger.info(`DNS-over-HTTPS proxy sidecar enabled - DNS queries encrypted via ${config.dnsOverHttps}`); diff --git a/src/services/service-security.ts b/src/services/service-security.ts new file mode 100644 index 000000000..6e78d95e9 --- /dev/null +++ b/src/services/service-security.ts @@ -0,0 +1,45 @@ +/** + * Shared container security-hardening helpers. + * + * Centralises the `cap_drop`, `security_opt`, and resource-limit fields that + * must be applied uniformly to every sidecar service built by the firewall. + * Using a single helper means a future hardening change (e.g. adding + * `read_only: true`) propagates to all sidecars automatically. + */ + +interface ContainerResourceLimits { + /** Maximum memory for the container (Docker memory format, e.g. '512m'). */ + memLimit: string; + /** Maximum number of processes/threads the container may create. */ + pidsLimit: number; + /** + * Relative CPU weight (cpu_shares). + * If omitted the field is not included in the output. + */ + cpuShares?: number; +} + +/** + * Returns the standard security-hardening fields for a sidecar service. + * + * The `cap_drop` and `security_opt` values are identical for every sidecar; + * only the resource limits vary per service. + * + * @example + * ```ts + * const service = { + * ...buildContainerSecurityHardening({ memLimit: '512m', pidsLimit: 100, cpuShares: 512 }), + * // other service-specific fields + * }; + * ``` + */ +export function buildContainerSecurityHardening(limits: ContainerResourceLimits): Record { + return { + cap_drop: ['ALL'], + security_opt: ['no-new-privileges:true'], + mem_limit: limits.memLimit, + memswap_limit: limits.memLimit, + pids_limit: limits.pidsLimit, + ...(limits.cpuShares !== undefined && { cpu_shares: limits.cpuShares }), + }; +} From 5f2592b5780c5f1da623580d787e2ac68751a46e Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 16 May 2026 22:31:40 +0000 Subject: [PATCH 3/3] docs(service-security): narrow scope to unprivileged proxy sidecars The module and function JSDoc now explicitly name the three proxy sidecars (api-proxy, cli-proxy, doh-proxy) this helper targets and call out that squid-service and agent-service have different hardening requirements and should not use it. --- src/services/service-security.ts | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/src/services/service-security.ts b/src/services/service-security.ts index 6e78d95e9..6aa24c50f 100644 --- a/src/services/service-security.ts +++ b/src/services/service-security.ts @@ -1,10 +1,16 @@ /** - * Shared container security-hardening helpers. + * Security-hardening helpers for unprivileged proxy sidecar containers. + * + * This module centralises the `cap_drop`, `security_opt`, and resource-limit + * fields shared by the lightweight proxy sidecars (api-proxy, cli-proxy, + * doh-proxy) that run without any Linux capabilities. + * + * Note: other services such as `squid-service` and `agent-service` have + * different hardening requirements (custom cap_drop sets, seccomp profiles, + * AppArmor options) and should NOT use this helper. * - * Centralises the `cap_drop`, `security_opt`, and resource-limit fields that - * must be applied uniformly to every sidecar service built by the firewall. * Using a single helper means a future hardening change (e.g. adding - * `read_only: true`) propagates to all sidecars automatically. + * `read_only: true`) propagates to all three proxy sidecars automatically. */ interface ContainerResourceLimits { @@ -20,10 +26,11 @@ interface ContainerResourceLimits { } /** - * Returns the standard security-hardening fields for a sidecar service. + * Returns the standard security-hardening fields for an unprivileged proxy + * sidecar (api-proxy, cli-proxy, doh-proxy). * - * The `cap_drop` and `security_opt` values are identical for every sidecar; - * only the resource limits vary per service. + * `cap_drop: ['ALL']` and `security_opt: ['no-new-privileges:true']` are + * fixed; resource limits are caller-supplied because they differ per service. * * @example * ```ts