-
Notifications
You must be signed in to change notification settings - Fork 8
fix: hide workDir from agent container to prevent secrets exposure #718
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -718,17 +718,33 @@ export function generateDockerCompose( | |||||||||||||||||||||
| dns_search: [], // Disable DNS search domains to prevent embedded DNS fallback | ||||||||||||||||||||||
| volumes: agentVolumes, | ||||||||||||||||||||||
| environment, | ||||||||||||||||||||||
| // Hide /tmp/gh-aw/mcp-logs directory using tmpfs (empty in-memory filesystem) | ||||||||||||||||||||||
| // This prevents the agent from accessing MCP server logs while still allowing | ||||||||||||||||||||||
| // the host to write logs to /tmp/gh-aw/mcp-logs/ (e.g., /tmp/gh-aw/mcp-logs/safeoutputs/) | ||||||||||||||||||||||
| // For normal mode: hide /tmp/gh-aw/mcp-logs | ||||||||||||||||||||||
| // For chroot mode: hide both /tmp/gh-aw/mcp-logs and /host/tmp/gh-aw/mcp-logs | ||||||||||||||||||||||
| // SECURITY: Hide sensitive directories from agent using tmpfs overlays (empty in-memory filesystems) | ||||||||||||||||||||||
| // | ||||||||||||||||||||||
| // 1. MCP logs: tmpfs over /tmp/gh-aw/mcp-logs prevents the agent from reading | ||||||||||||||||||||||
| // MCP server logs inside the container. The host can still write to its own | ||||||||||||||||||||||
| // /tmp/gh-aw/mcp-logs directory since tmpfs only affects the container's view. | ||||||||||||||||||||||
| // | ||||||||||||||||||||||
| // 2. WorkDir: tmpfs over workDir (e.g., /tmp/awf-<timestamp>) prevents the agent | ||||||||||||||||||||||
| // from reading docker-compose.yml which contains environment variables (tokens, | ||||||||||||||||||||||
| // API keys) in plaintext. Without this overlay, code inside the container could | ||||||||||||||||||||||
| // extract secrets via: cat /tmp/awf-*/docker-compose.yml | ||||||||||||||||||||||
| // Note: volume mounts of workDir subdirectories (agent-logs, squid-logs, etc.) | ||||||||||||||||||||||
| // are mapped to different container paths (e.g., ~/.copilot/logs, /var/log/squid) | ||||||||||||||||||||||
| // so they are unaffected by the tmpfs overlay on workDir. | ||||||||||||||||||||||
| // | ||||||||||||||||||||||
| // For chroot mode: hide both normal and /host-prefixed paths since /tmp is | ||||||||||||||||||||||
| // mounted at both /tmp and /host/tmp | ||||||||||||||||||||||
| tmpfs: config.enableChroot | ||||||||||||||||||||||
| ? [ | ||||||||||||||||||||||
| '/tmp/gh-aw/mcp-logs:rw,noexec,nosuid,size=1m', | ||||||||||||||||||||||
| '/host/tmp/gh-aw/mcp-logs:rw,noexec,nosuid,size=1m', | ||||||||||||||||||||||
| `${config.workDir}:rw,noexec,nosuid,size=1m`, | ||||||||||||||||||||||
| `/host${config.workDir}:rw,noexec,nosuid,size=1m`, | ||||||||||||||||||||||
| ] | ||||||||||||||||||||||
| : ['/tmp/gh-aw/mcp-logs:rw,noexec,nosuid,size=1m'], | ||||||||||||||||||||||
| : [ | ||||||||||||||||||||||
| '/tmp/gh-aw/mcp-logs:rw,noexec,nosuid,size=1m', | ||||||||||||||||||||||
| `${config.workDir}:rw,noexec,nosuid,size=1m`, | ||||||||||||||||||||||
| ], | ||||||||||||||||||||||
|
Comment on lines
737
to
+747
|
||||||||||||||||||||||
| depends_on: { | ||||||||||||||||||||||
| 'squid-proxy': { | ||||||||||||||||||||||
| condition: 'service_healthy', | ||||||||||||||||||||||
|
|
@@ -852,9 +868,13 @@ export function generateDockerCompose( | |||||||||||||||||||||
| export async function writeConfigs(config: WrapperConfig): Promise<void> { | ||||||||||||||||||||||
| logger.debug('Writing configuration files...'); | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| // Ensure work directory exists | ||||||||||||||||||||||
| // Ensure work directory exists with restricted permissions (owner-only access) | ||||||||||||||||||||||
| // Defense-in-depth: even if tmpfs overlay fails, non-root processes on the host | ||||||||||||||||||||||
| // cannot read the docker-compose.yml which contains sensitive tokens | ||||||||||||||||||||||
| if (!fs.existsSync(config.workDir)) { | ||||||||||||||||||||||
| fs.mkdirSync(config.workDir, { recursive: true }); | ||||||||||||||||||||||
| fs.mkdirSync(config.workDir, { recursive: true, mode: 0o700 }); | ||||||||||||||||||||||
| } else { | ||||||||||||||||||||||
| fs.chmodSync(config.workDir, 0o700); | ||||||||||||||||||||||
|
||||||||||||||||||||||
| fs.chmodSync(config.workDir, 0o700); | |
| // If the directory already exists, only tighten permissions if it is | |
| // world-accessible. This avoids unexpectedly overriding intentionally-set | |
| // permissions (e.g., in shared or multi-user environments) while still | |
| // preventing access by "others". | |
| const stat = fs.statSync(config.workDir); | |
| const currentMode = stat.mode & 0o777; | |
| if ((currentMode & 0o007) !== 0) { | |
| fs.chmodSync(config.workDir, 0o700); | |
| } |
Copilot
AI
Feb 12, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missing explicit permissions on agent-logs subdirectory. The agent-logs directory at line 878-882 is created without specifying mode, so it will inherit from the parent workDir's umask.
For consistency with the pattern established for squid-logs (mode 0o777) and mcp-logs (mode 0o777), and considering this directory needs to be writable by the agent container running as awfuser, this should explicitly set appropriate permissions.
However, note that this comment may be superseded by the critical bug in the tmpfs implementation that would prevent this subdirectory from being accessible anyway.
See below for a potential fix:
// Make directory writable by the agent container user (awfuser)
const agentLogsDir = path.join(config.workDir, 'agent-logs');
if (!fs.existsSync(agentLogsDir)) {
fs.mkdirSync(agentLogsDir, { recursive: true, mode: 0o777 });
// Explicitly set permissions to 0o777 (not affected by umask)
fs.chmodSync(agentLogsDir, 0o777);
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The tests verify that tmpfs overlays are added, but they don't test for the critical bug where tmpfs on workDir will break volume mounts of subdirectories.
These tests should also verify that essential volume mounts (like agent-logs, SSL certificates, seccomp profile) are not broken by the tmpfs overlay. Consider adding integration tests that actually start containers to verify the tmpfs implementation doesn't interfere with required functionality.
The current unit tests only check that tmpfs entries exist in the configuration but don't validate the runtime behavior or interaction with volume mounts.