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.
- package/LICENSE +201 -21
- package/NOTICE +4 -0
- package/README.md +290 -167
- package/dist/cli.js +30 -15
- package/dist/cli.js.map +1 -1
- package/dist/policy/adapters/bwrap.d.ts.map +1 -1
- package/dist/policy/adapters/bwrap.js +26 -11
- package/dist/policy/adapters/bwrap.js.map +1 -1
- package/dist/policy/adapters/docker.d.ts +32 -0
- package/dist/policy/adapters/docker.d.ts.map +1 -0
- package/dist/policy/adapters/docker.js +143 -0
- package/dist/policy/adapters/docker.js.map +1 -0
- package/dist/policy/adapters/egress.d.ts +24 -0
- package/dist/policy/adapters/egress.d.ts.map +1 -0
- package/dist/policy/adapters/egress.js +194 -0
- package/dist/policy/adapters/egress.js.map +1 -0
- package/dist/policy/index.d.ts +4 -0
- package/dist/policy/index.d.ts.map +1 -1
- package/dist/policy/index.js +2 -0
- package/dist/policy/index.js.map +1 -1
- package/package.json +66 -64
- package/dist/.tsbuildinfo +0 -1
|
@@ -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
|
|
24
|
-
//
|
|
25
|
-
//
|
|
26
|
-
//
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
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
|
-
//
|
|
110
|
-
//
|
|
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
|
|
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":"AAAA,2EAA2E;AAC3E,EAAE;AACF,oEAAoE;AACpE,kDAAkD;AAClD,sDAAsD;AACtD,uDAAuD;AACvD,EAAE;AACF,sBAAsB;AACtB,wEAAwE;AACxE,+DAA+D;AAC/D,wEAAwE;AACxE,EAAE;AACF,2EAA2E;AAC3E,iFAAiF;AACjF,wDAAwD;AA8BxD,MAAM,qBAAqB,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,UAAU,EAAE,sBAAsB,CAAC,CAAC;AAE9G,MAAM,UAAU,YAAY,CAAC,MAAwB,EAAE,OAAqB,EAAE;IAC5E,MAAM,IAAI,GAAa,EAAE,CAAC;IAC1B,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,MAAM,YAAY,GAAG,IAAI,CAAC,YAAY,IAAI,qBAAqB,CAAC;IAChE,MAAM,SAAS,GAAG,IAAI,CAAC,SAAS,IAAI,MAAM,CAAC,aAAa,CAAC;IAEzD,wCAAwC;IACxC,
|
|
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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"}
|
package/dist/policy/index.d.ts
CHANGED
|
@@ -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"}
|
package/dist/policy/index.js
CHANGED
|
@@ -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
|
package/dist/policy/index.js.map
CHANGED
|
@@ -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"}
|