capgate 0.0.1 → 0.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -10,6 +10,18 @@
10
10
  // - seccomp syscall filters → out of scope for v0.1
11
11
  // - secret value resolution → caller pulls from secret store
12
12
  //
13
+ // Net posture (fail-closed). bwrap's --unshare-net is all-or-nothing: an
14
+ // unprivileged user namespace cannot mint veth pairs or routes, so bwrap can
15
+ // only fully isolate the net namespace or fully share the host's. Selective
16
+ // egress is impossible inside bwrap alone. Therefore this adapter ALWAYS emits
17
+ // --unshare-net — even when egress is declared — and NEVER shares the host
18
+ // netns. A declared net capability does not relax isolation; it only records an
19
+ // EgressRule[] that a privileged broker must honor by launching the bwrap
20
+ // process inside a pre-created, egress-constrained network namespace. If no
21
+ // such broker exists, the sandbox simply has no network: deny-by-default holds.
22
+ // (The prior behavior — sharing host netns whenever net>0 — was a silent
23
+ // privilege escalation that read as a constraint; it is refused by design.)
24
+ //
13
25
  // This adapter emits a BwrapArtifact: argv for bwrap itself plus companion
14
26
  // data the host must honor. The host decides how to wire EgressRule — mitmproxy,
15
27
  // nftables, Envoy; the adapter stays policy-layer only.
@@ -20,14 +32,14 @@ export function lowerToBwrap(policy, opts = {}) {
20
32
  const systemMounts = opts.systemMounts ?? DEFAULT_SYSTEM_MOUNTS;
21
33
  const exposeDev = opts.exposeDev ?? policy.nestedSandbox;
22
34
  // ---------- base namespaces ----------
23
- // We build the unshare set explicitly instead of using --unshare-all so the
24
- // net flag is a single decision point. Net is only shared when policy.net
25
- // is non-empty; nestedSandbox additionally keeps user/pid/ipc for inner
26
- // sandboxes (Chromium, QEMU) that re-namespace themselves.
27
- const shareNet = policy.net.length > 0;
28
- argv.push('--unshare-uts', '--unshare-cgroup-try');
29
- if (!shareNet)
30
- argv.push('--unshare-net');
35
+ // We build the unshare set explicitly instead of using --unshare-all so each
36
+ // namespace is an auditable decision point. Net is ALWAYS unshared: bwrap
37
+ // cannot do selective egress (see header), so sharing the host netns would be
38
+ // a silent grant of full host network access. Declared egress is carried in
39
+ // egress[] for an external netns broker to enforce, never by relaxing this.
40
+ // nestedSandbox additionally keeps user/pid/ipc for inner sandboxes
41
+ // (Chromium, QEMU) that re-namespace themselves.
42
+ argv.push('--unshare-uts', '--unshare-cgroup-try', '--unshare-net');
31
43
  if (!policy.nestedSandbox) {
32
44
  argv.push('--unshare-user-try', '--unshare-pid', '--unshare-ipc');
33
45
  }
@@ -106,15 +118,18 @@ export function lowerToBwrap(policy, opts = {}) {
106
118
  argv.push('--setenv', 'HOME', '/tmp');
107
119
  // Values for envInjections are injected by the caller via --setenv at exec time.
108
120
  // ---------- net ----------
109
- // bwrap has only binary share/unshare. Host-level allowlists live in the
110
- // egress proxy. We emit rules either way so a deny-all proxy is a valid host.
121
+ // The net namespace is already unshared above, so the sandbox starts with NO
122
+ // network (loopback only). bwrap cannot open a constrained hole, so declared
123
+ // egress is NOT enforced here: we emit EgressRule[] for a privileged broker
124
+ // that runs this bwrap process inside a pre-created, egress-aware netns. We
125
+ // emit rules even for the empty case so a deny-all broker is a valid host.
111
126
  const egress = policy.net.map((n) => ({
112
127
  host: n.host,
113
128
  port: n.port,
114
129
  blockPrivate: n.blockPrivate,
115
130
  }));
116
131
  if (policy.net.length > 0) {
117
- notes.push(`net: ${policy.net.length} endpoint(s) declared — host MUST route outbound traffic through an egress proxy honoring egress[]`);
132
+ notes.push(`net: ${policy.net.length} endpoint(s) declared but NOT enforced by bwrap alone bwrap always unshares the net namespace (no host network). To grant egress, a privileged broker MUST launch bwrap inside a pre-created network namespace whose egress honors egress[]. Absent that broker the sandbox has no network. Sharing the host netns is refused by design.`);
118
133
  }
119
134
  return {
120
135
  argv,
@@ -1 +1 @@
1
- {"version":3,"file":"bwrap.js","sourceRoot":"","sources":["../../../src/policy/adapters/bwrap.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"bwrap.js","sourceRoot":"","sources":["../../../src/policy/adapters/bwrap.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,32 @@
1
+ import { NormalizedPolicy } from '../ir.js';
2
+ import type { EgressRule } from './bwrap.js';
3
+ export interface DockerArtifact {
4
+ /** argv, ready for execFile("docker", ["run", ...argv, image, ...cmd]). */
5
+ argv: string[];
6
+ /** Network egress rules. Empty = no net allowed (host SHOULD use --network none). */
7
+ egress: EgressRule[];
8
+ /** Env vars the host must inject (names only; values resolved out-of-band). */
9
+ envInjections: string[];
10
+ /** Declared assertions — emitted as metadata, not enforced here. */
11
+ assertions: {
12
+ id: string;
13
+ description: string;
14
+ }[];
15
+ /** Human-readable diagnostics for audit logs / PR review. */
16
+ notes: string[];
17
+ }
18
+ export interface DockerOptions {
19
+ /**
20
+ * Whether to emit `--read-only` on the root filesystem. Default true.
21
+ * Disable only if your image cannot run with a read-only rootfs (rare).
22
+ */
23
+ readOnlyRootfs?: boolean;
24
+ /**
25
+ * Optional user to run as inside the container, e.g. "1000:1000". Default
26
+ * unset (image-defined). Recommended in production; left unset here so the
27
+ * adapter's output is portable across images.
28
+ */
29
+ user?: string;
30
+ }
31
+ export declare function lowerToDocker(policy: NormalizedPolicy, opts?: DockerOptions): DockerArtifact;
32
+ //# sourceMappingURL=docker.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"docker.d.ts","sourceRoot":"","sources":["../../../src/policy/adapters/docker.ts"],"names":[],"mappings":"AAqBA,OAAO,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AAC5C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAE7C,MAAM,WAAW,cAAc;IAC7B,2EAA2E;IAC3E,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,qFAAqF;IACrF,MAAM,EAAE,UAAU,EAAE,CAAC;IACrB,+EAA+E;IAC/E,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,oEAAoE;IACpE,UAAU,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IAClD,6DAA6D;IAC7D,KAAK,EAAE,MAAM,EAAE,CAAC;CACjB;AAED,MAAM,WAAW,aAAa;IAC5B;;;OAGG;IACH,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB;;;;OAIG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAMD,wBAAgB,aAAa,CAAC,MAAM,EAAE,gBAAgB,EAAE,IAAI,GAAE,aAAkB,GAAG,cAAc,CA+GhG"}
@@ -0,0 +1,143 @@
1
+ // Docker adapter: NormalizedPolicy → `docker run` argv + companion artifacts.
2
+ //
3
+ // Scope parallel to bwrap: emit flags, not images or commands. The host
4
+ // appends the image reference and entrypoint args. The host also chooses
5
+ // which network to attach to (or none) — this adapter only signals that an
6
+ // egress-aware network is required when policy.net is non-empty.
7
+ //
8
+ // What Docker handles natively:
9
+ // - filesystem bind mounts (--volume :ro / :rw)
10
+ // - network isolation (--network none)
11
+ // - env injection (--env)
12
+ // - capability scrub (--cap-drop=ALL, --security-opt=no-new-privileges)
13
+ //
14
+ // What Docker does NOT handle here:
15
+ // - host-level egress allowlisting → EgressRule[] (companion proxy)
16
+ // - seccomp customization beyond default → out of scope for v0.1
17
+ // - secret value resolution → caller pulls from a secret store
18
+ // - inner Chromium-style sandboxes → surfaced as a note; host decides whether
19
+ // to add SYS_ADMIN with a tailored seccomp profile or use a microVM adapter.
20
+ // This adapter refuses to silently elevate.
21
+ const SAFE_DEFAULTS = {
22
+ readOnlyRootfs: true,
23
+ };
24
+ export function lowerToDocker(policy, opts = {}) {
25
+ const argv = [];
26
+ const notes = [];
27
+ const readOnlyRootfs = opts.readOnlyRootfs ?? SAFE_DEFAULTS.readOnlyRootfs;
28
+ // ---------- baseline hardening ----------
29
+ argv.push('--rm');
30
+ argv.push('--cap-drop', 'ALL');
31
+ argv.push('--security-opt', 'no-new-privileges');
32
+ if (readOnlyRootfs)
33
+ argv.push('--read-only');
34
+ // Tmpfs for /tmp unless a declared fs root covers it (mirror of bwrap logic).
35
+ const tmpCovered = policy.fs.some((fs) => {
36
+ const host = dirForBind(fs.path);
37
+ return host === '/tmp' || host.startsWith('/tmp/');
38
+ });
39
+ if (!tmpCovered)
40
+ argv.push('--tmpfs', '/tmp');
41
+ if (opts.user)
42
+ argv.push('--user', opts.user);
43
+ // ---------- network ----------
44
+ // bwrap is binary-share. Docker has the same shape: --network none vs
45
+ // attach-to-an-egress-aware-network. We emit the deny case directly and
46
+ // signal the allow case via a note — choosing the network name is host policy.
47
+ if (policy.net.length === 0) {
48
+ argv.push('--network', 'none');
49
+ }
50
+ else {
51
+ notes.push(`net: ${policy.net.length} endpoint(s) declared — host MUST attach the container to a network whose egress honors egress[]; do NOT use --network host`);
52
+ }
53
+ // ---------- fs binds ----------
54
+ for (const fs of policy.fs) {
55
+ const hostPath = dirForBind(fs.path);
56
+ const writable = fs.actions.includes('write') || fs.actions.includes('create') || fs.actions.includes('delete');
57
+ argv.push('--volume', `${hostPath}:${hostPath}:${writable ? 'rw' : 'ro'}`);
58
+ if (fs.isGlob) {
59
+ notes.push(`fs: "${fs.path}" lowered to volume mount "${hostPath}" — Docker mounts directories, not globs. Fine-grained glob enforcement is the server's job.`);
60
+ }
61
+ if (hostPath === '/tmp') {
62
+ notes.push('fs: mounting host /tmp exposes all user tmpfiles to the container; prefer a scoped subdirectory like /tmp/<server-name>.');
63
+ }
64
+ }
65
+ // ---------- clock ----------
66
+ if (policy.clock === 'tzdata') {
67
+ argv.push('--volume', '/usr/share/zoneinfo:/usr/share/zoneinfo:ro');
68
+ argv.push('--volume', '/etc/localtime:/etc/localtime:ro');
69
+ }
70
+ // ---------- exec binaries ----------
71
+ // No mounts to add — the image is responsible for shipping the binary.
72
+ // We surface the declaration so a reviewer can verify the image actually
73
+ // contains it (catches image/manifest drift).
74
+ for (const e of policy.exec) {
75
+ notes.push(`exec: ${e.binary} (must be present in the chosen image)`);
76
+ }
77
+ // ---------- env (declared first so ipc handlers can push injections) ----------
78
+ const envInjections = [];
79
+ for (const e of policy.env)
80
+ envInjections.push(e.name);
81
+ // ---------- ipc ----------
82
+ for (const i of policy.ipc) {
83
+ if (i.endpoint === 'x11') {
84
+ argv.push('--volume', '/tmp/.X11-unix:/tmp/.X11-unix:ro');
85
+ envInjections.push('DISPLAY');
86
+ }
87
+ else if (i.endpoint.startsWith('unix:')) {
88
+ const sock = i.endpoint.slice('unix:'.length);
89
+ argv.push('--volume', `${sock}:${sock}`);
90
+ }
91
+ else if (i.endpoint === 'dbus:session') {
92
+ notes.push('ipc: dbus:session declared — host must mount DBUS_SESSION_BUS_ADDRESS socket');
93
+ }
94
+ else if (i.endpoint === 'dbus:system') {
95
+ argv.push('--volume', '/run/dbus/system_bus_socket:/run/dbus/system_bus_socket');
96
+ }
97
+ else {
98
+ notes.push(`ipc: unrecognized endpoint "${i.endpoint}" — no mount emitted`);
99
+ }
100
+ }
101
+ // ---------- nestedSandbox ----------
102
+ // Chromium and friends fight container isolation similarly to namespaces.
103
+ // We surface the conflict explicitly rather than auto-adding SYS_ADMIN or
104
+ // --privileged. The host knows whether it's running a trusted image.
105
+ if (policy.nestedSandbox) {
106
+ notes.push('nestedSandbox: declared — Docker default seccomp + dropped capabilities may break inner sandboxes (e.g. Chromium). Host MUST decide between (a) granting SYS_ADMIN + a tailored seccomp profile, or (b) using a microVM adapter (Firecracker) instead. capgate refuses to choose for you.');
107
+ }
108
+ // ---------- env injections (emit names; values are caller's job) ----------
109
+ for (const name of envInjections) {
110
+ argv.push('--env', name);
111
+ }
112
+ // ---------- egress ----------
113
+ const egress = policy.net.map((n) => ({
114
+ host: n.host,
115
+ port: n.port,
116
+ blockPrivate: n.blockPrivate,
117
+ }));
118
+ return {
119
+ argv,
120
+ egress,
121
+ envInjections,
122
+ assertions: policy.assertions.map((a) => ({ id: a.id, description: a.description })),
123
+ notes,
124
+ };
125
+ }
126
+ /**
127
+ * Return the directory portion Docker should bind. Mirrors bwrap's logic
128
+ * exactly so the two adapters agree on host-path scope. Kept duplicated (not
129
+ * shared) intentionally: if one adapter ever needs different glob semantics,
130
+ * we want that divergence to be a deliberate edit, not a shared-helper surprise.
131
+ */
132
+ function dirForBind(path) {
133
+ const stripped = path.replace(/\/\*\*?$/, '').replace(/\/\*$/, '');
134
+ if (stripped.endsWith('/'))
135
+ return stripped.slice(0, -1) || '/';
136
+ const lastSlash = stripped.lastIndexOf('/');
137
+ const tail = stripped.slice(lastSlash + 1);
138
+ if (tail.includes('.') && !tail.startsWith('.')) {
139
+ return stripped.slice(0, lastSlash) || '/';
140
+ }
141
+ return stripped || '/';
142
+ }
143
+ //# sourceMappingURL=docker.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"docker.js","sourceRoot":"","sources":["../../../src/policy/adapters/docker.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,24 @@
1
+ import { NormalizedPolicy } from '../ir.js';
2
+ import type { EgressRule } from './bwrap.js';
3
+ export type EgressTarget = 'squid' | 'nftables';
4
+ export interface EgressArtifact {
5
+ /** Which backend this config targets. */
6
+ target: EgressTarget;
7
+ /** The config file contents, ready to write to disk and load into the proxy. */
8
+ config: string;
9
+ /** Suggested filename, e.g. "capgate-egress.squid.conf". */
10
+ filename: string;
11
+ /** Declared rules this target CANNOT honor — surfaced, never silently dropped. */
12
+ unenforceable: {
13
+ rule: EgressRule;
14
+ reason: string;
15
+ }[];
16
+ /** Human-readable diagnostics for audit logs / PR review. */
17
+ notes: string[];
18
+ }
19
+ export interface EgressOptions {
20
+ /** Required — no default. The caller must pick the backend the host runs. */
21
+ target: EgressTarget;
22
+ }
23
+ export declare function lowerToEgress(policy: NormalizedPolicy, opts: EgressOptions): EgressArtifact;
24
+ //# sourceMappingURL=egress.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"egress.d.ts","sourceRoot":"","sources":["../../../src/policy/adapters/egress.ts"],"names":[],"mappings":"AA0BA,OAAO,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AAC5C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAE7C,MAAM,MAAM,YAAY,GAAG,OAAO,GAAG,UAAU,CAAC;AAEhD,MAAM,WAAW,cAAc;IAC7B,yCAAyC;IACzC,MAAM,EAAE,YAAY,CAAC;IACrB,gFAAgF;IAChF,MAAM,EAAE,MAAM,CAAC;IACf,4DAA4D;IAC5D,QAAQ,EAAE,MAAM,CAAC;IACjB,kFAAkF;IAClF,aAAa,EAAE;QAAE,IAAI,EAAE,UAAU,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IACtD,6DAA6D;IAC7D,KAAK,EAAE,MAAM,EAAE,CAAC;CACjB;AAED,MAAM,WAAW,aAAa;IAC5B,6EAA6E;IAC7E,MAAM,EAAE,YAAY,CAAC;CACtB;AAQD,wBAAgB,aAAa,CAAC,MAAM,EAAE,gBAAgB,EAAE,IAAI,EAAE,aAAa,GAAG,cAAc,CAe3F"}
@@ -0,0 +1,194 @@
1
+ // Egress adapter: NormalizedPolicy → static proxy config blob.
2
+ //
3
+ // This is the third lowering lane (see README "Why egress will lower to a proxy
4
+ // config, not a proxy"). bwrap and docker emit argv; this adapter emits a config
5
+ // *file* for a proxy the HOST already runs. Same compiler-not-runtime contract:
6
+ // capgate compiles policy.net (+ blockPrivate) into a config, and the host wires
7
+ // it into squid / nftables. We never open a socket.
8
+ //
9
+ // What this lane handles:
10
+ // - squid: allowlist by hostname via CONNECT (no TLS interception)
11
+ // - nftables: allowlist by IP+port in-kernel (bypass-proof) + blockPrivate drops
12
+ // - the blockPrivate RFC1918/loopback deny that bwrap/docker can only assert
13
+ //
14
+ // What this lane does NOT handle:
15
+ // - RUNNING the proxy — the host owns the long-lived process; we emit a file.
16
+ // - DNS resolution — nftables filters IPs, not names; hostname rules that it
17
+ // cannot express are surfaced in unenforceable[], never silently dropped.
18
+ // - TLS interception / secret inspection — out of scope for v0.1. squid here
19
+ // is CONNECT-allowlist only; it sees SNI/host, never plaintext.
20
+ //
21
+ // Net posture (fail-closed). Every emitted config terminates or defaults in
22
+ // deny/drop: squid ends in an unconditional `http_access deny all`, nftables
23
+ // sets `policy drop`. No code path produces an allow-all config. An empty
24
+ // policy.net compiles to a deny-ALL config — mirroring how bwrap/docker treat
25
+ // net==0 (`--unshare-net` / `--network none`): declaring nothing grants nothing.
26
+ // Private-range destinations the blockPrivate flag must deny. Kept as two lists
27
+ // because squid and nftables spell them differently (squid uses one `dst` acl
28
+ // covering both families; nftables splits ip / ip6 daddr sets).
29
+ const PRIVATE_V4 = ['10.0.0.0/8', '172.16.0.0/12', '192.168.0.0/16', '127.0.0.0/8', '169.254.0.0/16'];
30
+ const PRIVATE_V6 = ['::1/128', 'fc00::/7', 'fe80::/10'];
31
+ export function lowerToEgress(policy, opts) {
32
+ const rules = policy.net.map((n) => ({
33
+ host: n.host,
34
+ port: n.port,
35
+ blockPrivate: n.blockPrivate,
36
+ }));
37
+ // blockPrivate is OR-ed across the policy: if ANY declared rule asks for the
38
+ // private-range block, the whole config enforces it (deny wins, so a single
39
+ // rule cannot opt the proxy out of a sibling rule's blockPrivate).
40
+ const blockPrivate = rules.some((r) => r.blockPrivate);
41
+ if (opts.target === 'squid')
42
+ return lowerToSquid(rules, blockPrivate);
43
+ if (opts.target === 'nftables')
44
+ return lowerToNftables(rules, blockPrivate);
45
+ // Fail-closed on an unknown target rather than emitting a permissive default.
46
+ throw new Error(`[EGRESS_UNKNOWN_TARGET] unsupported egress target "${opts.target}"`);
47
+ }
48
+ // ---------------------------------------------------------------------------
49
+ // squid — allowlist by hostname via CONNECT, no TLS interception
50
+ // ---------------------------------------------------------------------------
51
+ //
52
+ // squid matches on the CONNECT host, so it allowlists by DOMAIN cleanly and is
53
+ // the right target for rotating-CDN hostnames (api.github.com). IP-literal hosts
54
+ // are matched with a `dst` acl instead of `dstdomain`. The deny-all terminator
55
+ // is the last line, unconditional — nothing can be allowed past it.
56
+ function lowerToSquid(rules, blockPrivate) {
57
+ const notes = [];
58
+ const unenforceable = [];
59
+ const lines = ['# capgate-egress.squid.conf (generated — do not edit)'];
60
+ // ---------- blockPrivate (deny FIRST so it wins over the allows below) ----------
61
+ if (blockPrivate) {
62
+ lines.push(`acl to_private dst ${[...PRIVATE_V4, ...PRIVATE_V6].join(' ')}`);
63
+ lines.push('http_access deny to_private');
64
+ }
65
+ // ---------- allow rules ----------
66
+ rules.forEach((rule, i) => {
67
+ const dstAcl = `cg_dst_${i}`;
68
+ if (rule.host === '*') {
69
+ // squid can't allowlist "any host" as a *restriction*: `*` means the rule
70
+ // permits any destination. We represent it honestly as `dstdomain .`
71
+ // (matches every domain) and lean on blockPrivate to be the only real
72
+ // constraint left. Surfaced as a note so a reviewer sees the degeneracy.
73
+ lines.push(`acl ${dstAcl} dstdomain .`);
74
+ notes.push(`net[${i}]: host "*" degenerates to allow-any (dstdomain .) — squid cannot turn a wildcard into a restriction; only blockPrivate then constrains it.`);
75
+ }
76
+ else if (isIpLiteral(rule.host)) {
77
+ // IP literal: squid matches it with `dst`, not `dstdomain`.
78
+ lines.push(`acl ${dstAcl} dst ${rule.host}`);
79
+ }
80
+ else {
81
+ lines.push(`acl ${dstAcl} dstdomain ${rule.host}`);
82
+ }
83
+ const aclRefs = [dstAcl];
84
+ if (rule.port === null) {
85
+ // Any port → no port acl. Noted so the widened scope is auditable.
86
+ notes.push(`net[${i}]: port null (any port) — no port acl emitted for ${rule.host}.`);
87
+ }
88
+ else {
89
+ const portAcl = `cg_port_${i}`;
90
+ lines.push(`acl ${portAcl} port ${rule.port}`);
91
+ aclRefs.push(portAcl);
92
+ }
93
+ lines.push(`http_access allow ${aclRefs.join(' ')} CONNECT`);
94
+ });
95
+ if (rules.length === 0) {
96
+ notes.push('net: no egress declared — all outbound denied (deny-all only).');
97
+ }
98
+ // ---------- fail-closed terminator (unconditional, LAST line) ----------
99
+ lines.push('http_access deny all');
100
+ return {
101
+ target: 'squid',
102
+ config: lines.join('\n'),
103
+ filename: 'capgate-egress.squid.conf',
104
+ unenforceable,
105
+ notes,
106
+ };
107
+ }
108
+ // ---------------------------------------------------------------------------
109
+ // nftables — allowlist by IP+port in-kernel, bypass-proof
110
+ // ---------------------------------------------------------------------------
111
+ //
112
+ // nftables filters packets by destination IP; it cannot resolve or track
113
+ // rotating hostnames in-kernel. So hostname rules are NOT lowered to a (broken)
114
+ // rule — they are pushed to unenforceable[] with a reason pointing at the squid
115
+ // target. blockPrivate drops are emitted FIRST (before accepts), and the chain
116
+ // defaults to `policy drop` so anything not explicitly accepted is dropped.
117
+ function lowerToNftables(rules, blockPrivate) {
118
+ const notes = [];
119
+ const unenforceable = [];
120
+ const body = [];
121
+ // ---------- blockPrivate (drops FIRST, before any accept) ----------
122
+ if (blockPrivate) {
123
+ body.push(` ip daddr { ${PRIVATE_V4.join(', ')} } drop`);
124
+ body.push(` ip6 daddr { ${PRIVATE_V6.join(', ')} } drop`);
125
+ }
126
+ // ---------- accept rules ----------
127
+ rules.forEach((rule, i) => {
128
+ if (rule.host === '*') {
129
+ // Can't allowlist "any host" by IP. The blockPrivate drops above still
130
+ // apply; the allow itself is unenforceable here.
131
+ unenforceable.push({
132
+ rule,
133
+ reason: 'nftables filters IPs, not hostnames; "*" cannot be expressed as an IP allowlist. Use the "squid" target for wildcard/hostname rules.',
134
+ });
135
+ return;
136
+ }
137
+ if (!isIpLiteral(rule.host)) {
138
+ // Hostname (e.g. api.github.com): resolves to rotating CDN IPs that
139
+ // nftables can't track. Surface it; do not emit a broken rule.
140
+ unenforceable.push({
141
+ rule,
142
+ reason: `nftables filters IPs, not hostnames; ${rule.host} resolves to rotating IPs. Use the 'squid' target for hostname rules, or pin to resolved CIDRs.`,
143
+ });
144
+ return;
145
+ }
146
+ // IP literal: pick the family by colon presence and accept it.
147
+ const fam = rule.host.includes(':') ? 'ip6' : 'ip';
148
+ const dport = rule.port === null ? '' : ` tcp dport ${rule.port}`;
149
+ if (rule.port === null) {
150
+ notes.push(`net[${i}]: port null (any port) — accept emitted without tcp dport for ${rule.host}.`);
151
+ }
152
+ body.push(` ${fam} daddr ${rule.host}${dport} accept`);
153
+ });
154
+ if (rules.length === 0) {
155
+ notes.push('net: no egress declared — all outbound denied (policy drop, no accepts).');
156
+ }
157
+ if (unenforceable.length > 0) {
158
+ notes.push(`net: ${unenforceable.length} rule(s) unenforceable by nftables (hostname/wildcard) — see unenforceable[]; compile those to the "squid" target.`);
159
+ }
160
+ // ---------- assemble table (policy drop = fail-closed default) ----------
161
+ const lines = [
162
+ '# capgate-egress.nftables (generated — do not edit)',
163
+ 'table inet capgate {',
164
+ ' chain egress {',
165
+ ' type filter hook output priority 0; policy drop;',
166
+ ...body,
167
+ ' }',
168
+ '}',
169
+ ];
170
+ return {
171
+ target: 'nftables',
172
+ config: lines.join('\n'),
173
+ filename: 'capgate-egress.nftables',
174
+ unenforceable,
175
+ notes,
176
+ };
177
+ }
178
+ /**
179
+ * True if `host` is an IPv4 or IPv6 literal (vs a DNS name). Heuristic, kept
180
+ * deliberately simple: IPv4 is four dot-separated 0-255 octets; IPv6 is "has a
181
+ * colon" (real names never contain colons). This is enough to decide squid
182
+ * `dst` vs `dstdomain` and nftables accept-vs-unenforceable; we do not need a
183
+ * full RFC parser here, and over-classifying a name as an IP would only ever
184
+ * tighten matching, never loosen it.
185
+ */
186
+ function isIpLiteral(host) {
187
+ if (host.includes(':'))
188
+ return true; // IPv6 literal (names never contain ':')
189
+ const octets = host.split('.');
190
+ if (octets.length !== 4)
191
+ return false;
192
+ return octets.every((o) => /^\d{1,3}$/.test(o) && Number(o) <= 255);
193
+ }
194
+ //# sourceMappingURL=egress.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"egress.js","sourceRoot":"","sources":["../../../src/policy/adapters/egress.ts"],"names":[],"mappings":"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"}
@@ -6,4 +6,8 @@ export { compile, parseManifest, normalize } from './compiler.js';
6
6
  export type { RawServerManifest, RawToolManifest } from './compiler.js';
7
7
  export { lowerToBwrap } from './adapters/bwrap.js';
8
8
  export type { BwrapArtifact, BwrapOptions, EgressRule } from './adapters/bwrap.js';
9
+ export { lowerToDocker } from './adapters/docker.js';
10
+ export type { DockerArtifact, DockerOptions } from './adapters/docker.js';
11
+ export { lowerToEgress } from './adapters/egress.js';
12
+ export type { EgressArtifact, EgressOptions, EgressTarget } from './adapters/egress.js';
9
13
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/policy/index.ts"],"names":[],"mappings":"AAGA,YAAY,EACV,UAAU,EACV,cAAc,EACd,QAAQ,EACR,SAAS,EACT,UAAU,EACV,SAAS,EACT,SAAS,EACT,OAAO,EACP,QAAQ,EACR,SAAS,EACT,QAAQ,EACR,QAAQ,EACR,UAAU,EACV,WAAW,EACX,WAAW,EACX,YAAY,EACZ,cAAc,EACd,gBAAgB,GACjB,MAAM,SAAS,CAAC;AACjB,OAAO,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAE1D,OAAO,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAChE,YAAY,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAEhD,OAAO,EAAE,OAAO,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAClE,YAAY,EAAE,iBAAiB,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAExE,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACnD,YAAY,EAAE,aAAa,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/policy/index.ts"],"names":[],"mappings":"AAGA,YAAY,EACV,UAAU,EACV,cAAc,EACd,QAAQ,EACR,SAAS,EACT,UAAU,EACV,SAAS,EACT,SAAS,EACT,OAAO,EACP,QAAQ,EACR,SAAS,EACT,QAAQ,EACR,QAAQ,EACR,UAAU,EACV,WAAW,EACX,WAAW,EACX,YAAY,EACZ,cAAc,EACd,gBAAgB,GACjB,MAAM,SAAS,CAAC;AACjB,OAAO,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAE1D,OAAO,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAChE,YAAY,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAEhD,OAAO,EAAE,OAAO,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAClE,YAAY,EAAE,iBAAiB,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAExE,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACnD,YAAY,EAAE,aAAa,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAEnF,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,YAAY,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAE1E,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,YAAY,EAAE,cAAc,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC"}
@@ -4,4 +4,6 @@ export { CompilationError, isEnforceable } from './ir.js';
4
4
  export { parseCapability, GRAMMAR_VERSION } from './grammar.js';
5
5
  export { compile, parseManifest, normalize } from './compiler.js';
6
6
  export { lowerToBwrap } from './adapters/bwrap.js';
7
+ export { lowerToDocker } from './adapters/docker.js';
8
+ export { lowerToEgress } from './adapters/egress.js';
7
9
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/policy/index.ts"],"names":[],"mappings":"AAAA,6EAA6E;AAC7E,+BAA+B;AAsB/B,OAAO,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAE1D,OAAO,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAGhE,OAAO,EAAE,OAAO,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAGlE,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/policy/index.ts"],"names":[],"mappings":"AAAA,6EAA6E;AAC7E,+BAA+B;AAsB/B,OAAO,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAE1D,OAAO,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAGhE,OAAO,EAAE,OAAO,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAGlE,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAGnD,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAGrD,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC"}