Skip to content

Commit d892aa2

Browse files
reiabreuclaude
andauthored
Test the OCI launch-command username and mount-source enforcement (#9014)
Extracts the launch-command username match from main.c into oci_launch_cmd_matches_user (defined in oci_launch_cmd.c, which is linked into the test binary; main.c is not) so it can be unit-tested, and adds two tests: - test_oci_launch_cmd_matches_user covers the username match directly. - test_oci_parse_launch_cmd_mounts parses a launch command that is valid except for its bind-mount source and asserts parse_oci_launch_cmd rejects a source outside the configured directories while accepting one under them, exercising the is_valid_mount enforcement that earlier tests only reached at the is_valid_mount_source helper level. Follow-up to the worker-launcher OCI changes in #9008 and #9010. Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
1 parent e33d74d commit d892aa2

4 files changed

Lines changed: 91 additions & 1 deletion

File tree

storm-core/src/native/worker-launcher/impl/main.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -308,7 +308,7 @@ int main(int argc, char **argv) {
308308
oci_launch_cmd* olc = parse_oci_launch_cmd(command_file);
309309
if (olc == NULL) {
310310
exit_code = INVALID_CONFIG_FILE;
311-
} else if (strcmp(olc->username, user_name) != 0) {
311+
} else if (!oci_launch_cmd_matches_user(olc, user_name)) {
312312
// The launch command file's username must match the user passed to
313313
// the worker-launcher on the command line.
314314
fprintf(ERRORFILE, "ERROR: OCI command file username %s does not match %s\n",

storm-core/src/native/worker-launcher/impl/oci/oci_launch_cmd.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -393,6 +393,11 @@ bool is_valid_mount_source(const char* source) {
393393
return allowed;
394394
}
395395

396+
bool oci_launch_cmd_matches_user(const oci_launch_cmd* olc, const char* user_name) {
397+
return olc != NULL && olc->username != NULL && user_name != NULL
398+
&& strcmp(olc->username, user_name) == 0;
399+
}
400+
396401
static bool is_valid_mount(const cJSON* mount) {
397402
if (!cJSON_IsObject(mount)) {
398403
fputs("ERROR: OCI config mount entry is not an object\n", ERRORFILE);

storm-core/src/native/worker-launcher/impl/oci/oci_launch_cmd.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,4 +96,11 @@ bool is_mount_source_under(const char* source, const char* allowed);
9696
*/
9797
bool is_valid_mount_source(const char* source);
9898

99+
/**
100+
* Return true if the launch command's username is present and equals
101+
* user_name. Used to check that a launch command file's username matches the
102+
* user the worker-launcher was invoked for.
103+
*/
104+
bool oci_launch_cmd_matches_user(const oci_launch_cmd* olc, const char* user_name);
105+
99106
#endif /* OCI_OCI_LAUNCH_CMD_H */

storm-core/src/native/worker-launcher/test/test-worker-launcher.c

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -441,6 +441,80 @@ void test_get_values_degenerate() {
441441
printf("get_values degenerate-value handling OK\n");
442442
}
443443

444+
// oci_launch_cmd_matches_user backs the check in main.c that a launch command
445+
// file's username matches the user the worker-launcher was invoked for.
446+
void test_oci_launch_cmd_matches_user() {
447+
oci_launch_cmd olc;
448+
memset(&olc, 0, sizeof(olc));
449+
olc.username = "alice";
450+
EXPECT(oci_launch_cmd_matches_user(&olc, "alice"), "matching username rejected");
451+
EXPECT(!oci_launch_cmd_matches_user(&olc, "bob"), "mismatched username accepted");
452+
olc.username = NULL;
453+
EXPECT(!oci_launch_cmd_matches_user(&olc, "alice"), "command with no username accepted");
454+
EXPECT(!oci_launch_cmd_matches_user(NULL, "alice"), "null command accepted");
455+
}
456+
457+
// Write a launch command file that is valid except for the bind-mount source,
458+
// which is set to mount_source.
459+
static void write_olc_file(const char* path, const char* mount_source) {
460+
FILE* f = fopen(path, "w");
461+
EXPECT(f != NULL, "could not write launch command file");
462+
fprintf(f,
463+
"{\n"
464+
" \"username\": \"olcuser\",\n"
465+
" \"containerId\": \"85afb30b-286e-4d32-ab7a-9d5aad89bb88\",\n"
466+
" \"pidFile\": \"" TEST_ROOT "/olc/pid\",\n"
467+
" \"containerScriptPath\": \"" TEST_ROOT "/olc/script.sh\",\n"
468+
" \"reapLayerKeepCount\": 0,\n"
469+
" \"layers\": [ { \"mediaType\": \"application/vnd.squashfs\", \"path\": \"/layer\" } ],\n"
470+
" \"ociRuntimeConfig\": {\n"
471+
" \"linux\": { \"cgroupsPath\": \"/storm\" },\n"
472+
" \"process\": { \"args\": [\"/bin/true\"], \"cwd\": \"/\", \"env\": [\"A=B\"] },\n"
473+
" \"mounts\": [ { \"type\": \"bind\", \"source\": \"%s\", \"destination\": \"/dst\", \"options\": [\"rbind\", \"rprivate\"] } ]\n"
474+
" }\n"
475+
"}\n", mount_source);
476+
fclose(f);
477+
}
478+
479+
// parse_oci_launch_cmd runs the bind-mount allow-list check (is_valid_mount ->
480+
// is_valid_mount_source) as part of validation, so a launch command whose mount
481+
// source is outside the configured directories must fail to parse. The source
482+
// is resolved with realpath, so build a real tree; run in a child so the
483+
// temporary config does not leak into later tests.
484+
void test_oci_parse_launch_cmd_mounts() {
485+
const char* base = TEST_ROOT "/olc";
486+
const char* allowed = TEST_ROOT "/olc/allowed";
487+
const char* good_src = TEST_ROOT "/olc/allowed/mount.conf";
488+
const char* bad_src = TEST_ROOT "/olc/outside.conf";
489+
EXPECT(mkdir(base, 0755) == 0 || errno == EEXIST, "could not create olc base");
490+
EXPECT(mkdir(allowed, 0755) == 0 || errno == EEXIST, "could not create allowed dir");
491+
FILE* g = fopen(good_src, "w"); EXPECT(g != NULL, "could not create mount.conf"); fclose(g);
492+
FILE* b = fopen(bad_src, "w"); EXPECT(b != NULL, "could not create outside.conf"); fclose(b);
493+
494+
const char* cfg = TEST_ROOT "/olc/wl.cfg";
495+
FILE* c = fopen(cfg, "w");
496+
EXPECT(c != NULL, "could not write wl.cfg");
497+
fprintf(c, "min.user.id=%d\n", getuid());
498+
fprintf(c, "worker.launcher.oci.allowed.mount.source.dirs=%s\n", allowed);
499+
fclose(c);
500+
read_config(cfg);
501+
502+
const char* good_cmd = TEST_ROOT "/olc/good.json";
503+
const char* bad_cmd = TEST_ROOT "/olc/bad.json";
504+
write_olc_file(good_cmd, good_src);
505+
write_olc_file(bad_cmd, bad_src);
506+
507+
oci_launch_cmd* olc = parse_oci_launch_cmd(good_cmd);
508+
EXPECT(olc != NULL, "launch command with an allowed mount source rejected");
509+
free_oci_launch_cmd(olc);
510+
511+
// the key case: the mount hookup must reject a source outside the allowed dirs
512+
olc = parse_oci_launch_cmd(bad_cmd);
513+
EXPECT(olc == NULL, "launch command with a mount source outside the allowed dirs accepted");
514+
515+
printf("parse_oci_launch_cmd mount-source enforcement OK\n");
516+
}
517+
444518
int main(int argc, char **argv) {
445519
LOGFILE = stdout;
446520
ERRORFILE = stderr;
@@ -496,7 +570,11 @@ int main(int argc, char **argv) {
496570
printf("\nTesting mount path helpers\n");
497571
test_mount_path_helpers();
498572

573+
printf("\nTesting oci_launch_cmd_matches_user\n");
574+
test_oci_launch_cmd_matches_user();
575+
499576
run_test_in_child("test_mount_source_allowed_dirs", test_mount_source_allowed_dirs);
577+
run_test_in_child("test_oci_parse_launch_cmd_mounts", test_oci_parse_launch_cmd_mounts);
500578

501579
run_test_in_child("test_signal_container", test_signal_container);
502580
run_test_in_child("test_signal_container_group", test_signal_container_group);

0 commit comments

Comments
 (0)