Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
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
58 changes: 0 additions & 58 deletions containers/agent/seccomp-profile.json

This file was deleted.

3 changes: 1 addition & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -80,8 +80,7 @@
"pkg": {
"scripts": "dist/**/*.js",
"assets": [
"node_modules/chalk/**/*",
"containers/agent/seccomp-profile.json"
"node_modules/chalk/**/*"
],
"targets": [
"node18-linux-x64",
Expand Down
13 changes: 7 additions & 6 deletions src/docker-manager.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1015,8 +1015,9 @@ describe('docker-manager', () => {
'MKNOD',
]);

// Verify seccomp profile is configured
expect(agent.security_opt).toContain('seccomp=/tmp/awf-test/seccomp-profile.json');
// Verify Docker's built-in default seccomp profile is used (allowlist-based, SCMP_ACT_ERRNO default)
// No custom seccomp profile - Docker applies its default which blocks ~44 dangerous syscall families
expect(agent.security_opt).not.toContainEqual(expect.stringContaining('seccomp='));

// Verify no-new-privileges is enabled to prevent privilege escalation
expect(agent.security_opt).toContain('no-new-privileges:true');
Expand Down Expand Up @@ -1880,11 +1881,11 @@ describe('docker-manager', () => {
workDir: newWorkDir,
};

// writeConfigs may succeed if seccomp profile is found, or fail if not
// writeConfigs may succeed or fail depending on SSL/other config
try {
await writeConfigs(config);
} catch {
// Expected to fail if seccomp profile not found, but directories should still be created
// Expected to fail in test environment, but directories should still be created
}

// Verify work directory was created
Expand Down Expand Up @@ -1967,7 +1968,7 @@ describe('docker-manager', () => {
// May fail after writing configs
}

// Verify squid.conf was created (it's created before seccomp check)
// Verify squid.conf was created
const squidConfPath = path.join(testDir, 'squid.conf');
if (fs.existsSync(squidConfPath)) {
const content = fs.readFileSync(squidConfPath, 'utf-8');
Expand Down Expand Up @@ -2013,7 +2014,7 @@ describe('docker-manager', () => {
try {
await writeConfigs(config);
} catch {
// May fail if seccomp profile not found
// May fail in test environment
}

// Verify directory was created with restricted permissions
Expand Down
27 changes: 6 additions & 21 deletions src/docker-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -902,13 +902,17 @@ export function generateDockerCompose(
'SYS_RAWIO', // Prevents raw I/O access
'MKNOD', // Prevents device node creation
],
// Apply seccomp profile and no-new-privileges to restrict dangerous syscalls and prevent privilege escalation
// Security hardening via Docker's built-in default seccomp profile (allowlist-based, SCMP_ACT_ERRNO default).
// Docker's default profile blocks ~44 dangerous syscall families while allowing standard container operations.
// Dangerous syscalls like mount/chroot are conditionally allowed based on capabilities (CAP_SYS_ADMIN,
// CAP_SYS_CHROOT), which are irrevocably dropped via capsh before user code runs.
// ptrace/process_vm_readv/process_vm_writev are blocked by cap_drop: SYS_PTRACE above.
// no-new-privileges prevents SUID/SGID privilege escalation.
// AppArmor is set to unconfined to allow mounting procfs at /host/proc
// (Docker's default AppArmor profile blocks mount). This is safe because SYS_ADMIN is
// dropped via capsh before user code runs, so user code cannot mount anything.
security_opt: [
'no-new-privileges:true',
`seccomp=${config.workDir}/seccomp-profile.json`,
'apparmor:unconfined',
],
// Resource limits to prevent DoS attacks (conservative defaults)
Expand Down Expand Up @@ -1222,25 +1226,6 @@ export async function writeConfigs(config: WrapperConfig): Promise<void> {
logger.debug(`Using network config: ${networkConfig.subnet} (squid: ${networkConfig.squidIp}, agent: ${networkConfig.agentIp}, api-proxy: ${networkConfig.proxyIp})`);


// Copy seccomp profile to work directory for container security
const seccompSourcePath = path.join(__dirname, '..', 'containers', 'agent', 'seccomp-profile.json');
const seccompDestPath = path.join(config.workDir, 'seccomp-profile.json');
if (fs.existsSync(seccompSourcePath)) {
fs.copyFileSync(seccompSourcePath, seccompDestPath);
logger.debug(`Seccomp profile written to: ${seccompDestPath}`);
} else {
// If running from dist, try relative to dist
const altSeccompPath = path.join(__dirname, '..', '..', 'containers', 'agent', 'seccomp-profile.json');
if (fs.existsSync(altSeccompPath)) {
fs.copyFileSync(altSeccompPath, seccompDestPath);
logger.debug(`Seccomp profile written to: ${seccompDestPath}`);
} else {
const message = `Seccomp profile not found at ${seccompSourcePath} or ${altSeccompPath}. Container security hardening requires the seccomp profile.`;
logger.error(message);
throw new Error(message);
}
}

// Generate SSL Bump certificates if enabled
let sslConfig: SslConfig | undefined;
if (config.sslBump) {
Expand Down
Loading