@capsulos/cli 0.1.0 → 0.1.2
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/dist/cli.js +11 -1
- package/dist/cli.js.map +1 -1
- package/dist/config.d.ts +4 -0
- package/dist/config.js.map +1 -1
- package/dist/template.d.ts +16 -0
- package/dist/template.js +39 -6
- package/dist/template.js.map +1 -1
- package/package.json +4 -4
- package/src/cli.ts +10 -1
- package/src/config.ts +4 -0
- package/src/template.test.ts +29 -1
- package/src/template.ts +50 -6
package/dist/cli.js
CHANGED
|
@@ -20,7 +20,8 @@ const log = (msg) => console.error(`[capsulos] ${msg}`);
|
|
|
20
20
|
function usage() {
|
|
21
21
|
console.error(`usage:
|
|
22
22
|
capsulos init [--region us-east-1] [--profile p] [--s3-bucket b ...] [--egress-connector arn]
|
|
23
|
-
capsulos template build <name> --context <dir> [--dockerfile f] [--capsulod bin | --daemon-src dir] [--memory 512]
|
|
23
|
+
capsulos template build <name> --context <dir> [--dockerfile f] [--capsulod bin | --daemon-src dir] [--memory 512]
|
|
24
|
+
[--capabilities default|all] [--tag k=v ...] [--profile p]
|
|
24
25
|
capsulos run <template> [--docker | --profile p]
|
|
25
26
|
capsulos ls <template> [--docker | --profile p]
|
|
26
27
|
capsulos shell <id> [--cmd /bin/bash] [--docker | --profile p]
|
|
@@ -79,12 +80,19 @@ export async function main(argv = process.argv.slice(2)) {
|
|
|
79
80
|
capsulod: { type: 'string' },
|
|
80
81
|
'daemon-src': { type: 'string' },
|
|
81
82
|
memory: { type: 'string' },
|
|
83
|
+
capabilities: { type: 'string' },
|
|
84
|
+
tag: { type: 'string', multiple: true },
|
|
82
85
|
profile: { type: 'string' },
|
|
83
86
|
description: { type: 'string' },
|
|
84
87
|
},
|
|
85
88
|
});
|
|
86
89
|
if (!values.context)
|
|
87
90
|
usage();
|
|
91
|
+
if (values.capabilities && values.capabilities !== 'default' && values.capabilities !== 'all')
|
|
92
|
+
usage();
|
|
93
|
+
const tags = values.tag?.length
|
|
94
|
+
? Object.fromEntries(values.tag.map((t) => { const i = t.indexOf('='); return i > 0 ? [t.slice(0, i), t.slice(i + 1)] : usage(); }))
|
|
95
|
+
: undefined;
|
|
88
96
|
const res = await templateBuild(loadConfig(), {
|
|
89
97
|
name,
|
|
90
98
|
context: values.context,
|
|
@@ -92,6 +100,8 @@ export async function main(argv = process.argv.slice(2)) {
|
|
|
92
100
|
capsulod: values.capsulod,
|
|
93
101
|
daemonSrc: values['daemon-src'],
|
|
94
102
|
memoryMiB: values.memory ? Number(values.memory) : undefined,
|
|
103
|
+
capabilities: values.capabilities,
|
|
104
|
+
tags,
|
|
95
105
|
profile: values.profile,
|
|
96
106
|
description: values.description,
|
|
97
107
|
log,
|
package/dist/cli.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"AAAA,qBAAqB;AACrB,4EAA4E;AAC5E,8CAA8C;AAC9C,mDAAmD;AACnD,2CAA2C;AAC3C,sDAAsD;AACtD,gFAAgF;AAChF,wCAAwC;AACxC,oFAAoF;AACpF,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AACtC,OAAO,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAC3D,OAAO,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAC3D,OAAO,EAAE,OAAO,EAAiB,MAAM,eAAe,CAAC;AACvD,OAAO,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzD,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AACvC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACnC,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAE9C,MAAM,GAAG,GAAG,CAAC,GAAW,EAAE,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,cAAc,GAAG,EAAE,CAAC,CAAC;AAEhE,SAAS,KAAK;IACZ,OAAO,CAAC,KAAK,CAAC
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
|
package/dist/config.d.ts
CHANGED
|
@@ -11,6 +11,10 @@ export interface CapsulosConfig {
|
|
|
11
11
|
executionRoleArn: string;
|
|
12
12
|
/** Optional egress connector (e.g. a VPC connector with a deny-all security group). */
|
|
13
13
|
egressConnectorArn?: string;
|
|
14
|
+
/** Default OS capabilities for `template build` ('default' | 'all'); the flag wins. */
|
|
15
|
+
capabilities?: 'default' | 'all';
|
|
16
|
+
/** Default tags for the images `template build` creates; the flags win. */
|
|
17
|
+
tags?: Record<string, string>;
|
|
14
18
|
}
|
|
15
19
|
export declare const CONFIG_FILE = "capsulos.json";
|
|
16
20
|
export declare function configPath(cwd?: string): string;
|
package/dist/config.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,0EAA0E;AAC1E,2EAA2E;AAC3E,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AACtD,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,qBAAqB,EAAE,MAAM,+BAA+B,CAAC;
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,0EAA0E;AAC1E,2EAA2E;AAC3E,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AACtD,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,qBAAqB,EAAE,MAAM,+BAA+B,CAAC;AAsBtE,MAAM,CAAC,MAAM,WAAW,GAAG,eAAe,CAAC;AAE3C,MAAM,UAAU,UAAU,CAAC,GAAG,GAAG,OAAO,CAAC,GAAG,EAAE;IAC5C,OAAO,OAAO,CAAC,GAAG,EAAE,WAAW,CAAC,CAAC;AACnC,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,GAAG,GAAG,OAAO,CAAC,GAAG,EAAE;IAC5C,IAAI,CAAC;QACH,OAAO,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC,CAAmB,CAAC;IAC7E,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,IAAI,KAAK,CAAC,GAAG,WAAW,iBAAiB,GAAG,gCAAgC,CAAC,CAAC;IACtF,CAAC;AACH,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,GAAmB,EAAE,GAAG,GAAG,OAAO,CAAC,GAAG,EAAE;IACjE,MAAM,CAAC,GAAG,UAAU,CAAC,GAAG,CAAC,CAAC;IAC1B,aAAa,CAAC,CAAC,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC;IACtD,OAAO,CAAC,CAAC;AACX,CAAC;AAED,6EAA6E;AAC7E,MAAM,UAAU,cAAc,CAAC,OAAgB;IAC7C,IAAI,OAAO,CAAC,GAAG,CAAC,iBAAiB,IAAI,CAAC,OAAO;QAAE,OAAO,qBAAqB,EAAE,CAAC;IAC9E,OAAO,qBAAqB,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC;AAC5C,CAAC"}
|
package/dist/template.d.ts
CHANGED
|
@@ -9,13 +9,29 @@ export interface TemplateBuildOptions {
|
|
|
9
9
|
/** Path to the daemon's Go module (defaults to the monorepo's daemon/). */
|
|
10
10
|
daemonSrc?: string;
|
|
11
11
|
memoryMiB?: number;
|
|
12
|
+
/**
|
|
13
|
+
* OS capabilities of the capsules: 'default' (Docker's set) or 'all'
|
|
14
|
+
* (every capability: what kernel mounts — tmpfs, overlay, FUSE/S3 — need).
|
|
15
|
+
* Default 'default': opt into 'all' when the template mounts things,
|
|
16
|
+
* knowing untrusted code inside gets a less hardened guest. Seccomp is off
|
|
17
|
+
* on Lambda either way (docs/lambda-microvms-findings.md).
|
|
18
|
+
*/
|
|
19
|
+
capabilities?: Capabilities;
|
|
20
|
+
/** Tags on the MicrovmImage (cost allocation); `project: capsulos` unless you say otherwise. */
|
|
21
|
+
tags?: Record<string, string>;
|
|
12
22
|
profile?: string;
|
|
13
23
|
description?: string;
|
|
14
24
|
log?: (msg: string) => void;
|
|
15
25
|
}
|
|
26
|
+
export type Capabilities = 'default' | 'all';
|
|
16
27
|
export declare function imageArn(cfg: CapsulosConfig, name: string): string;
|
|
17
28
|
/** Resolve or cross-compile capsulod for linux/arm64 (the service runs Graviton). */
|
|
18
29
|
export declare function resolveCapsulod(opts: Pick<TemplateBuildOptions, 'capsulod' | 'daemonSrc' | 'log'>): string;
|
|
30
|
+
/**
|
|
31
|
+
* Refuse a capsulod that cannot run on the service: not an ELF, not 64-bit,
|
|
32
|
+
* not aarch64. A wrong file makes the build die at /ready with no log at all.
|
|
33
|
+
*/
|
|
34
|
+
export declare function checkCapsulod(path: string): void;
|
|
19
35
|
/** Build the zip the image builder consumes: context + Dockerfile + capsulod at the root. */
|
|
20
36
|
export declare function assembleArtifact(opts: TemplateBuildOptions, capsulod: string): Buffer;
|
|
21
37
|
export declare function templateBuild(cfg: CapsulosConfig, opts: TemplateBuildOptions): Promise<{
|
package/dist/template.js
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
// `capsulos template build <name>`: a Dockerfile becomes a MicrovmImage.
|
|
2
2
|
// 1. assemble a build context: the user's directory + Dockerfile + capsulod
|
|
3
3
|
// 2. zip it and upload to the artifact bucket
|
|
4
|
-
// 3. CreateMicrovmImage (or UpdateMicrovmImage → new version) with
|
|
5
|
-
//
|
|
4
|
+
// 3. CreateMicrovmImage (or UpdateMicrovmImage → new version) with the
|
|
5
|
+
// /ready + /run hooks and, when asked, additionalOsCapabilities=ALL (mounts)
|
|
6
6
|
// 4. poll until CREATED/UPDATED
|
|
7
7
|
// The Dockerfile must end up running capsulod on :8080 (see templates/base).
|
|
8
8
|
import { execFileSync } from 'node:child_process';
|
|
9
|
-
import { cpSync, existsSync, mkdtempSync, readFileSync, rmSync } from 'node:fs';
|
|
9
|
+
import { chmodSync, closeSync, cpSync, existsSync, mkdtempSync, openSync, readFileSync, readSync, rmSync } from 'node:fs';
|
|
10
10
|
import { tmpdir } from 'node:os';
|
|
11
11
|
import { dirname, join, resolve } from 'node:path';
|
|
12
12
|
import { fileURLToPath } from 'node:url';
|
|
@@ -39,6 +39,33 @@ export function resolveCapsulod(opts) {
|
|
|
39
39
|
});
|
|
40
40
|
return out;
|
|
41
41
|
}
|
|
42
|
+
/**
|
|
43
|
+
* Refuse a capsulod that cannot run on the service: not an ELF, not 64-bit,
|
|
44
|
+
* not aarch64. A wrong file makes the build die at /ready with no log at all.
|
|
45
|
+
*/
|
|
46
|
+
export function checkCapsulod(path) {
|
|
47
|
+
const head = Buffer.alloc(20);
|
|
48
|
+
const fd = openSync(path, 'r');
|
|
49
|
+
let n = 0;
|
|
50
|
+
try {
|
|
51
|
+
n = readSync(fd, head, 0, 20, 0);
|
|
52
|
+
}
|
|
53
|
+
finally {
|
|
54
|
+
closeSync(fd);
|
|
55
|
+
}
|
|
56
|
+
const why = (() => {
|
|
57
|
+
if (n < 20 || head.readUInt32BE(0) !== 0x7f454c46)
|
|
58
|
+
return 'not an ELF binary';
|
|
59
|
+
if (head[4] !== 2)
|
|
60
|
+
return 'not a 64-bit binary';
|
|
61
|
+
if (head.readUInt16LE(18) !== 0xb7)
|
|
62
|
+
return 'not built for aarch64 (Lambda MicroVMs run Graviton)';
|
|
63
|
+
return null;
|
|
64
|
+
})();
|
|
65
|
+
if (why) {
|
|
66
|
+
throw new Error(`capsulod at ${path} is ${why}; download capsulod-linux-arm64 from https://github.com/tornoai/capsulos/releases or build it with GOOS=linux GOARCH=arm64`);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
42
69
|
/** Build the zip the image builder consumes: context + Dockerfile + capsulod at the root. */
|
|
43
70
|
export function assembleArtifact(opts, capsulod) {
|
|
44
71
|
const context = resolve(opts.context);
|
|
@@ -50,6 +77,8 @@ export function assembleArtifact(opts, capsulod) {
|
|
|
50
77
|
cpSync(context, stage, { recursive: true });
|
|
51
78
|
cpSync(dockerfile, join(stage, 'Dockerfile'));
|
|
52
79
|
cpSync(capsulod, join(stage, 'capsulod'));
|
|
80
|
+
// A downloaded release asset comes as 0644; COPY keeps the mode and CMD ["capsulod"] then cannot run.
|
|
81
|
+
chmodSync(join(stage, 'capsulod'), 0o755);
|
|
53
82
|
const zipPath = join(stage, '..', `capsulos-${opts.name}.zip`);
|
|
54
83
|
rmSync(zipPath, { force: true });
|
|
55
84
|
execFileSync('zip', ['-q', '-r', '-X', zipPath, '.'], { cwd: stage });
|
|
@@ -65,11 +94,14 @@ export async function templateBuild(cfg, opts) {
|
|
|
65
94
|
const s3 = new S3Client({ region: cfg.region, credentials });
|
|
66
95
|
const mvm = new LambdaMicrovmsClient({ region: cfg.region, credentials });
|
|
67
96
|
const capsulod = resolveCapsulod(opts);
|
|
97
|
+
checkCapsulod(capsulod);
|
|
68
98
|
const zip = assembleArtifact(opts, capsulod);
|
|
69
99
|
const key = `templates/${opts.name}.zip`;
|
|
70
100
|
await s3.send(new PutObjectCommand({ Bucket: cfg.bucket, Key: key, Body: zip }));
|
|
71
101
|
log(`uploaded s3://${cfg.bucket}/${key} (${zip.length} bytes)`);
|
|
72
102
|
const arn = imageArn(cfg, opts.name);
|
|
103
|
+
const capabilities = opts.capabilities ?? cfg.capabilities ?? 'default';
|
|
104
|
+
log(`os capabilities: ${capabilities}${capabilities === 'all' ? ' (kernel mounts allowed, guest less hardened)' : ' (no kernel mounts; pass --capabilities all for tmpfs/overlay/S3)'}`);
|
|
73
105
|
const spec = {
|
|
74
106
|
codeArtifact: { uri: `s3://${cfg.bucket}/${key}` },
|
|
75
107
|
baseImageArn: `arn:aws:lambda:${cfg.region}:aws:microvm-image:al2023-1`,
|
|
@@ -77,8 +109,9 @@ export async function templateBuild(cfg, opts) {
|
|
|
77
109
|
cpuConfigurations: [{ architecture: 'ARM_64' }],
|
|
78
110
|
resources: [{ minimumMemoryInMiB: opts.memoryMiB ?? 512 }],
|
|
79
111
|
// Verified: without this the capsule has Docker's capability set and
|
|
80
|
-
// cannot mount anything (docs/lambda-microvms-findings.md)
|
|
81
|
-
|
|
112
|
+
// cannot mount anything (docs/lambda-microvms-findings.md); with it,
|
|
113
|
+
// every process inside runs with all capabilities (seccomp is off either way).
|
|
114
|
+
...(capabilities === 'all' ? { additionalOsCapabilities: ['ALL'] } : {}),
|
|
82
115
|
hooks: {
|
|
83
116
|
port: HOOKS_PORT,
|
|
84
117
|
microvmHooks: { run: 'ENABLED', runTimeoutInSeconds: 60 },
|
|
@@ -90,7 +123,7 @@ export async function templateBuild(cfg, opts) {
|
|
|
90
123
|
await mvm.send(new CreateMicrovmImageCommand({
|
|
91
124
|
name: opts.name,
|
|
92
125
|
description: opts.description ?? `capsulos template ${opts.name}`,
|
|
93
|
-
tags: { project: 'capsulos' },
|
|
126
|
+
tags: opts.tags ?? cfg.tags ?? { project: 'capsulos' },
|
|
94
127
|
...spec,
|
|
95
128
|
}));
|
|
96
129
|
log(`image ${arn} created; building…`);
|
package/dist/template.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"template.js","sourceRoot":"","sources":["../src/template.ts"],"names":[],"mappings":"AAAA,yEAAyE;AACzE,8EAA8E;AAC9E,gDAAgD;AAChD,
|
|
1
|
+
{"version":3,"file":"template.js","sourceRoot":"","sources":["../src/template.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@capsulos/cli",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"description": "capsulos command line: set up your AWS account, build templates, list and kill capsules.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"keywords": [
|
|
@@ -52,9 +52,9 @@
|
|
|
52
52
|
"@aws-sdk/client-s3": "^3.1158.0",
|
|
53
53
|
"@aws-sdk/client-sts": "^3.1158.0",
|
|
54
54
|
"@aws-sdk/credential-providers": "^3.1158.0",
|
|
55
|
-
"@capsulos/provider-docker": "0.1.
|
|
56
|
-
"@capsulos/provider-lambda": "0.1.
|
|
57
|
-
"@capsulos/sdk": "0.1.
|
|
55
|
+
"@capsulos/provider-docker": "0.1.2",
|
|
56
|
+
"@capsulos/provider-lambda": "0.1.2",
|
|
57
|
+
"@capsulos/sdk": "0.1.2"
|
|
58
58
|
},
|
|
59
59
|
"devDependencies": {
|
|
60
60
|
"@types/node": "^22.20.1",
|
package/src/cli.ts
CHANGED
|
@@ -22,7 +22,8 @@ const log = (msg: string) => console.error(`[capsulos] ${msg}`);
|
|
|
22
22
|
function usage(): never {
|
|
23
23
|
console.error(`usage:
|
|
24
24
|
capsulos init [--region us-east-1] [--profile p] [--s3-bucket b ...] [--egress-connector arn]
|
|
25
|
-
capsulos template build <name> --context <dir> [--dockerfile f] [--capsulod bin | --daemon-src dir] [--memory 512]
|
|
25
|
+
capsulos template build <name> --context <dir> [--dockerfile f] [--capsulod bin | --daemon-src dir] [--memory 512]
|
|
26
|
+
[--capabilities default|all] [--tag k=v ...] [--profile p]
|
|
26
27
|
capsulos run <template> [--docker | --profile p]
|
|
27
28
|
capsulos ls <template> [--docker | --profile p]
|
|
28
29
|
capsulos shell <id> [--cmd /bin/bash] [--docker | --profile p]
|
|
@@ -83,11 +84,17 @@ export async function main(argv = process.argv.slice(2)): Promise<void> {
|
|
|
83
84
|
capsulod: { type: 'string' },
|
|
84
85
|
'daemon-src': { type: 'string' },
|
|
85
86
|
memory: { type: 'string' },
|
|
87
|
+
capabilities: { type: 'string' },
|
|
88
|
+
tag: { type: 'string', multiple: true },
|
|
86
89
|
profile: { type: 'string' },
|
|
87
90
|
description: { type: 'string' },
|
|
88
91
|
},
|
|
89
92
|
});
|
|
90
93
|
if (!values.context) usage();
|
|
94
|
+
if (values.capabilities && values.capabilities !== 'default' && values.capabilities !== 'all') usage();
|
|
95
|
+
const tags = values.tag?.length
|
|
96
|
+
? Object.fromEntries(values.tag.map((t) => { const i = t.indexOf('='); return i > 0 ? [t.slice(0, i), t.slice(i + 1)] : usage(); }))
|
|
97
|
+
: undefined;
|
|
91
98
|
const res = await templateBuild(loadConfig(), {
|
|
92
99
|
name,
|
|
93
100
|
context: values.context,
|
|
@@ -95,6 +102,8 @@ export async function main(argv = process.argv.slice(2)): Promise<void> {
|
|
|
95
102
|
capsulod: values.capsulod,
|
|
96
103
|
daemonSrc: values['daemon-src'],
|
|
97
104
|
memoryMiB: values.memory ? Number(values.memory) : undefined,
|
|
105
|
+
capabilities: values.capabilities as 'default' | 'all' | undefined,
|
|
106
|
+
tags,
|
|
98
107
|
profile: values.profile,
|
|
99
108
|
description: values.description,
|
|
100
109
|
log,
|
package/src/config.ts
CHANGED
|
@@ -18,6 +18,10 @@ export interface CapsulosConfig {
|
|
|
18
18
|
executionRoleArn: string;
|
|
19
19
|
/** Optional egress connector (e.g. a VPC connector with a deny-all security group). */
|
|
20
20
|
egressConnectorArn?: string;
|
|
21
|
+
/** Default OS capabilities for `template build` ('default' | 'all'); the flag wins. */
|
|
22
|
+
capabilities?: 'default' | 'all';
|
|
23
|
+
/** Default tags for the images `template build` creates; the flags win. */
|
|
24
|
+
tags?: Record<string, string>;
|
|
21
25
|
}
|
|
22
26
|
|
|
23
27
|
export const CONFIG_FILE = 'capsulos.json';
|
package/src/template.test.ts
CHANGED
|
@@ -3,7 +3,7 @@ import { mkdtempSync, writeFileSync } from 'node:fs';
|
|
|
3
3
|
import { tmpdir } from 'node:os';
|
|
4
4
|
import { join } from 'node:path';
|
|
5
5
|
import { describe, expect, it } from 'vitest';
|
|
6
|
-
import { assembleArtifact, imageArn } from './template.js';
|
|
6
|
+
import { assembleArtifact, checkCapsulod, imageArn } from './template.js';
|
|
7
7
|
|
|
8
8
|
describe('assembleArtifact', () => {
|
|
9
9
|
it('zips the context with the Dockerfile and capsulod at the root', () => {
|
|
@@ -18,6 +18,9 @@ describe('assembleArtifact', () => {
|
|
|
18
18
|
writeFileSync(zipPath, zip);
|
|
19
19
|
const listing = execFileSync('unzip', ['-Z1', zipPath]).toString();
|
|
20
20
|
expect(listing.split('\n').filter(Boolean).sort()).toEqual(['Dockerfile', 'app.py', 'capsulod']);
|
|
21
|
+
// A downloaded binary arrives 0644; the zip must carry it executable.
|
|
22
|
+
const modes = execFileSync('unzip', ['-Z', zipPath]).toString();
|
|
23
|
+
expect(modes).toMatch(/-rwxr-xr-x.*capsulod/);
|
|
21
24
|
});
|
|
22
25
|
|
|
23
26
|
it('fails without a Dockerfile', () => {
|
|
@@ -26,6 +29,31 @@ describe('assembleArtifact', () => {
|
|
|
26
29
|
});
|
|
27
30
|
});
|
|
28
31
|
|
|
32
|
+
describe('checkCapsulod', () => {
|
|
33
|
+
const elf = (cls: number, machine: number) => {
|
|
34
|
+
const b = Buffer.alloc(64);
|
|
35
|
+
b.writeUInt32BE(0x7f454c46, 0);
|
|
36
|
+
b[4] = cls;
|
|
37
|
+
b.writeUInt16LE(machine, 18);
|
|
38
|
+
return b;
|
|
39
|
+
};
|
|
40
|
+
const file = (content: Buffer | string) => {
|
|
41
|
+
const p = join(mkdtempSync(join(tmpdir(), 'bin-')), 'capsulod');
|
|
42
|
+
writeFileSync(p, content);
|
|
43
|
+
return p;
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
it('accepts a linux/arm64 ELF', () => {
|
|
47
|
+
expect(() => checkCapsulod(file(elf(2, 0xb7)))).not.toThrow();
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it('rejects scripts, 32-bit and x86_64 binaries with a pointer to the fix', () => {
|
|
51
|
+
expect(() => checkCapsulod(file('#!/bin/sh\n'))).toThrow(/not an ELF binary.*releases/);
|
|
52
|
+
expect(() => checkCapsulod(file(elf(1, 0xb7)))).toThrow(/not a 64-bit/);
|
|
53
|
+
expect(() => checkCapsulod(file(elf(2, 0x3e)))).toThrow(/not built for aarch64/);
|
|
54
|
+
});
|
|
55
|
+
});
|
|
56
|
+
|
|
29
57
|
describe('imageArn', () => {
|
|
30
58
|
it('builds the full ARN the API requires', () => {
|
|
31
59
|
expect(imageArn({ region: 'us-east-1', accountId: '123', bucket: '', buildRoleArn: '', executionRoleArn: '' }, 'python')).toBe(
|
package/src/template.ts
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
// `capsulos template build <name>`: a Dockerfile becomes a MicrovmImage.
|
|
2
2
|
// 1. assemble a build context: the user's directory + Dockerfile + capsulod
|
|
3
3
|
// 2. zip it and upload to the artifact bucket
|
|
4
|
-
// 3. CreateMicrovmImage (or UpdateMicrovmImage → new version) with
|
|
5
|
-
//
|
|
4
|
+
// 3. CreateMicrovmImage (or UpdateMicrovmImage → new version) with the
|
|
5
|
+
// /ready + /run hooks and, when asked, additionalOsCapabilities=ALL (mounts)
|
|
6
6
|
// 4. poll until CREATED/UPDATED
|
|
7
7
|
// The Dockerfile must end up running capsulod on :8080 (see templates/base).
|
|
8
8
|
import { execFileSync } from 'node:child_process';
|
|
9
|
-
import { cpSync, existsSync, mkdtempSync, readFileSync, rmSync } from 'node:fs';
|
|
9
|
+
import { chmodSync, closeSync, cpSync, existsSync, mkdtempSync, openSync, readFileSync, readSync, rmSync } from 'node:fs';
|
|
10
10
|
import { tmpdir } from 'node:os';
|
|
11
11
|
import { dirname, join, resolve } from 'node:path';
|
|
12
12
|
import { fileURLToPath } from 'node:url';
|
|
@@ -29,11 +29,23 @@ export interface TemplateBuildOptions {
|
|
|
29
29
|
/** Path to the daemon's Go module (defaults to the monorepo's daemon/). */
|
|
30
30
|
daemonSrc?: string;
|
|
31
31
|
memoryMiB?: number;
|
|
32
|
+
/**
|
|
33
|
+
* OS capabilities of the capsules: 'default' (Docker's set) or 'all'
|
|
34
|
+
* (every capability: what kernel mounts — tmpfs, overlay, FUSE/S3 — need).
|
|
35
|
+
* Default 'default': opt into 'all' when the template mounts things,
|
|
36
|
+
* knowing untrusted code inside gets a less hardened guest. Seccomp is off
|
|
37
|
+
* on Lambda either way (docs/lambda-microvms-findings.md).
|
|
38
|
+
*/
|
|
39
|
+
capabilities?: Capabilities;
|
|
40
|
+
/** Tags on the MicrovmImage (cost allocation); `project: capsulos` unless you say otherwise. */
|
|
41
|
+
tags?: Record<string, string>;
|
|
32
42
|
profile?: string;
|
|
33
43
|
description?: string;
|
|
34
44
|
log?: (msg: string) => void;
|
|
35
45
|
}
|
|
36
46
|
|
|
47
|
+
export type Capabilities = 'default' | 'all';
|
|
48
|
+
|
|
37
49
|
const HOOKS_PORT = 9000;
|
|
38
50
|
const sleep = (ms: number) => new Promise((r) => setTimeout(r, ms));
|
|
39
51
|
|
|
@@ -64,6 +76,32 @@ export function resolveCapsulod(opts: Pick<TemplateBuildOptions, 'capsulod' | 'd
|
|
|
64
76
|
return out;
|
|
65
77
|
}
|
|
66
78
|
|
|
79
|
+
/**
|
|
80
|
+
* Refuse a capsulod that cannot run on the service: not an ELF, not 64-bit,
|
|
81
|
+
* not aarch64. A wrong file makes the build die at /ready with no log at all.
|
|
82
|
+
*/
|
|
83
|
+
export function checkCapsulod(path: string): void {
|
|
84
|
+
const head = Buffer.alloc(20);
|
|
85
|
+
const fd = openSync(path, 'r');
|
|
86
|
+
let n = 0;
|
|
87
|
+
try {
|
|
88
|
+
n = readSync(fd, head, 0, 20, 0);
|
|
89
|
+
} finally {
|
|
90
|
+
closeSync(fd);
|
|
91
|
+
}
|
|
92
|
+
const why = (() => {
|
|
93
|
+
if (n < 20 || head.readUInt32BE(0) !== 0x7f454c46) return 'not an ELF binary';
|
|
94
|
+
if (head[4] !== 2) return 'not a 64-bit binary';
|
|
95
|
+
if (head.readUInt16LE(18) !== 0xb7) return 'not built for aarch64 (Lambda MicroVMs run Graviton)';
|
|
96
|
+
return null;
|
|
97
|
+
})();
|
|
98
|
+
if (why) {
|
|
99
|
+
throw new Error(
|
|
100
|
+
`capsulod at ${path} is ${why}; download capsulod-linux-arm64 from https://github.com/tornoai/capsulos/releases or build it with GOOS=linux GOARCH=arm64`,
|
|
101
|
+
);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
67
105
|
/** Build the zip the image builder consumes: context + Dockerfile + capsulod at the root. */
|
|
68
106
|
export function assembleArtifact(opts: TemplateBuildOptions, capsulod: string): Buffer {
|
|
69
107
|
const context = resolve(opts.context);
|
|
@@ -74,6 +112,8 @@ export function assembleArtifact(opts: TemplateBuildOptions, capsulod: string):
|
|
|
74
112
|
cpSync(context, stage, { recursive: true });
|
|
75
113
|
cpSync(dockerfile, join(stage, 'Dockerfile'));
|
|
76
114
|
cpSync(capsulod, join(stage, 'capsulod'));
|
|
115
|
+
// A downloaded release asset comes as 0644; COPY keeps the mode and CMD ["capsulod"] then cannot run.
|
|
116
|
+
chmodSync(join(stage, 'capsulod'), 0o755);
|
|
77
117
|
const zipPath = join(stage, '..', `capsulos-${opts.name}.zip`);
|
|
78
118
|
rmSync(zipPath, { force: true });
|
|
79
119
|
execFileSync('zip', ['-q', '-r', '-X', zipPath, '.'], { cwd: stage });
|
|
@@ -90,12 +130,15 @@ export async function templateBuild(cfg: CapsulosConfig, opts: TemplateBuildOpti
|
|
|
90
130
|
const mvm = new LambdaMicrovmsClient({ region: cfg.region, credentials });
|
|
91
131
|
|
|
92
132
|
const capsulod = resolveCapsulod(opts);
|
|
133
|
+
checkCapsulod(capsulod);
|
|
93
134
|
const zip = assembleArtifact(opts, capsulod);
|
|
94
135
|
const key = `templates/${opts.name}.zip`;
|
|
95
136
|
await s3.send(new PutObjectCommand({ Bucket: cfg.bucket, Key: key, Body: zip }));
|
|
96
137
|
log(`uploaded s3://${cfg.bucket}/${key} (${zip.length} bytes)`);
|
|
97
138
|
|
|
98
139
|
const arn = imageArn(cfg, opts.name);
|
|
140
|
+
const capabilities: Capabilities = opts.capabilities ?? cfg.capabilities ?? 'default';
|
|
141
|
+
log(`os capabilities: ${capabilities}${capabilities === 'all' ? ' (kernel mounts allowed, guest less hardened)' : ' (no kernel mounts; pass --capabilities all for tmpfs/overlay/S3)'}`);
|
|
99
142
|
const spec = {
|
|
100
143
|
codeArtifact: { uri: `s3://${cfg.bucket}/${key}` },
|
|
101
144
|
baseImageArn: `arn:aws:lambda:${cfg.region}:aws:microvm-image:al2023-1`,
|
|
@@ -103,8 +146,9 @@ export async function templateBuild(cfg: CapsulosConfig, opts: TemplateBuildOpti
|
|
|
103
146
|
cpuConfigurations: [{ architecture: 'ARM_64' as const }],
|
|
104
147
|
resources: [{ minimumMemoryInMiB: opts.memoryMiB ?? 512 }],
|
|
105
148
|
// Verified: without this the capsule has Docker's capability set and
|
|
106
|
-
// cannot mount anything (docs/lambda-microvms-findings.md)
|
|
107
|
-
|
|
149
|
+
// cannot mount anything (docs/lambda-microvms-findings.md); with it,
|
|
150
|
+
// every process inside runs with all capabilities (seccomp is off either way).
|
|
151
|
+
...(capabilities === 'all' ? { additionalOsCapabilities: ['ALL' as const] } : {}),
|
|
108
152
|
hooks: {
|
|
109
153
|
port: HOOKS_PORT,
|
|
110
154
|
microvmHooks: { run: 'ENABLED' as const, runTimeoutInSeconds: 60 },
|
|
@@ -118,7 +162,7 @@ export async function templateBuild(cfg: CapsulosConfig, opts: TemplateBuildOpti
|
|
|
118
162
|
new CreateMicrovmImageCommand({
|
|
119
163
|
name: opts.name,
|
|
120
164
|
description: opts.description ?? `capsulos template ${opts.name}`,
|
|
121
|
-
tags: { project: 'capsulos' },
|
|
165
|
+
tags: opts.tags ?? cfg.tags ?? { project: 'capsulos' },
|
|
122
166
|
...spec,
|
|
123
167
|
}),
|
|
124
168
|
);
|