Skip to content

Commit 1ace48b

Browse files
authored
refactor(services): deduplicate container security-hardening config across sidecar builders (#3274)
* Initial plan * 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 #<issue> * 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. --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
1 parent 777c0b2 commit 1ace48b

4 files changed

Lines changed: 61 additions & 22 deletions

File tree

src/services/api-proxy-service.ts

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import { pickEnvVars } from '../env-utils';
1212
import { COPILOT_PLACEHOLDER_TOKEN } from '../constants/placeholders';
1313
import { NetworkConfig, ImageBuildConfig } from './squid-service';
1414
import { applyHostPathPrefixToVolumes } from './host-path-prefix';
15+
import { buildContainerSecurityHardening } from './service-security';
1516

1617
interface ApiProxyBuildResult {
1718
/** The api-proxy service definition to add to Docker Compose services. */
@@ -208,16 +209,8 @@ export function buildApiProxyService(params: ApiProxyServiceParams): ApiProxyBui
208209
retries: 15,
209210
start_period: '30s',
210211
},
211-
// Security hardening: Drop all capabilities
212-
cap_drop: ['ALL'],
213-
security_opt: [
214-
'no-new-privileges:true',
215-
],
216-
// Resource limits to prevent DoS attacks
217-
mem_limit: '512m',
218-
memswap_limit: '512m',
219-
pids_limit: 100,
220-
cpu_shares: 512,
212+
// Security hardening and resource limits to prevent DoS attacks
213+
...buildContainerSecurityHardening({ memLimit: '512m', pidsLimit: 100, cpuShares: 512 }),
221214
stop_grace_period: '2s',
222215
};
223216

src/services/cli-proxy-service.ts

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { logger } from '../logger';
66
import { WrapperConfig, CLI_PROXY_PORT } from '../types';
77
import { NetworkConfig, ImageBuildConfig } from './squid-service';
88
import { applyHostPathPrefixToVolumes } from './host-path-prefix';
9+
import { buildContainerSecurityHardening } from './service-security';
910

1011
interface CliProxyBuildResult {
1112
/** The cli-proxy service definition to add to Docker Compose services. */
@@ -91,12 +92,8 @@ export function buildCliProxyService(params: CliProxyServiceParams): CliProxyBui
9192
condition: 'service_healthy',
9293
},
9394
},
94-
cap_drop: ['ALL'],
95-
security_opt: ['no-new-privileges:true'],
96-
mem_limit: '256m',
97-
memswap_limit: '256m',
98-
pids_limit: 50,
99-
cpu_shares: 256,
95+
// Security hardening and resource limits to prevent DoS attacks
96+
...buildContainerSecurityHardening({ memLimit: '256m', pidsLimit: 50, cpuShares: 256 }),
10097
stop_grace_period: '2s',
10198
};
10299

src/services/doh-proxy-service.ts

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { DOH_PROXY_CONTAINER_NAME } from '../constants';
22
import { logger } from '../logger';
33
import { WrapperConfig } from '../types';
44
import { NetworkConfig } from './squid-service';
5+
import { buildContainerSecurityHardening } from './service-security';
56

67
interface DohProxyServiceParams {
78
config: WrapperConfig;
@@ -34,12 +35,8 @@ export function buildDohProxyService(params: DohProxyServiceParams): any {
3435
retries: 5,
3536
start_period: '2s',
3637
},
37-
// Security hardening: Drop all capabilities
38-
cap_drop: ['ALL'],
39-
security_opt: ['no-new-privileges:true'],
40-
mem_limit: '128m',
41-
memswap_limit: '128m',
42-
pids_limit: 50,
38+
// Security hardening and resource limits to prevent DoS attacks
39+
...buildContainerSecurityHardening({ memLimit: '128m', pidsLimit: 50 }),
4340
};
4441

4542
logger.info(`DNS-over-HTTPS proxy sidecar enabled - DNS queries encrypted via ${config.dnsOverHttps}`);

src/services/service-security.ts

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
/**
2+
* Security-hardening helpers for unprivileged proxy sidecar containers.
3+
*
4+
* This module centralises the `cap_drop`, `security_opt`, and resource-limit
5+
* fields shared by the lightweight proxy sidecars (api-proxy, cli-proxy,
6+
* doh-proxy) that run without any Linux capabilities.
7+
*
8+
* Note: other services such as `squid-service` and `agent-service` have
9+
* different hardening requirements (custom cap_drop sets, seccomp profiles,
10+
* AppArmor options) and should NOT use this helper.
11+
*
12+
* Using a single helper means a future hardening change (e.g. adding
13+
* `read_only: true`) propagates to all three proxy sidecars automatically.
14+
*/
15+
16+
interface ContainerResourceLimits {
17+
/** Maximum memory for the container (Docker memory format, e.g. '512m'). */
18+
memLimit: string;
19+
/** Maximum number of processes/threads the container may create. */
20+
pidsLimit: number;
21+
/**
22+
* Relative CPU weight (cpu_shares).
23+
* If omitted the field is not included in the output.
24+
*/
25+
cpuShares?: number;
26+
}
27+
28+
/**
29+
* Returns the standard security-hardening fields for an unprivileged proxy
30+
* sidecar (api-proxy, cli-proxy, doh-proxy).
31+
*
32+
* `cap_drop: ['ALL']` and `security_opt: ['no-new-privileges:true']` are
33+
* fixed; resource limits are caller-supplied because they differ per service.
34+
*
35+
* @example
36+
* ```ts
37+
* const service = {
38+
* ...buildContainerSecurityHardening({ memLimit: '512m', pidsLimit: 100, cpuShares: 512 }),
39+
* // other service-specific fields
40+
* };
41+
* ```
42+
*/
43+
export function buildContainerSecurityHardening(limits: ContainerResourceLimits): Record<string, unknown> {
44+
return {
45+
cap_drop: ['ALL'],
46+
security_opt: ['no-new-privileges:true'],
47+
mem_limit: limits.memLimit,
48+
memswap_limit: limits.memLimit,
49+
pids_limit: limits.pidsLimit,
50+
...(limits.cpuShares !== undefined && { cpu_shares: limits.cpuShares }),
51+
};
52+
}

0 commit comments

Comments
 (0)