Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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
14 changes: 14 additions & 0 deletions containers/agent/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,21 @@ RUN set -eux; \
apt-get -o Acquire::Retries=3 -o Acquire::http::Timeout=30 -o Acquire::https::Timeout=30 upgrade -y; \
}; \
PKGS="iptables curl ca-certificates git gnupg dnsutils net-tools netcat-openbsd libcap2-bin libgdiplus libev-dev libssl-dev php-intl php-gd python3"; \
# Chromium/Playwright native runtime libraries. The agent uses selective bind
# mounts (not a full host filesystem mount), so Playwright-managed browsers
# cannot pick these up from the host and must be present in the image.
BROWSER_PKGS="libasound2 libatk-bridge2.0-0 libatk1.0-0 libatspi2.0-0 libcairo2 libcups2 libdbus-1-3 libdrm2 libexpat1 libgbm1 libglib2.0-0 libnspr4 libnss3 libpango-1.0-0 libpangocairo-1.0-0 libx11-6 libxcb1 libxcomposite1 libxdamage1 libxext6 libxfixes3 libxkbcommon0 libxrandr2 libxrender1 libxshmfence1 fonts-liberation"; \
Comment on lines +112 to +122
apt_update_retry && \
# Ubuntu 24.04 renamed several of these to time_t-64 variants (e.g. libasound2t64),
# and the old names became ambiguous virtual packages. Prefer the t64 package when
# the base image provides one, otherwise keep the 22.04 name.
for pkg in $BROWSER_PKGS; do \
if apt-cache show "${pkg}t64" >/dev/null 2>&1; then \
PKGS="$PKGS ${pkg}t64"; \
else \
PKGS="$PKGS $pkg"; \
fi; \
done && \
apt_install_retry $PKGS && \
apt_upgrade_retry && \
# Raw sockets are unavailable to the agent; remove stale file capabilities
Expand Down
5 changes: 5 additions & 0 deletions docs-site/src/content/docs/reference/agent-images.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,11 @@ The default `agent` image (based on Ubuntu 22.04) includes the following pre-ins
| libssl-dev | — | `libssl-dev` | OpenSSL development headers for native extensions |
| php-intl | — | `php-intl` | PHP Internationalization extension |
| php-gd | — | `php-gd` | PHP GD graphics extension |
| Chromium runtime libraries | — | see below | Shared libraries required by Playwright-managed browsers |

:::note[Chromium/Playwright runtime libraries]
Because the agent uses selective bind mounts instead of a full host filesystem mount, browsers downloaded by Playwright cannot rely on host libraries. The image therefore preinstalls `libasound2`, `libatk-bridge2.0-0`, `libatk1.0-0`, `libatspi2.0-0`, `libcairo2`, `libcups2`, `libdbus-1-3`, `libdrm2`, `libexpat1`, `libgbm1`, `libglib2.0-0`, `libnspr4`, `libnss3`, `libpango-1.0-0`, `libpangocairo-1.0-0`, `libx11-6`, `libxcb1`, `libxcomposite1`, `libxdamage1`, `libxext6`, `libxfixes3`, `libxkbcommon0`, `libxrandr2`, `libxrender1`, `libxshmfence1`, and `fonts-liberation` (plus their transitive dependencies). On Ubuntu 24.04 base images the `t64` variants of these packages are installed automatically.
:::

:::caution[Docker CLI Stub]
The `docker` command is present but is a stub—there is no Docker daemon running inside the container. Docker-in-Docker is not supported. Use `--mount` to access Docker sockets from the host if needed.
Expand Down
3 changes: 3 additions & 0 deletions docs/agent-images.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,9 @@ The `default` preset image (based on Ubuntu 22.04) includes the following pre-in
| capsh | — | `libcap2-bin` | Capability management |
| gnupg | 2.2.27 | `gnupg` | GPG encryption |
| ca-certificates | — | `ca-certificates` | Trusted root certificates |
| Chromium runtime libraries | — | see below | Shared libraries required by Playwright-managed browsers |

**Chromium/Playwright runtime libraries:** Because the agent uses selective bind mounts instead of a full host filesystem mount, browsers downloaded by Playwright cannot rely on host libraries. The image therefore preinstalls `libasound2`, `libatk-bridge2.0-0`, `libatk1.0-0`, `libatspi2.0-0`, `libcairo2`, `libcups2`, `libdbus-1-3`, `libdrm2`, `libexpat1`, `libgbm1`, `libglib2.0-0`, `libnspr4`, `libnss3`, `libpango-1.0-0`, `libpangocairo-1.0-0`, `libx11-6`, `libxcb1`, `libxcomposite1`, `libxdamage1`, `libxext6`, `libxfixes3`, `libxkbcommon0`, `libxrandr2`, `libxrender1`, `libxshmfence1`, and `fonts-liberation` (plus their transitive dependencies). On Ubuntu 24.04 base images the `t64` variants of these packages are installed automatically.

**⚠️ Docker CLI Stub:** The `docker` command is present but is a stub—there is no Docker daemon running inside the container. Docker-in-Docker is not supported. Use `--mount` to access Docker sockets from the host if needed.

Expand Down
31 changes: 31 additions & 0 deletions docs/troubleshooting.md
Original file line number Diff line number Diff line change
Expand Up @@ -364,6 +364,37 @@ AWF uses a forward proxy (Squid) for HTTPS egress control rather than transparen
- **Java tools**: Use `JAVA_TOOL_OPTIONS` with JVM system properties (set automatically by AWF)
- **Maven**: Requires `~/.m2/settings.xml` (must be configured manually — see above)

## Playwright / Chromium Issues

### Browser fails to launch with missing shared libraries

**Problem:** `playwright-cli` (or `npx playwright`) fails to start Chromium with errors such as:

```
error while loading shared libraries: libnspr4.so: cannot open shared object file
Host system is missing dependencies to run browsers.
```

**Cause:** The agent container uses selective bind mounts rather than a full host filesystem mount, so browsers downloaded by Playwright cannot pick up native libraries installed on the host.

**Solution:** The agent image preinstalls Chromium's native runtime libraries (`libnspr4`, `libnss3`, `libatk1.0-0`, `libatk-bridge2.0-0`, `libatspi2.0-0`, `libcups2`, `libdrm2`, `libgbm1`, `libpango-1.0-0`, `libxkbcommon0`, `libxcomposite1`, `libxdamage1`, `libxrandr2`, `libasound2`, `fonts-liberation`, and related packages). Use a current agent image (`--image-tag latest`, or `--build-local` when building from source) and the browser will launch.

Verify the libraries are present inside the sandbox:

```bash
sudo awf --allow-domains '' -- bash -c 'ldd $(find ~/.cache/ms-playwright -name headless_shell | head -1) | grep "not found" || echo "all libraries resolved"'
```

If you pin an older agent image, or you use a custom base image, install the dependencies yourself before running the browser:

```bash
npx playwright install-deps chromium
```

### Browser downloads are blocked

Playwright downloads browser binaries from `cdn.playwright.dev`. Either add that domain to `--allow-domains`, or download the browsers on the host before the sandbox starts and point `PLAYWRIGHT_BROWSERS_PATH` at the staged directory.

## Harness Binary Resolution Issues

### `spawn /usr/local/bin/<tool> ENOENT` (hardcoded absolute paths)
Expand Down
104 changes: 104 additions & 0 deletions scripts/ci/agent-dockerfile-browser-deps.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
import * as fs from 'fs';
import * as path from 'path';
import { spawnSync } from 'child_process';

const dockerfilePath = path.resolve(__dirname, '../../containers/agent/Dockerfile');

const readDockerfile = (): string => fs.readFileSync(dockerfilePath, 'utf-8');

const readBrowserPackages = (): string[] => {
const declaration = readDockerfile().match(/BROWSER_PKGS="([^"]+)"/)?.[1];
expect(declaration).toBeDefined();
return declaration!.split(/\s+/).filter(Boolean);
};

/**
* Extracts the shell loop that appends the browser packages to PKGS, resolving
* Ubuntu 24.04 `t64` package renames, so it can be exercised with a stubbed
* `apt-cache`.
*/
const readResolutionScript = (): string => {
const block = readDockerfile().match(/for pkg in \$BROWSER_PKGS; do[\s\S]*?done && \\/)?.[0];

expect(block).toBeDefined();
return block!
.replace(/done && \\\s*$/, 'done')
.split('\n')
.map((line) => line.trim().replace(/\s*\\$/, ''))
.join('\n');
};

const runResolution = (t64Packages: string[]): string => {
const script = `
set -eu
apt-cache() {
for available in ${t64Packages.map((pkg) => `"${pkg}"`).join(' ')}; do
if [ "$2" = "$available" ]; then return 0; fi
done
return 100
}
PKGS="iptables"
BROWSER_PKGS="${readBrowserPackages().join(' ')}"
${readResolutionScript()}
echo "$PKGS"
`;

const result = spawnSync('bash', ['-c', script], { encoding: 'utf-8' });
expect(result.status).toBe(0);
return result.stdout.trim();
};

describe('agent Dockerfile Chromium runtime dependencies', () => {
it('preinstalls the shared libraries Playwright-managed Chromium needs', () => {
const packages = readBrowserPackages();

for (const required of [
'libasound2',
'libatk-bridge2.0-0',
'libatk1.0-0',
'libatspi2.0-0',
'libcups2',
'libdbus-1-3',
'libdrm2',
'libgbm1',
'libglib2.0-0',
'libnspr4',
'libnss3',
'libpango-1.0-0',
'libxcomposite1',
'libxdamage1',
'libxfixes3',
'libxkbcommon0',
'libxrandr2',
'fonts-liberation',
]) {
expect(packages).toContain(required);
}
});

it('declares the base (22.04) package names, never the t64 variants', () => {
for (const pkg of readBrowserPackages()) {
expect(pkg.endsWith('t64')).toBe(false);
}
});

it('keeps the 22.04 names when the base image has no t64 packages', () => {
const resolved = runResolution([]);

expect(resolved.split(/\s+/)).toEqual(['iptables', ...readBrowserPackages()]);
});

it('prefers t64 packages when the base image provides them', () => {
const resolved = runResolution(['libasound2t64', 'libcups2t64']);

expect(resolved).toContain('libasound2t64');
expect(resolved).toContain('libcups2t64');
expect(resolved.split(/\s+/)).not.toContain('libasound2');
expect(resolved.split(/\s+/)).not.toContain('libcups2');
expect(resolved).toContain('libnss3');
});

it('installs the resolved browser packages with the other agent packages', () => {
expect(readDockerfile()).toMatch(/apt_install_retry \$PKGS/);
});
});