YARN-9884. Make container-executor mount logic modular

Contributed by Eric Badger
This commit is contained in:
Eric Yang 2019-10-18 19:09:20 -04:00
parent 6d92aa7c30
commit 72b1bed998
10 changed files with 929 additions and 455 deletions

View File

@ -137,6 +137,7 @@ add_library(container
main/native/container-executor/impl/modules/fpga/fpga-module.c
main/native/container-executor/impl/modules/devices/devices-module.c
main/native/container-executor/impl/utils/docker-util.c
main/native/container-executor/impl/utils/mount-utils.c
)
add_executable(container-executor

View File

@ -1367,7 +1367,7 @@ char **construct_docker_command(const char *command_file) {
ret = get_docker_command(command_file, &CFG, &buffer);
if (ret != 0) {
fprintf(ERRORFILE, "Error constructing docker command, docker error code=%d, error message='%s'\n", ret,
get_docker_error_message(ret));
get_error_message(ret));
exit(DOCKER_RUN_FAILED);
}
@ -1414,7 +1414,7 @@ int exec_container(const char *command_file) {
if (ret != 0) {
free_configuration(&command_config);
free(docker_binary);
return INVALID_COMMAND_FILE;
return INVALID_DOCKER_COMMAND_FILE;
}
char *value = get_configuration_value("docker-command", DOCKER_COMMAND_FILE_SECTION, &command_config);

View File

@ -788,6 +788,11 @@ int main(int argc, char **argv) {
break;
}
if (exit_code) {
fprintf(ERRORFILE, "Nonzero exit code=%d, error message='%s'\n", exit_code,
get_error_message(exit_code));
}
flush_and_close_log_files();
return exit_code;
}

View File

@ -195,3 +195,140 @@ void quote_and_append_arg(char **str, size_t *size, const char* param, const cha
sprintf(cur_ptr, append_format, param, tmp);
free(tmp);
}
const char *get_error_message(const int error_code) {
switch (error_code) {
case INVALID_ARGUMENT_NUMBER:
return "Invalid argument number";
case INVALID_COMMAND_PROVIDED:
return "Invalid command provided";
case INVALID_NM_ROOT_DIRS:
return "Invalid NM root dirs";
case SETUID_OPER_FAILED:
return "setuid operation failed";
case UNABLE_TO_EXECUTE_CONTAINER_SCRIPT:
return "Unable to execute container script";
case UNABLE_TO_SIGNAL_CONTAINER:
return "Unable to signal container";
case INVALID_CONTAINER_PID:
return "Invalid container PID";
case OUT_OF_MEMORY:
return "Out of memory";
case INITIALIZE_USER_FAILED:
return "Initialize user failed";
case PATH_TO_DELETE_IS_NULL:
return "Path to delete is null";
case INVALID_CONTAINER_EXEC_PERMISSIONS:
return "Invalid container-executor permissions";
case INVALID_CONFIG_FILE:
return "Invalid config file";
case SETSID_OPER_FAILED:
return "setsid operation failed";
case WRITE_PIDFILE_FAILED:
return "Write to pidfile failed";
case WRITE_CGROUP_FAILED:
return "Write to cgroup failed";
case TRAFFIC_CONTROL_EXECUTION_FAILED:
return "Traffic control execution failed";
case DOCKER_RUN_FAILED:
return "Docker run failed";
case ERROR_OPENING_DOCKER_FILE:
return "Error opening Docker file";
case ERROR_READING_DOCKER_FILE:
return "Error reading Docker file";
case FEATURE_DISABLED:
return "Feature disabled";
case COULD_NOT_CREATE_SCRIPT_COPY:
return "Could not create script copy";
case COULD_NOT_CREATE_CREDENTIALS_COPY:
return "Could not create credentials copy";
case COULD_NOT_CREATE_WORK_DIRECTORIES:
return "Could not create work dirs";
case COULD_NOT_CREATE_APP_LOG_DIRECTORIES:
return "Could not create app log dirs";
case COULD_NOT_CREATE_TMP_DIRECTORIES:
return "Could not create tmp dirs";
case ERROR_CREATE_CONTAINER_DIRECTORIES_ARGUMENTS:
return "Error in create container directories arguments";
case ERROR_SANITIZING_DOCKER_COMMAND:
return "Error sanitizing Docker command";
case DOCKER_IMAGE_INVALID:
return "Docker image invalid";
case ERROR_COMPILING_REGEX:
return "Error compiling regex";
case INVALID_CONTAINER_ID:
return "Invalid container id";
case DOCKER_EXEC_FAILED:
return "Docker exec failed";
case COULD_NOT_CREATE_KEYSTORE_COPY:
return "Could not create keystore copy";
case COULD_NOT_CREATE_TRUSTSTORE_COPY:
return "Could not create truststore copy";
case ERROR_CALLING_SETVBUF:
return "Error calling setvbuf";
case BUFFER_TOO_SMALL:
return "Buffer too small";
case INVALID_MOUNT:
return "Invalid mount";
case INVALID_RO_MOUNT:
return "Invalid read-only mount";
case INVALID_RW_MOUNT:
return "Invalid read-write mount";
case MOUNT_ACCESS_ERROR:
return "Mount access error";
case INVALID_DOCKER_COMMAND_FILE:
return "Invalid docker command file passed";
case INCORRECT_DOCKER_COMMAND:
return "Incorrect command";
case INVALID_DOCKER_CONTAINER_NAME:
return "Invalid docker container name";
case INVALID_DOCKER_IMAGE_NAME:
return "Invalid docker image name";
case INVALID_DOCKER_USER_NAME:
return "Invalid docker user name";
case INVALID_DOCKER_INSPECT_FORMAT:
return "Invalid docker inspect format";
case UNKNOWN_DOCKER_COMMAND:
return "Unknown docker command";
case INVALID_DOCKER_NETWORK:
return "Invalid docker network";
case INVALID_DOCKER_PORTS_MAPPING:
return "Invalid docker ports mapping";
case INVALID_DOCKER_CAPABILITY:
return "Invalid docker capability";
case PRIVILEGED_DOCKER_CONTAINERS_DISABLED:
return "Privileged docker containers are disabled";
case INVALID_DOCKER_DEVICE:
return "Invalid docker device";
case INVALID_DOCKER_STOP_COMMAND:
return "Invalid docker stop command";
case INVALID_DOCKER_KILL_COMMAND:
return "Invalid docker kill command";
case INVALID_DOCKER_VOLUME_DRIVER:
return "Invalid docker volume-driver";
case INVALID_DOCKER_VOLUME_NAME:
return "Invalid docker volume name";
case INVALID_DOCKER_VOLUME_COMMAND:
return "Invalid docker volume command";
case DOCKER_PID_HOST_DISABLED:
return "Docker host pid namespace is disabled";
case INVALID_DOCKER_PID_NAMESPACE:
return "Invalid docker pid namespace";
case INVALID_DOCKER_IMAGE_TRUST:
return "Docker image is not trusted";
case INVALID_DOCKER_TMPFS_MOUNT:
return "Invalid docker tmpfs mount";
case INVALID_DOCKER_RUNTIME:
return "Invalid docker runtime";
case DOCKER_SERVICE_MODE_DISABLED:
return "Docker service mode disabled";
default:
return "Unknown error code";
}
}
int is_regex(const char *str) {
// regex should begin with prefix "regex:"
return (strncmp(str, "regex:", 6) == 0);
}

View File

@ -31,10 +31,10 @@ enum errorcodes {
INVALID_COMMAND_PROVIDED = 3,
// SUPER_USER_NOT_ALLOWED_TO_RUN_TASKS (NOT USED) 4
INVALID_NM_ROOT_DIRS = 5,
SETUID_OPER_FAILED, //6
UNABLE_TO_EXECUTE_CONTAINER_SCRIPT, //7
UNABLE_TO_SIGNAL_CONTAINER, //8
INVALID_CONTAINER_PID, //9
SETUID_OPER_FAILED = 6,
UNABLE_TO_EXECUTE_CONTAINER_SCRIPT = 7,
UNABLE_TO_SIGNAL_CONTAINER = 8,
INVALID_CONTAINER_PID = 9,
// ERROR_RESOLVING_FILE_PATH (NOT_USED) 10
// RELATIVE_PATH_COMPONENTS_IN_FILE_PATH (NOT USED) 11
// UNABLE_TO_STAT_FILE (NOT USED) 12
@ -46,8 +46,8 @@ enum errorcodes {
OUT_OF_MEMORY = 18,
// INITIALIZE_DISTCACHEFILE_FAILED (NOT USED) 19
INITIALIZE_USER_FAILED = 20,
PATH_TO_DELETE_IS_NULL, //21
INVALID_CONTAINER_EXEC_PERMISSIONS, //22
PATH_TO_DELETE_IS_NULL = 21,
INVALID_CONTAINER_EXEC_PERMISSIONS = 22,
// PREPARE_JOB_LOGS_FAILED (NOT USED) 23
INVALID_CONFIG_FILE = 24,
SETSID_OPER_FAILED = 25,
@ -72,7 +72,35 @@ enum errorcodes {
DOCKER_EXEC_FAILED = 44,
COULD_NOT_CREATE_KEYSTORE_COPY = 45,
COULD_NOT_CREATE_TRUSTSTORE_COPY = 46,
ERROR_CALLING_SETVBUF = 47
ERROR_CALLING_SETVBUF = 47,
BUFFER_TOO_SMALL = 48,
INVALID_MOUNT = 49,
INVALID_RO_MOUNT = 50,
INVALID_RW_MOUNT = 51,
MOUNT_ACCESS_ERROR = 52,
INVALID_DOCKER_COMMAND_FILE = 53,
INCORRECT_DOCKER_COMMAND = 54,
INVALID_DOCKER_CONTAINER_NAME = 55,
INVALID_DOCKER_IMAGE_NAME = 56,
INVALID_DOCKER_USER_NAME = 57,
INVALID_DOCKER_INSPECT_FORMAT = 58,
UNKNOWN_DOCKER_COMMAND = 59,
INVALID_DOCKER_NETWORK = 60,
INVALID_DOCKER_PORTS_MAPPING = 61,
INVALID_DOCKER_CAPABILITY = 62,
PRIVILEGED_DOCKER_CONTAINERS_DISABLED = 63,
INVALID_DOCKER_DEVICE = 64,
INVALID_DOCKER_STOP_COMMAND = 65,
INVALID_DOCKER_KILL_COMMAND = 66,
INVALID_DOCKER_VOLUME_DRIVER = 67,
INVALID_DOCKER_VOLUME_NAME = 68,
INVALID_DOCKER_VOLUME_COMMAND = 69,
DOCKER_PID_HOST_DISABLED = 70,
INVALID_DOCKER_PID_NAMESPACE = 71,
INVALID_DOCKER_IMAGE_TRUST = 72,
INVALID_DOCKER_TMPFS_MOUNT = 73,
INVALID_DOCKER_RUNTIME = 74,
DOCKER_SERVICE_MODE_DISABLED = 75
};
/* Macros for min/max. */
@ -169,4 +197,8 @@ inline void* alloc_and_clear_memory(size_t num, size_t size) {
return ret;
}
const char *get_error_message(const int error_code);
int is_regex(const char *str);
#endif

View File

@ -32,6 +32,7 @@
#include <grp.h>
#include <pwd.h>
#include <errno.h>
#include "mount-utils.h"
int entry_point = 0;
@ -40,11 +41,11 @@ static int read_and_verify_command_file(const char *command_file, const char *do
int ret = 0;
ret = read_config(command_file, command_config);
if (ret != 0) {
return INVALID_COMMAND_FILE;
return INVALID_DOCKER_COMMAND_FILE;
}
char *command = get_configuration_value("docker-command", DOCKER_COMMAND_FILE_SECTION, command_config);
if (command == NULL || (strcmp(command, docker_command) != 0)) {
ret = INCORRECT_COMMAND;
ret = INCORRECT_DOCKER_COMMAND;
}
free(command);
return ret;
@ -173,23 +174,12 @@ free_and_exit:
return ret;
}
static int is_regex(const char *str) {
// regex should begin with prefix "regex:"
return (strncmp(str, "regex:", 6) == 0);
}
static int is_valid_tmpfs_mount(const char *mount) {
const char *regex_str = "^/[^:]+$";
// execute_regex_match return 0 is matched success
return execute_regex_match(regex_str, mount) == 0;
}
static int is_volume_name(const char *volume_name) {
const char *regex_str = "^[a-zA-Z0-9]([a-zA-Z0-9_.-]*)$";
// execute_regex_match return 0 is matched success
return execute_regex_match(regex_str, volume_name) == 0;
}
static int is_valid_ports_mapping(const char *ports_mapping) {
const char *regex_str = "^:[0-9]+|^[0-9]+:[0-9]+|^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.)"
"{3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5]):[0-9]+:[0-9]+$";
@ -197,11 +187,6 @@ static int is_valid_ports_mapping(const char *ports_mapping) {
return execute_regex_match(regex_str, ports_mapping) == 0;
}
static int is_volume_name_matched_by_regex(const char* requested, const char* pattern) {
// execute_regex_match return 0 is matched success
return is_volume_name(requested) && (execute_regex_match(pattern + sizeof("regex:"), requested) == 0);
}
static int add_param_to_command_if_allowed(const struct configuration *command_config,
const struct configuration *executor_cfg,
const char *key, const char *allowed_key, const char *param,
@ -322,66 +307,6 @@ static int validate_container_name(const char *container_name) {
return INVALID_DOCKER_CONTAINER_NAME;
}
const char *get_docker_error_message(const int error_code) {
switch (error_code) {
case INVALID_COMMAND_FILE:
return "Invalid command file passed";
case INCORRECT_COMMAND:
return "Incorrect command";
case BUFFER_TOO_SMALL:
return "Command buffer too small";
case INVALID_DOCKER_CONTAINER_NAME:
return "Invalid docker container name";
case INVALID_DOCKER_IMAGE_NAME:
return "Invalid docker image name";
case INVALID_DOCKER_USER_NAME:
return "Invalid docker user name";
case INVALID_DOCKER_INSPECT_FORMAT:
return "Invalid docker inspect format";
case UNKNOWN_DOCKER_COMMAND:
return "Unknown docker command";
case INVALID_DOCKER_NETWORK:
return "Invalid docker network";
case INVALID_DOCKER_PORTS_MAPPING:
return "Invalid docker ports mapping";
case INVALID_DOCKER_CAPABILITY:
return "Invalid docker capability";
case PRIVILEGED_CONTAINERS_DISABLED:
return "Privileged containers are disabled";
case INVALID_DOCKER_MOUNT:
return "Invalid docker mount";
case INVALID_DOCKER_RO_MOUNT:
return "Invalid docker read-only mount";
case INVALID_DOCKER_RW_MOUNT:
return "Invalid docker read-write mount";
case MOUNT_ACCESS_ERROR:
return "Mount access error";
case INVALID_DOCKER_DEVICE:
return "Invalid docker device";
case INVALID_DOCKER_VOLUME_DRIVER:
return "Invalid docker volume-driver";
case INVALID_DOCKER_VOLUME_NAME:
return "Invalid docker volume name";
case INVALID_DOCKER_VOLUME_COMMAND:
return "Invalid docker volume command";
case PID_HOST_DISABLED:
return "Host pid namespace is disabled";
case INVALID_PID_NAMESPACE:
return "Invalid pid namespace";
case INVALID_DOCKER_IMAGE_TRUST:
return "Docker image is not trusted";
case INVALID_DOCKER_TMPFS_MOUNT:
return "Invalid docker tmpfs mount";
case INVALID_DOCKER_RUNTIME:
return "Invalid docker runtime";
case SERVICE_MODE_DISABLED:
return "Service mode disabled";
default:
return "Unknown error";
}
}
int get_max_retries(const struct configuration *conf) {
int retries = 10;
char *max_retries = get_configuration_value(DOCKER_INSPECT_MAX_RETRIES_KEY,
@ -424,7 +349,7 @@ int get_docker_command(const char *command_file, const struct configuration *con
ret = read_config(command_file, &command_config);
if (ret != 0) {
free_configuration(&command_config);
return INVALID_COMMAND_FILE;
return INVALID_DOCKER_COMMAND_FILE;
}
char *docker = get_docker_binary(conf);
@ -905,7 +830,7 @@ int get_docker_exec_command(const char *command_file, const struct configuration
}
}
} else {
ret = INVALID_COMMAND_FILE;
ret = INVALID_DOCKER_COMMAND_FILE;
}
free_and_exit:
free(container_name);
@ -999,7 +924,7 @@ int is_service_mode_enabled(const struct configuration *command_config,
if (is_feature_enabled(DOCKER_SERVICE_MODE_ENABLED_KEY, ret, section)) {
ret = 1;
} else {
ret = SERVICE_MODE_DISABLED;
ret = DOCKER_SERVICE_MODE_DISABLED;
}
}
free(value);
@ -1083,17 +1008,17 @@ static int set_pid_namespace(const struct configuration *command_config,
}
} else {
fprintf(ERRORFILE, "Host pid namespace is disabled\n");
ret = PID_HOST_DISABLED;
ret = DOCKER_PID_HOST_DISABLED;
goto free_and_exit;
}
} else {
fprintf(ERRORFILE, "Host pid namespace is disabled\n");
ret = PID_HOST_DISABLED;
ret = DOCKER_PID_HOST_DISABLED;
goto free_and_exit;
}
} else {
fprintf(ERRORFILE, "Invalid pid namespace\n");
ret = INVALID_PID_NAMESPACE;
ret = INVALID_DOCKER_PID_NAMESPACE;
}
}
@ -1161,164 +1086,6 @@ static int set_env(const struct configuration *command_config, struct args *args
return ret;
}
/**
* Helper function to help normalize mounts for checking if mounts are
* permitted. The function does the following -
* 1. Find the canonical path for mount using realpath
* 2. If the path is a directory, add a '/' at the end (if not present)
* 3. Return a copy of the canonicalised path(to be freed by the caller)
* @param mount path to be canonicalised
* @param isRegexAllowed whether regex matching is allowed for normalize mount
* @return pointer to canonicalised path, NULL on error
*/
static char* normalize_mount(const char* mount, int isRegexAllowed) {
int ret = 0;
struct stat buff;
char *ret_ptr = NULL, *real_mount = NULL;
if (mount == NULL) {
return NULL;
}
real_mount = realpath(mount, NULL);
if (real_mount == NULL) {
// If mount is a valid named volume, just return it and let docker decide
if (is_volume_name(mount)) {
return strdup(mount);
}
// we only allow permitted mount to be REGEX, for permitted mount, we check
// if it's a valid REGEX return; for user mount, we need to strictly check
if (isRegexAllowed) {
if (is_regex(mount)) {
return strdup(mount);
}
}
fprintf(ERRORFILE, "Could not determine real path of mount '%s'\n", mount);
return NULL;
}
ret = stat(real_mount, &buff);
if (ret == 0) {
if (S_ISDIR(buff.st_mode)) {
size_t len = strlen(real_mount);
if (len <= 0) {
free(real_mount);
return NULL;
}
if (real_mount[len - 1] != '/') {
ret_ptr = (char *) alloc_and_clear_memory(len + 2, sizeof(char));
strncpy(ret_ptr, real_mount, len);
ret_ptr[len] = '/';
ret_ptr[len + 1] = '\0';
} else {
ret_ptr = strdup(real_mount);
}
} else {
ret_ptr = strdup(real_mount);
}
} else {
fprintf(ERRORFILE, "Could not stat path '%s'\n", real_mount);
ret_ptr = NULL;
}
free(real_mount);
return ret_ptr;
}
static int normalize_mounts(char **mounts, int isRegexAllowed) {
int i = 0;
char *tmp = NULL;
if (mounts == NULL) {
return 0;
}
for (i = 0; mounts[i] != NULL; ++i) {
tmp = normalize_mount(mounts[i], isRegexAllowed);
if (tmp == NULL) {
return -1;
}
free(mounts[i]);
mounts[i] = tmp;
}
return 0;
}
static int check_mount_permitted(const char **permitted_mounts, const char *requested) {
int i = 0, ret = 0;
size_t permitted_mount_len = 0;
if (permitted_mounts == NULL) {
return 0;
}
char *normalized_path = normalize_mount(requested, 0);
if (normalized_path == NULL) {
return -1;
}
for (i = 0; permitted_mounts[i] != NULL; ++i) {
if (strcmp(normalized_path, permitted_mounts[i]) == 0) {
ret = 1;
break;
}
// if (permitted_mounts[i] is a REGEX): use REGEX to compare; return
if (is_regex(permitted_mounts[i]) &&
is_volume_name_matched_by_regex(normalized_path, permitted_mounts[i])) {
ret = 1;
break;
}
// directory check
permitted_mount_len = strlen(permitted_mounts[i]);
struct stat path_stat;
stat(permitted_mounts[i], &path_stat);
if(S_ISDIR(path_stat.st_mode)) {
if (strncmp(normalized_path, permitted_mounts[i], permitted_mount_len) == 0) {
ret = 1;
break;
}
}
}
free(normalized_path);
return ret;
}
static char* get_mount_source(const char *mount) {
const char *tmp = strchr(mount, ':');
if (tmp == NULL) {
fprintf(ERRORFILE, "Invalid docker mount '%s'\n", mount);
return NULL;
}
size_t len = tmp - mount;
return strndup(mount, len);
}
static char* get_mount_type(const char *mount) {
const char *tmp = strrchr(mount, ':');
if (tmp == NULL) {
fprintf(ERRORFILE, "Invalid docker mount '%s'\n", mount);
return NULL;
}
if (strlen(tmp) < 2) {
fprintf(ERRORFILE, "Invalid docker mount '%s'\n", mount);
return NULL;
}
char *mount_type = strdup(&tmp[1]);
if (strncmp("ro", mount_type, 2) != 0 &&
strncmp("rw", mount_type, 2) != 0) {
fprintf(ERRORFILE, "Invalid docker mount type '%s'\n", mount_type);
free(mount_type);
return NULL;
}
if (strlen(mount_type) > 2) {
if (strlen(mount_type) < 8 ||
(strcmp("shared", mount_type + 3) != 0 &&
strcmp("rshared", mount_type + 3) != 0 &&
strcmp("slave", mount_type + 3) != 0 &&
strcmp("rslave", mount_type + 3) != 0 &&
strcmp("private", mount_type + 3) != 0 &&
strcmp("rprivate", mount_type + 3) != 0)) {
fprintf(ERRORFILE, "Invalid docker mount type '%s'\n", mount_type);
free(mount_type);
return NULL;
}
mount_type[2] = ',';
}
return mount_type;
}
static int add_tmpfs_mounts(const struct configuration *command_config, args *args) {
char **values = get_configuration_values_delimiter("tmpfs", DOCKER_COMMAND_FILE_SECTION, command_config, ",");
int i = 0, ret = 0;
@ -1348,122 +1115,7 @@ free_and_exit:
return ret;
}
static int add_mounts(const struct configuration *command_config, const struct configuration *conf, args *args) {
const char *tmp_path_buffer[2] = {NULL, NULL};
char *mount_src = NULL;
char *mount_type = NULL;
char **permitted_ro_mounts = get_configuration_values_delimiter("docker.allowed.ro-mounts",
CONTAINER_EXECUTOR_CFG_DOCKER_SECTION, conf, ",");
char **permitted_rw_mounts = get_configuration_values_delimiter("docker.allowed.rw-mounts",
CONTAINER_EXECUTOR_CFG_DOCKER_SECTION, conf, ",");
char **values = get_configuration_values_delimiter("mounts", DOCKER_COMMAND_FILE_SECTION, command_config, ",");
char *config_path = get_config_path("");
const char *container_executor_cfg_path = normalize_mount(config_path, 0);
free(config_path);
int i = 0, permitted_rw = 0, permitted_ro = 0, ret = 0;
if (values == NULL) {
goto free_and_exit;
}
// Disable mount volumes if image is not trusted.
if (check_trusted_image(command_config, conf) != 0) {
fprintf(ERRORFILE, "Disable mount volume for untrusted image\n");
// YARN will implicitly bind node manager local directory to
// docker image. This can create file system security holes,
// if docker container has binary to escalate privileges.
// For untrusted image, we drop mounting without reporting
// INVALID_DOCKER_MOUNT messages to allow running untrusted
// image in a sandbox.
ret = 0;
goto free_and_exit;
}
ret = normalize_mounts(permitted_ro_mounts, 1);
ret |= normalize_mounts(permitted_rw_mounts, 1);
if (ret != 0) {
fprintf(ERRORFILE, "Unable to find permitted docker mounts on disk\n");
ret = MOUNT_ACCESS_ERROR;
goto free_and_exit;
}
for (i = 0; values[i] != NULL; i++) {
mount_src = get_mount_source(values[i]);
if (mount_src == NULL) {
fprintf(ERRORFILE, "Invalid docker mount '%s'\n", values[i]);
ret = INVALID_DOCKER_MOUNT;
goto free_and_exit;
}
mount_type = get_mount_type(values[i]);
if (mount_type == NULL) {
fprintf(ERRORFILE, "Invalid docker mount '%s'\n", values[i]);
ret = INVALID_DOCKER_MOUNT;
goto free_and_exit;
}
permitted_rw = check_mount_permitted((const char **) permitted_rw_mounts, mount_src);
permitted_ro = check_mount_permitted((const char **) permitted_ro_mounts, mount_src);
if (permitted_ro == -1 || permitted_rw == -1) {
fprintf(ERRORFILE, "Invalid docker mount '%s', realpath=%s\n", values[i], mount_src);
ret = INVALID_DOCKER_MOUNT;
goto free_and_exit;
}
if (strncmp("rw", mount_type, 2) == 0) {
// rw mount
if (permitted_rw == 0) {
fprintf(ERRORFILE, "Configuration does not allow docker mount '%s', realpath=%s\n", values[i], mount_src);
ret = INVALID_DOCKER_RW_MOUNT;
goto free_and_exit;
} else {
// determine if the user can modify the container-executor.cfg file
tmp_path_buffer[0] = normalize_mount(mount_src, 0);
// just re-use the function, flip the args to check if the container-executor path is in the requested
// mount point
ret = check_mount_permitted(tmp_path_buffer, container_executor_cfg_path);
free((void *) tmp_path_buffer[0]);
if (ret == 1) {
fprintf(ERRORFILE, "Attempting to mount a parent directory '%s' of container-executor.cfg as read-write\n",
values[i]);
ret = INVALID_DOCKER_RW_MOUNT;
goto free_and_exit;
}
}
} else {
// ro mount
if (permitted_ro == 0 && permitted_rw == 0) {
fprintf(ERRORFILE, "Configuration does not allow docker mount '%s', realpath=%s\n", values[i], mount_src);
ret = INVALID_DOCKER_RO_MOUNT;
goto free_and_exit;
}
}
if (strlen(mount_type) > 2) {
// overwrite separator between read mode and propagation option with ','
int mount_type_index = strlen(values[i]) - strlen(mount_type);
values[i][mount_type_index + 2] = ',';
}
ret = add_to_args(args, "-v");
if (ret != 0) {
ret = BUFFER_TOO_SMALL;
goto free_and_exit;
}
ret = add_to_args(args, values[i]);
if (ret != 0) {
ret = BUFFER_TOO_SMALL;
goto free_and_exit;
}
free(mount_src);
free(mount_type);
mount_src = NULL;
mount_type = NULL;
}
free_and_exit:
free(mount_src);
free(mount_type);
free_values(permitted_ro_mounts);
free_values(permitted_rw_mounts);
free_values(values);
free((void *) container_executor_cfg_path);
return ret;
}
static int check_privileges(const char *user) {
int ngroups = 0;
@ -1567,13 +1219,13 @@ static int set_privileged(const struct configuration *command_config, const stru
// Disable set privileged if entry point mode is disabled
if (get_use_entry_point_flag() != 1) {
fprintf(ERRORFILE, "Privileged containers are disabled for non-entry-point mode\n");
ret = PRIVILEGED_CONTAINERS_DISABLED;
ret = PRIVILEGED_DOCKER_CONTAINERS_DISABLED;
goto free_and_exit;
}
// Disable set privileged if image is not trusted.
if (check_trusted_image(command_config, conf) != 0) {
fprintf(ERRORFILE, "Privileged containers are disabled from untrusted source\n");
ret = PRIVILEGED_CONTAINERS_DISABLED;
ret = PRIVILEGED_DOCKER_CONTAINERS_DISABLED;
goto free_and_exit;
}
allowed = check_privileges(user);
@ -1584,17 +1236,17 @@ static int set_privileged(const struct configuration *command_config, const stru
}
} else {
fprintf(ERRORFILE, "Privileged containers are disabled for user: %s\n", user);
ret = PRIVILEGED_CONTAINERS_DISABLED;
ret = PRIVILEGED_DOCKER_CONTAINERS_DISABLED;
goto free_and_exit;
}
} else {
fprintf(ERRORFILE, "Privileged containers are disabled\n");
ret = PRIVILEGED_CONTAINERS_DISABLED;
ret = PRIVILEGED_DOCKER_CONTAINERS_DISABLED;
goto free_and_exit;
}
} else {
fprintf(ERRORFILE, "Privileged containers are disabled\n");
ret = PRIVILEGED_CONTAINERS_DISABLED;
ret = PRIVILEGED_DOCKER_CONTAINERS_DISABLED;
goto free_and_exit;
}
}
@ -1606,6 +1258,287 @@ free_and_exit:
return ret;
}
static char* get_docker_mount_source(const char *mount) {
const char *tmp = strchr(mount, ':');
if (tmp == NULL) {
fprintf(ERRORFILE, "Invalid docker mount '%s'\n", mount);
return NULL;
}
size_t len = tmp - mount;
return strndup(mount, len);
}
static char* get_docker_mount_dest(const char *mount) {
size_t len;
const char *start = strchr(mount, ':') + 1;
if (start == NULL) {
fprintf(ERRORFILE, "Invalid docker mount '%s'\n", mount);
return NULL;
}
const char *end = strchr(start, ':');
if (end == NULL) {
len = strlen(mount) - (start - mount);
} else {
len = end - start;
}
return strndup(start, len);
}
static mount_options* get_docker_mount_options(const char *mount_string) {
char **opts = NULL;
mount_options *options = NULL;
unsigned int num_opts = 0;
char *option = NULL;
int len = 0;
//+1 because we don't care about the ':'
const char *option_string = strrchr(mount_string, ':') + 1;
if (option_string == NULL) {
fprintf(ERRORFILE, "Invalid docker mount '%s'\n", mount_string);
return NULL;
}
options = (mount_options *) calloc(1, sizeof(*options));
if (options == NULL) {
fprintf(ERRORFILE, "Unable to allocate %ld bytes\n", sizeof(*options));
return NULL;
}
len = strlen(option_string);
if (len == 2) {
opts = (char **) calloc(1, sizeof(*opts));
if (opts == NULL) {
fprintf(ERRORFILE, "Unable to allocate %ld bytes\n", sizeof(*opts));
free(options);
return NULL;
}
num_opts = 1;
} else if (len < 8) {
fprintf(ERRORFILE, "Invalid docker mount. Too many options. '%s'\n", mount_string);
free(options);
return NULL;
} else {
opts = (char **) calloc(2, sizeof(*opts));
if (opts == NULL) {
fprintf(ERRORFILE, "Unable to allocate %ld bytes\n", 2 * sizeof(*opts));
free(options);
return NULL;
}
num_opts = 2;
}
options->opts = opts;
options->num_opts = num_opts;
char *option_string_token = strdup(option_string);
option = strtok(option_string_token, "+");
for (unsigned int i = 0; i < options->num_opts; i++) {
if (option == NULL) {
fprintf(ERRORFILE, "Invalid docker mount options '%s'\n", mount_string);
free(option_string_token);
free_mount_options(options);
return NULL;
}
if (strcmp("rw", option) == 0) {
options->rw = 1;
} else if (strcmp("ro", option) == 0) {
options->rw = 0;
} else if (strcmp("shared", option) != 0 &&
(strcmp("rshared", option) != 0) &&
(strcmp("slave", option) != 0) &&
(strcmp("rslave", option) != 0) &&
(strcmp("private", option) != 0) &&
(strcmp("rprivate", option) != 0)) {
fprintf(ERRORFILE, "Invalid docker mount options '%s'\n", mount_string);
free(option_string_token);
free_mount_options(options);
return NULL;
}
options->opts[i] = strdup(option);
option = strtok(NULL, "+");
}
free(option_string_token);
return options;
}
static char* get_docker_mount_options_string(mount_options *options) {
char *options_string = NULL;
int len = 0;
int idx = 0;
unsigned int i;
for (i = 0; i < options->num_opts; i++) {
len += strlen(options->opts[i]);
}
len += i; // i-1 commas plus 1 for NUL termination
options_string = (char *) calloc(len, sizeof(*options_string));
if (options_string == NULL) {
fputs("Unable to allocate memory\n", ERRORFILE);
return NULL;
}
idx += sprintf(options_string, "%s", options->opts[0]);
for (i = 1; i < options->num_opts; i++) {
idx += sprintf(options_string + idx, ",%s", options->opts[i]);
}
return options_string;
}
static int add_mounts_to_docker_args(mount *mounts, unsigned int num_mounts, args *args) {
int ret = 0, len;
unsigned int i;
char *mount_string = NULL;
char *options_string = NULL;
if (mounts == NULL) {
return ret;
}
for (i = 0; i < num_mounts; i++) {
ret = add_to_args(args, "-v");
if (ret != 0) {
ret = BUFFER_TOO_SMALL;
return ret;
}
options_string = get_docker_mount_options_string(mounts[i].options);
//magic number '+3': +1 for both ':', and +1 for NULL termination
len = strlen(mounts[i].src) + strlen(mounts[i].dest) + strlen(options_string) + 3;
mount_string = (char *) calloc(len, sizeof(*mount_string));
snprintf(mount_string, len, "%s:%s:%s", mounts[i].src, mounts[i].dest, options_string);
ret = add_to_args(args, mount_string);
if (ret != 0) {
ret = BUFFER_TOO_SMALL;
goto free_and_exit;
}
free(mount_string);
free(options_string);
}
return ret;
free_and_exit:
free(mount_string);
free(options_string);
return ret;
}
static int get_docker_mounts(mount *mounts, char **mounts_string, unsigned int num_mounts) {
unsigned int i;
int ret = 0;
char *src, *dest;
mount_options *options = NULL;
if (mounts_string == NULL) {
fprintf(ERRORFILE, "Unable to normalize container-executor.cfg path\n");
ret = MOUNT_ACCESS_ERROR;
goto free_and_exit;
}
for (i = 0; i < num_mounts; i++) {
src = get_docker_mount_source(mounts_string[i]);
if (src == NULL) {
fprintf(ERRORFILE, "Invalid mount '%s'\n", mounts_string[i]);
ret = INVALID_MOUNT;
goto free_and_exit;
}
mounts[i].src = src;
dest = get_docker_mount_dest(mounts_string[i]);
if (dest == NULL) {
fprintf(ERRORFILE, "Invalid mount '%s'\n", mounts_string[i]);
ret = INVALID_MOUNT;
goto free_and_exit;
}
mounts[i].dest = dest;
options = get_docker_mount_options(mounts_string[i]);
if (options == NULL) {
fprintf(ERRORFILE, "Invalid mount '%s'\n", mounts_string[i]);
ret = INVALID_MOUNT;
goto free_and_exit;
}
mounts[i].options = options;
}
free_and_exit:
return ret;
}
static int add_docker_mounts(const struct configuration *command_config, const struct configuration *conf, args *args) {
mount *mounts = NULL;
unsigned int num_mounts = 0;
int ret;
char **permitted_ro_mounts = NULL;
char **permitted_rw_mounts = NULL;
char **mounts_string = NULL;
permitted_ro_mounts = get_configuration_values_delimiter("docker.allowed.ro-mounts",
CONTAINER_EXECUTOR_CFG_DOCKER_SECTION, conf, ",");
permitted_rw_mounts = get_configuration_values_delimiter("docker.allowed.rw-mounts",
CONTAINER_EXECUTOR_CFG_DOCKER_SECTION, conf, ",");
mounts_string = get_configuration_values_delimiter("mounts", DOCKER_COMMAND_FILE_SECTION, command_config, ",");
if (mounts_string == NULL) {
ret = 0;
goto free_and_exit;
}
while (mounts_string[num_mounts] != NULL) {
num_mounts++;
}
// Disable mount volumes if image is not trusted.
if (check_trusted_image(command_config, conf) != 0) {
fprintf(ERRORFILE, "Disable mount volume for untrusted image\n");
// YARN will implicitly bind node manager local directory to
// docker image. This can create file system security holes,
// if docker container has binary to escalate privileges.
// For untrusted image, we drop mounting without reporting
// INVALID_MOUNT messages to allow running untrusted
// image in a sandbox.
ret = 0;
goto free_and_exit;
}
mounts = (mount *) calloc(num_mounts, sizeof(*mounts));
if (mounts == NULL) {
fprintf(ERRORFILE, "Unable to allocate %ld bytes\n", num_mounts * sizeof(*mounts));
ret = OUT_OF_MEMORY;
goto free_and_exit;
}
ret = get_docker_mounts(mounts, mounts_string, num_mounts);
if (ret != 0) {
goto free_and_exit;
}
ret = validate_mounts(permitted_ro_mounts, permitted_rw_mounts, mounts, num_mounts);
if (ret != 0) {
goto free_and_exit;
}
ret = add_mounts_to_docker_args(mounts, num_mounts, args);
if (ret != 0) {
goto free_and_exit;
}
free_and_exit:
free_values(permitted_ro_mounts);
free_values(permitted_rw_mounts);
free_values(mounts_string);
free_mounts(mounts, num_mounts);
return ret;
}
int get_docker_run_command(const char *command_file, const struct configuration *conf, args *args) {
int ret = 0, i = 0;
char *container_name = NULL, *user = NULL, *image = NULL;
@ -1622,8 +1555,8 @@ int get_docker_run_command(const char *command_file, const struct configuration
}
service_mode_enabled = is_service_mode_enabled(&command_config, conf, args);
if (service_mode_enabled == SERVICE_MODE_DISABLED) {
ret = SERVICE_MODE_DISABLED;
if (service_mode_enabled == DOCKER_SERVICE_MODE_DISABLED) {
ret = DOCKER_SERVICE_MODE_DISABLED;
goto free_and_exit;
}
@ -1721,7 +1654,7 @@ int get_docker_run_command(const char *command_file, const struct configuration
goto free_and_exit;
}
ret = add_mounts(&command_config, conf, args);
ret = add_docker_mounts(&command_config, conf, args);
if (ret != 0) {
goto free_and_exit;
}

View File

@ -45,37 +45,6 @@ typedef struct args {
char *data[DOCKER_ARG_MAX];
} args;
enum docker_error_codes {
INVALID_COMMAND_FILE = 1,
INCORRECT_COMMAND,
BUFFER_TOO_SMALL,
INVALID_DOCKER_CONTAINER_NAME,
INVALID_DOCKER_IMAGE_NAME,
INVALID_DOCKER_USER_NAME,
INVALID_DOCKER_INSPECT_FORMAT,
UNKNOWN_DOCKER_COMMAND,
INVALID_DOCKER_NETWORK,
INVALID_DOCKER_PORTS_MAPPING,
INVALID_DOCKER_CAPABILITY,
PRIVILEGED_CONTAINERS_DISABLED,
INVALID_DOCKER_MOUNT,
INVALID_DOCKER_RO_MOUNT,
INVALID_DOCKER_RW_MOUNT,
MOUNT_ACCESS_ERROR,
INVALID_DOCKER_DEVICE,
INVALID_DOCKER_STOP_COMMAND,
INVALID_DOCKER_KILL_COMMAND,
INVALID_DOCKER_VOLUME_DRIVER,
INVALID_DOCKER_VOLUME_NAME,
INVALID_DOCKER_VOLUME_COMMAND,
PID_HOST_DISABLED,
INVALID_PID_NAMESPACE,
INVALID_DOCKER_IMAGE_TRUST,
INVALID_DOCKER_TMPFS_MOUNT,
INVALID_DOCKER_RUNTIME,
SERVICE_MODE_DISABLED
};
/**
* Get the full path for the docker binary.
* @param conf Configuration for the container-executor

View File

@ -0,0 +1,356 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <sys/types.h>
#include <sys/stat.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "util.h"
#include "mount-utils.h"
#include "configuration.h"
/**
* Function to free an options struct.
* @param options - options struct to be freed.
* @return void.
*/
void free_mount_options(mount_options *options) {
if (options == NULL) {
return;
}
if (options->opts != NULL) {
for (unsigned int i = 0; i < options->num_opts; i++) {
free(options->opts[i]);
}
free(options->opts);
}
free(options);
}
/**
* Function to free an array of mounts.
* @param mounts - Array of mounts to be freed.
* @param num_mounts - Number of mounts to be freed.
* @return void.
*/
void free_mounts(mount *mounts, const unsigned int num_mounts) {
if (mounts == NULL) {
return;
}
for (unsigned int i = 0; i < num_mounts; i++) {
free(mounts[i].src);
free(mounts[i].dest);
free_mount_options(mounts[i].options);
}
free(mounts);
}
/**
* Function to determine whether a string is a volume name.
* @param volume_name - string to check.
* @return 1 on match, 0 on no match.
*/
static int is_volume_name(const char *volume_name) {
const char *regex_str = "^[a-zA-Z0-9]([a-zA-Z0-9_.-]*)$";
// execute_regex_match return 0 is matched success
return execute_regex_match(regex_str, volume_name) == 0;
}
/**
* Function to determine whether a string is a valid volume name and if
* it matches a passed in regex.
* @param requested - string to check.
* @param pattern - regular expression to check against.
* @return 1 on match, 0 on no match.
*/
static int is_volume_name_matched_by_regex(const char* requested, const char* pattern) {
// execute_regex_match return 0 is matched success
return is_volume_name(requested) && (execute_regex_match(pattern + sizeof("regex:"), requested) == 0);
}
/**
* Helper function to help normalize mounts for checking if mounts are
* permitted. The function does the following -
* 1. Find the canonical path for mount using realpath
* 2. If the path is a directory, add a '/' at the end (if not present)
* 3. Return a copy of the canonicalised path(to be freed by the caller)
* @param mount path to be canonicalised.
* @param isRegexAllowed whether regex matching is allowed for normalize mount.
* @return pointer to canonicalised path, NULL on error.
*/
static char* normalize_mount(const char* mount, const int isRegexAllowed) {
int ret = 0;
struct stat buff;
char *ret_ptr = NULL, *real_mount = NULL;
if (mount == NULL) {
return NULL;
}
real_mount = realpath(mount, NULL);
if (real_mount == NULL) {
// If mount is a valid named volume, just return it and let the container runtime decide
if (is_volume_name(mount)) {
ret_ptr = strdup(mount);
goto free_and_exit;
}
// we only allow permitted mount to be REGEX, for permitted mount, we check
// if it's a valid REGEX return; for user mount, we need to strictly check
if (isRegexAllowed) {
if (is_regex(mount)) {
ret_ptr = strdup(mount);
goto free_and_exit;
}
}
fprintf(ERRORFILE, "Could not determine real path of mount '%s'\n", mount);
ret_ptr = NULL;
goto free_and_exit;
}
ret = stat(real_mount, &buff);
if (ret == 0) {
if (S_ISDIR(buff.st_mode)) {
size_t len = strlen(real_mount);
if (len <= 0) {
ret_ptr = NULL;
goto free_and_exit;
}
if (real_mount[len - 1] != '/') {
ret_ptr = (char *) alloc_and_clear_memory(len + 2, sizeof(char));
strncpy(ret_ptr, real_mount, len);
ret_ptr[len] = '/';
ret_ptr[len + 1] = '\0';
} else {
ret_ptr = strdup(real_mount);
}
} else {
ret_ptr = strdup(real_mount);
}
} else {
fprintf(ERRORFILE, "Could not stat path '%s'\n", real_mount);
ret_ptr = NULL;
}
free_and_exit:
free(real_mount);
return ret_ptr;
}
/**
* Function to normalize an array of strings. Each string in the array will
* be normalized to its real path in the file system.
* @param mounts - An array of strings to normalize. The contents of this
* string array will be modified and replaced with their normalized equivalents.
* If a string is replaced, the original string will be freed. The caller is responsible
* for freeing the mounts string array.
* @param isRegexAllowed - Integer to determine whether or not regex is allowed.
* for any of the strings. 1 for allowed, 0 for not allowed.
* @return 0 on success, -1 on failure.
*/
static int normalize_mounts(char **mounts, const int isRegexAllowed) {
unsigned int i = 0;
char *tmp = NULL;
if (mounts == NULL) {
return 0;
}
for (i = 0; mounts[i] != NULL; ++i) {
tmp = normalize_mount(mounts[i], isRegexAllowed);
if (tmp == NULL) {
return -1;
}
free(mounts[i]);
mounts[i] = tmp;
}
return 0;
}
/**
* Function to get the normalized path of the container-executor config file.
* @param container_executor_cfg_path - A pointer to a string. This pointer will
* point to an allocated string containing the normalized path to the container-executor
* config file. The caller is responsible for freeing this memory.
* @return 0 on success, MOUNT_ACCESS_ERROR on failure.
*/
static int get_normalized_config_path(const char **container_executor_cfg_path) {
char *config_path = NULL;
int ret = 0;
config_path = get_config_path("");
*container_executor_cfg_path = normalize_mount(config_path, 0);
if (*container_executor_cfg_path == NULL) {
ret = MOUNT_ACCESS_ERROR;
goto free_and_exit;
}
free_and_exit:
free(config_path);
return ret;
}
/**
* Function to determine whether or not a requested string path is allowed as a mount.
* The requested string will be normalized and checked against the normalized paths in the
* permitted_mounts string array. Volumes that match a regex in the permitted_mounts list
* will also be allowed.
* @param permitted_mounts - An array of strings that define the permitted list of paths
* for the requested string.
* @param requested - A string that is requested to be mounted.
* @return 0 on not permitted, 1 on permitted, -1 on error.
*/
static int check_mount_permitted(const char **permitted_mounts, const char *requested) {
int ret = 0;
unsigned int i;
size_t permitted_mount_len = 0;
if (permitted_mounts == NULL) {
return 0;
}
char *normalized_path = normalize_mount(requested, 0);
if (normalized_path == NULL) {
return -1;
}
for (i = 0; permitted_mounts[i] != NULL; ++i) {
if (strcmp(normalized_path, permitted_mounts[i]) == 0) {
ret = 1;
break;
}
// if (permitted_mounts[i] is a REGEX): use REGEX to compare; return
if (is_regex(permitted_mounts[i]) &&
is_volume_name_matched_by_regex(normalized_path, permitted_mounts[i])) {
ret = 1;
break;
}
// directory check
permitted_mount_len = strlen(permitted_mounts[i]);
struct stat path_stat;
stat(permitted_mounts[i], &path_stat);
if (S_ISDIR(path_stat.st_mode)) {
if (strncmp(normalized_path, permitted_mounts[i], permitted_mount_len) == 0) {
ret = 1;
break;
}
}
}
free(normalized_path);
return ret;
}
/**
* Function to validate whether a requested mount path is permitted or not. The normalized mount path
* must be in the correct permitted list based on the type of mount (ro or rw) and must not be a
* parent of the container-executor config file.
* @param permitted_ro_mounts - Array of permitted read-only mounts.
* @param permitted_rw_mounts - Array of permitted read-write mounts.
* @param requested - Mount path to be validated
* @return 0 on valid mount, INVALID_MOUNT, INVALID_RW_MOUNT, INVALID_RO_MOUNT,
* or MOUNT_ACCESS_ERROR on error.
*/
static int validate_mount(const char **permitted_ro_mounts, const char **permitted_rw_mounts, const mount *requested) {
const char *container_executor_cfg_path = NULL;
const char *tmp_path_buffer[2] = {NULL, NULL};
int permitted_rw, permitted_ro;
int ret = 0;
if (requested == NULL) {
goto free_and_exit;
}
ret = get_normalized_config_path(&container_executor_cfg_path);
if (ret != 0) {
goto free_and_exit;
}
permitted_rw = check_mount_permitted(permitted_rw_mounts, requested->src);
permitted_ro = check_mount_permitted(permitted_ro_mounts, requested->src);
if (permitted_ro == -1 || permitted_rw == -1) {
fprintf(ERRORFILE, "Invalid mount src='%s', dest='%s'\n",
requested->src, requested->dest);
ret = INVALID_MOUNT;
goto free_and_exit;
}
if (requested->options != NULL && requested->options->rw == 1) {
// rw mount
if (permitted_rw == 0) {
fprintf(ERRORFILE, "Configuration does not allow mount src='%s', dest='%s'\n",
requested->src, requested->dest);
ret = INVALID_RW_MOUNT;
goto free_and_exit;
} else {
// determine if the user can modify the container-executor.cfg file
tmp_path_buffer[0] = normalize_mount(requested->src, 0);
// just re-use the function, flip the args to check if the container-executor path is in the requested
// mount point
ret = check_mount_permitted(tmp_path_buffer, container_executor_cfg_path);
free((void *) tmp_path_buffer[0]);
if (ret == 1) {
fprintf(ERRORFILE, "Attempting to mount a parent directory of container-executor.cfg as read-write. src='%s', dest='%s'\n",
requested->src, requested->dest);
ret = INVALID_RW_MOUNT;
goto free_and_exit;
}
}
} else {
// ro mount
if (permitted_ro == 0 && permitted_rw == 0) {
fprintf(ERRORFILE, "Configuration does not allow mount src='%s', dest='%s'\n",
requested->src, requested->dest);
ret = INVALID_RO_MOUNT;
goto free_and_exit;
}
}
free_and_exit:
free((void *) container_executor_cfg_path);
return ret;
}
/**
* Function to validate an array of mounts.
* @param permitted_ro_mounts - Array of permitted read-only mounts.
* @param permitted_rw_mounts - Array of permitted read-write mounts.
* @param mounts - Array of mounts to be validated.
* @param num_mounts - Number of mounts to be valildated.
* @return 0 on valid mounts, INVALID_MOUNT, INVALID_RW_MOUNT, INVALID_RO_MOUNT,
* or MOUNT_ACCESS_ERROR on error.
*/
int validate_mounts(char **permitted_ro_mounts, char **permitted_rw_mounts, mount *mounts, const unsigned int num_mounts) {
int ret = 0;
unsigned int i;
ret = normalize_mounts(permitted_ro_mounts, 1);
ret |= normalize_mounts(permitted_rw_mounts, 1);
if (ret != 0) {
fprintf(ERRORFILE, "Unable to find permitted mounts on disk\n");
ret = MOUNT_ACCESS_ERROR;
goto free_and_exit;
}
for (i = 0; i < num_mounts; i++) {
ret = validate_mount((const char **) permitted_ro_mounts, (const char **) permitted_rw_mounts, &mounts[i]);
if (ret != 0) {
goto free_and_exit;
}
}
free_and_exit:
return ret;
}

View File

@ -0,0 +1,40 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef __YARN_POSIX_CONTAINER_EXECUTOR_MOUNT_UTIL_H__
#define __YARN_POSIX_CONTAINER_EXECUTOR_MOUNT_UTIL_H__
typedef struct mount_options_struct {
char **opts;
unsigned int num_opts;
unsigned int rw; // 0 for read, 1 for write
} mount_options;
typedef struct mount_struct {
char *src;
char *dest;
mount_options *options;
} mount;
void free_mount_options(mount_options *options);
void free_mounts(mount *mounts, const unsigned int num_mounts);
int validate_mounts(char **permitted_ro_mounts, char **permitted_rw_mounts, mount *mounts, unsigned int num_mounts);
#endif

View File

@ -22,6 +22,7 @@
extern "C" {
#include "utils/docker-util.c"
#include "utils/mount-utils.c"
}
namespace ContainerExecutor {
@ -121,7 +122,7 @@ namespace ContainerExecutor {
for (itr = file_cmd_vec.begin(); itr != file_cmd_vec.end(); ++itr) {
write_command_file(itr->first);
int ret = (*docker_func)(docker_command_file.c_str(), &container_executor_cfg, &tmp);
ASSERT_EQ(0, ret) << "error message: " << get_docker_error_message(ret) << " for input " << itr->first;
ASSERT_EQ(0, ret) << "error message: " << get_error_message(ret) << " for input " << itr->first;
char *actual = flatten(&tmp);
ASSERT_STREQ(itr->second.c_str(), actual);
reset_args(&tmp);
@ -136,7 +137,7 @@ namespace ContainerExecutor {
reset_args(&tmp);
}
int ret = (*docker_func)("unknown-file", &container_executor_cfg, &tmp);
ASSERT_EQ(static_cast<int>(INVALID_COMMAND_FILE), ret);
ASSERT_EQ(static_cast<int>(INVALID_DOCKER_COMMAND_FILE), ret);
reset_args(&tmp);
}
@ -181,10 +182,10 @@ namespace ContainerExecutor {
std::vector<std::pair<std::string, int> > bad_file_cmd_vec;
bad_file_cmd_vec.push_back(std::make_pair<std::string, int>(
"[docker-command-execution]\n docker-command=run\n format='{{.State.Status}}'",
static_cast<int>(INCORRECT_COMMAND)));
static_cast<int>(INCORRECT_DOCKER_COMMAND)));
bad_file_cmd_vec.push_back(std::make_pair<std::string, int>(
"docker-command=inspect\n format='{{.State.Status}}'",
static_cast<int>(INCORRECT_COMMAND)));
static_cast<int>(INCORRECT_DOCKER_COMMAND)));
bad_file_cmd_vec.push_back(std::make_pair<std::string, int>(
"[docker-command-execution]\n docker-command=inspect\n format={{.State.Status}}\n name=",
static_cast<int>(INVALID_DOCKER_CONTAINER_NAME)));
@ -218,9 +219,9 @@ namespace ContainerExecutor {
std::vector<std::pair<std::string, int> > bad_file_cmd_vec;
bad_file_cmd_vec.push_back(std::make_pair<std::string, int>(
"[docker-command-execution]\n docker-command=run\n image=image-id", static_cast<int>(INCORRECT_COMMAND)));
"[docker-command-execution]\n docker-command=run\n image=image-id", static_cast<int>(INCORRECT_DOCKER_COMMAND)));
bad_file_cmd_vec.push_back(std::make_pair<std::string, int>(
"docker-command=load\n image=image-id", static_cast<int>(INCORRECT_COMMAND)));
"docker-command=load\n image=image-id", static_cast<int>(INCORRECT_DOCKER_COMMAND)));
bad_file_cmd_vec.push_back(std::make_pair<std::string, int>(
"[docker-command-execution]\n docker-command=load\n image=", static_cast<int>(INVALID_DOCKER_IMAGE_NAME)));
bad_file_cmd_vec.push_back(std::make_pair<std::string, int>("[docker-command-execution]\n docker-command=load",
@ -288,9 +289,9 @@ namespace ContainerExecutor {
std::vector<std::pair<std::string, int> > bad_file_cmd_vec;
bad_file_cmd_vec.push_back(std::make_pair<std::string, int>(
"[docker-command-execution]\n docker-command=run\n image=image-id", static_cast<int>(INCORRECT_COMMAND)));
"[docker-command-execution]\n docker-command=run\n image=image-id", static_cast<int>(INCORRECT_DOCKER_COMMAND)));
bad_file_cmd_vec.push_back(std::make_pair<std::string, int>(
"docker-command=pull\n image=image-id", static_cast<int>(INCORRECT_COMMAND)));
"docker-command=pull\n image=image-id", static_cast<int>(INCORRECT_DOCKER_COMMAND)));
bad_file_cmd_vec.push_back(std::make_pair<std::string, int>(
"[docker-command-execution]\n docker-command=pull\n image=", static_cast<int>(INVALID_DOCKER_IMAGE_NAME)));
bad_file_cmd_vec.push_back(std::make_pair<std::string, int>("[docker-command-execution]\n docker-command=pull",
@ -309,9 +310,9 @@ namespace ContainerExecutor {
std::vector<std::pair<std::string, int> > bad_file_cmd_vec;
bad_file_cmd_vec.push_back(std::make_pair<std::string, int>(
"[docker-command-execution]\n docker-command=run\n name=container_e1_12312_11111_02_000001",
static_cast<int>(INCORRECT_COMMAND)));
static_cast<int>(INCORRECT_DOCKER_COMMAND)));
bad_file_cmd_vec.push_back(std::make_pair<std::string, int>(
"docker-command=rm\n name=ctr-id", static_cast<int>(INCORRECT_COMMAND)));
"docker-command=rm\n name=ctr-id", static_cast<int>(INCORRECT_DOCKER_COMMAND)));
bad_file_cmd_vec.push_back(std::make_pair<std::string, int>(
"[docker-command-execution]\n docker-command=rm\n name=", static_cast<int>(INVALID_DOCKER_CONTAINER_NAME)));
bad_file_cmd_vec.push_back(std::make_pair<std::string, int>(
@ -332,9 +333,9 @@ namespace ContainerExecutor {
std::vector<std::pair<std::string, int> > bad_file_cmd_vec;
bad_file_cmd_vec.push_back(std::make_pair<std::string, int>(
"[docker-command-execution]\n docker-command=run\n name=container_e1_12312_11111_02_000001",
static_cast<int>(INCORRECT_COMMAND)));
static_cast<int>(INCORRECT_DOCKER_COMMAND)));
bad_file_cmd_vec.push_back(std::make_pair<std::string, int>(
"docker-command=stop\n name=ctr-id", static_cast<int>(INCORRECT_COMMAND)));
"docker-command=stop\n name=ctr-id", static_cast<int>(INCORRECT_DOCKER_COMMAND)));
bad_file_cmd_vec.push_back(std::make_pair<std::string, int>(
"[docker-command-execution]\n docker-command=stop\n name=", static_cast<int>(INVALID_DOCKER_CONTAINER_NAME)));
bad_file_cmd_vec.push_back(std::make_pair<std::string, int>(
@ -359,9 +360,9 @@ namespace ContainerExecutor {
std::vector<std::pair<std::string, int> > bad_file_cmd_vec;
bad_file_cmd_vec.push_back(std::make_pair<std::string, int>(
"[docker-command-execution]\n docker-command=run\n name=container_e1_12312_11111_02_000001",
static_cast<int>(INCORRECT_COMMAND)));
static_cast<int>(INCORRECT_DOCKER_COMMAND)));
bad_file_cmd_vec.push_back(std::make_pair<std::string, int>(
"docker-command=kill\n name=ctr-id", static_cast<int>(INCORRECT_COMMAND)));
"docker-command=kill\n name=ctr-id", static_cast<int>(INCORRECT_DOCKER_COMMAND)));
bad_file_cmd_vec.push_back(std::make_pair<std::string, int>(
"[docker-command-execution]\n docker-command=kill\n name=", static_cast<int>(INVALID_DOCKER_CONTAINER_NAME)));
bad_file_cmd_vec.push_back(std::make_pair<std::string, int>(
@ -382,9 +383,9 @@ namespace ContainerExecutor {
std::vector<std::pair<std::string, int> > bad_file_cmd_vec;
bad_file_cmd_vec.push_back(std::make_pair<std::string, int>(
"[docker-command-execution]\n docker-command=run\n name=container_e1_12312_11111_02_000001",
static_cast<int>(INCORRECT_COMMAND)));
static_cast<int>(INCORRECT_DOCKER_COMMAND)));
bad_file_cmd_vec.push_back(std::make_pair<std::string, int>(
"docker-command=start\n name=ctr-id", static_cast<int>(INCORRECT_COMMAND)));
"docker-command=start\n name=ctr-id", static_cast<int>(INCORRECT_DOCKER_COMMAND)));
bad_file_cmd_vec.push_back(std::make_pair<std::string, int>(
"[docker-command-execution]\n docker-command=start\n name=", static_cast<int>(INVALID_DOCKER_CONTAINER_NAME)));
bad_file_cmd_vec.push_back(std::make_pair<std::string, int>(
@ -474,7 +475,7 @@ namespace ContainerExecutor {
}
ret = set_runtime(&cmd_cfg, &container_cfg, &buff);
char *actual = flatten(&buff);
ASSERT_EQ(0, ret) << "error message: " << get_docker_error_message(ret) << " for input " << itr->first;
ASSERT_EQ(0, ret) << "error message: " << get_error_message(ret) << " for input " << itr->first;
ASSERT_STREQ(itr->second.c_str(), actual);
reset_args(&buff);
free(actual);
@ -599,7 +600,7 @@ namespace ContainerExecutor {
}
ret = add_ports_mapping_to_command(&cmd_cfg, &buff);
char *actual = flatten(&buff);
ASSERT_EQ(0, ret) << "error message: " << get_docker_error_message(ret) << " for input " << itr->first;
ASSERT_EQ(0, ret) << "error message: " << get_error_message(ret) << " for input " << itr->first;
ASSERT_STREQ(itr->second.c_str(), actual);
reset_args(&buff);
free(actual);
@ -655,7 +656,7 @@ namespace ContainerExecutor {
"[docker-command-execution]\n docker-command=run", ""));
bad_file_cmd_vec.push_back(std::make_pair<std::string, int>(
"[docker-command-execution]\n docker-command=run\n pid=other",
static_cast<int>(INVALID_PID_NAMESPACE)));
static_cast<int>(INVALID_DOCKER_PID_NAMESPACE)));
for (int i = 1; i < 3; ++i) {
write_container_executor_cfg(container_executor_cfg_contents[0]);
@ -719,10 +720,10 @@ namespace ContainerExecutor {
bad_file_cmd_vec.clear();
bad_file_cmd_vec.push_back(std::make_pair<std::string, int>(
"[docker-command-execution]\n docker-command=run\n pid=other",
static_cast<int>(INVALID_PID_NAMESPACE)));
static_cast<int>(INVALID_DOCKER_PID_NAMESPACE)));
bad_file_cmd_vec.push_back(std::make_pair<std::string, int>(
"[docker-command-execution]\n docker-command=run\n pid=host",
static_cast<int>(PID_HOST_DISABLED)));
static_cast<int>(DOCKER_PID_HOST_DISABLED)));
for (itr2 = bad_file_cmd_vec.begin(); itr2 != bad_file_cmd_vec.end(); ++itr2) {
write_command_file(itr2->first);
ret = read_config(docker_command_file.c_str(), &cmd_cfg);
@ -816,7 +817,7 @@ namespace ContainerExecutor {
FAIL();
}
ret = set_privileged(&cmd_cfg, &container_cfg, &buff);
ASSERT_EQ(6, ret);
ASSERT_EQ(INVALID_DOCKER_USER_NAME, ret);
ASSERT_EQ(0, buff.length);
reset_args(&buff);
free_configuration(&cmd_cfg);
@ -827,7 +828,7 @@ namespace ContainerExecutor {
FAIL();
}
ret = set_privileged(&cmd_cfg, &container_cfg, &buff);
ASSERT_EQ(PRIVILEGED_CONTAINERS_DISABLED, ret);
ASSERT_EQ(PRIVILEGED_DOCKER_CONTAINERS_DISABLED, ret);
ASSERT_EQ(0, buff.length);
reset_args(&buff);
free_configuration(&cmd_cfg);
@ -864,7 +865,7 @@ namespace ContainerExecutor {
FAIL();
}
ret = set_privileged(&cmd_cfg, &container_cfg, &buff);
ASSERT_EQ(PRIVILEGED_CONTAINERS_DISABLED, ret);
ASSERT_EQ(PRIVILEGED_DOCKER_CONTAINERS_DISABLED, ret);
ASSERT_EQ(0, buff.length);
reset_args(&buff);
free_configuration(&cmd_cfg);
@ -1069,7 +1070,7 @@ namespace ContainerExecutor {
}
TEST_F(TestDockerUtil, test_add_mounts) {
TEST_F(TestDockerUtil, test_add_docker_mounts) {
struct configuration container_cfg, cmd_cfg;
struct args buff = ARGS_INITIAL_VALUE;
int ret = 0;
@ -1125,7 +1126,7 @@ namespace ContainerExecutor {
if (ret != 0) {
FAIL();
}
ret = add_mounts(&cmd_cfg, &container_cfg, &buff);
ret = add_docker_mounts(&cmd_cfg, &container_cfg, &buff);
char *actual = flatten(&buff);
ASSERT_EQ(0, ret);
ASSERT_STREQ(itr->second.c_str(), actual);
@ -1137,22 +1138,22 @@ namespace ContainerExecutor {
std::vector<std::pair<std::string, int> > bad_file_cmds_vec;
bad_file_cmds_vec.push_back(std::make_pair<std::string, int>(
"[docker-command-execution]\n docker-command=run\n image=hadoop/image\n mounts=/lib:/lib:rw",
static_cast<int>(INVALID_DOCKER_RW_MOUNT)));
static_cast<int>(INVALID_RW_MOUNT)));
bad_file_cmds_vec.push_back(std::make_pair<std::string, int>(
"[docker-command-execution]\n docker-command=run\n image=hadoop/image\n mounts=/usr/bin/:/usr/bin:rw",
static_cast<int>(INVALID_DOCKER_RW_MOUNT)));
static_cast<int>(INVALID_RW_MOUNT)));
bad_file_cmds_vec.push_back(std::make_pair<std::string, int>(
"[docker-command-execution]\n docker-command=run\n image=hadoop/image\n mounts=/blah:/blah:rw",
static_cast<int>(INVALID_DOCKER_MOUNT)));
static_cast<int>(INVALID_MOUNT)));
bad_file_cmds_vec.push_back(std::make_pair<std::string, int>(
"[docker-command-execution]\n docker-command=run\n image=hadoop/image\n mounts=/tmp:/tmp:shared",
static_cast<int>(INVALID_DOCKER_MOUNT)));
static_cast<int>(INVALID_MOUNT)));
bad_file_cmds_vec.push_back(std::make_pair<std::string, int>(
"[docker-command-execution]\n docker-command=run\n image=hadoop/image\n mounts=/lib:/lib",
static_cast<int>(INVALID_DOCKER_MOUNT)));
static_cast<int>(INVALID_MOUNT)));
bad_file_cmds_vec.push_back(std::make_pair<std::string, int>(
"[docker-command-execution]\n docker-command=run\n image=hadoop/image\n mounts=/lib:/lib:other",
static_cast<int>(INVALID_DOCKER_MOUNT)));
static_cast<int>(INVALID_MOUNT)));
std::vector<std::pair<std::string, int> >::const_iterator itr2;
@ -1162,7 +1163,7 @@ namespace ContainerExecutor {
if (ret != 0) {
FAIL();
}
ret = add_mounts(&cmd_cfg, &container_cfg, &buff);
ret = add_docker_mounts(&cmd_cfg, &container_cfg, &buff);
char *actual = flatten(&buff);
ASSERT_EQ(itr2->second, ret);
ASSERT_STREQ("", actual);
@ -1181,8 +1182,8 @@ namespace ContainerExecutor {
if (ret != 0) {
FAIL();
}
ret = add_mounts(&cmd_cfg, &container_cfg, &buff);
ASSERT_EQ(INVALID_DOCKER_RW_MOUNT, ret) << " for input " << cmd_file_contents;
ret = add_docker_mounts(&cmd_cfg, &container_cfg, &buff);
ASSERT_EQ(INVALID_RW_MOUNT, ret) << " for input " << cmd_file_contents;
char *actual = flatten(&buff);
ASSERT_STREQ("", actual);
reset_args(&buff);
@ -1196,7 +1197,7 @@ namespace ContainerExecutor {
free(ce_path);
free_configuration(&container_cfg);
// For untrusted image, container add_mounts will pass through
// For untrusted image, container add_docker_mounts will pass through
// without mounting or report error code.
container_executor_cfg_contents = "[docker]\n";
write_container_executor_cfg(container_executor_cfg_contents);
@ -1204,7 +1205,7 @@ namespace ContainerExecutor {
if (ret != 0) {
FAIL();
}
ret = add_mounts(&cmd_cfg, &container_cfg, &buff);
ret = add_docker_mounts(&cmd_cfg, &container_cfg, &buff);
char *actual = flatten(&buff);
ASSERT_EQ(0, ret);
ASSERT_STREQ("", actual);
@ -1266,7 +1267,7 @@ namespace ContainerExecutor {
if (ret != 0) {
FAIL();
}
ret = add_mounts(&cmd_cfg, &container_cfg, &buff);
ret = add_docker_mounts(&cmd_cfg, &container_cfg, &buff);
char *actual = flatten(&buff);
ASSERT_EQ(0, ret);
ASSERT_STREQ(itr->second.c_str(), actual);
@ -1278,10 +1279,10 @@ namespace ContainerExecutor {
std::vector<std::pair<std::string, int> > bad_file_cmds_vec;
bad_file_cmds_vec.push_back(std::make_pair<std::string, int>(
"[docker-command-execution]\n docker-command=run\n image=hadoop/image\n mounts=/etc:/etc:ro",
static_cast<int>(INVALID_DOCKER_RO_MOUNT)));
static_cast<int>(INVALID_RO_MOUNT)));
bad_file_cmds_vec.push_back(std::make_pair<std::string, int>(
"[docker-command-execution]\n docker-command=run\n image=hadoop/image\n mounts=/blah:/blah:ro",
static_cast<int>(INVALID_DOCKER_MOUNT)));
static_cast<int>(INVALID_MOUNT)));
std::vector<std::pair<std::string, int> >::const_iterator itr2;
@ -1291,7 +1292,7 @@ namespace ContainerExecutor {
if (ret != 0) {
FAIL();
}
ret = add_mounts(&cmd_cfg, &container_cfg, &buff);
ret = add_docker_mounts(&cmd_cfg, &container_cfg, &buff);
char *actual = flatten(&buff);
ASSERT_EQ(itr2->second, ret);
ASSERT_STREQ("", actual);
@ -1312,8 +1313,8 @@ namespace ContainerExecutor {
if (ret != 0) {
FAIL();
}
ret = add_mounts(&cmd_cfg, &container_cfg, &buff);
ASSERT_EQ(INVALID_DOCKER_RO_MOUNT, ret);
ret = add_docker_mounts(&cmd_cfg, &container_cfg, &buff);
ASSERT_EQ(INVALID_RO_MOUNT, ret);
ASSERT_EQ(0, buff.length);
reset_args(&buff);
free_configuration(&cmd_cfg);
@ -1508,7 +1509,7 @@ namespace ContainerExecutor {
" network=bridge\n privileged=true\n"
" cap-add=CHOWN,SETUID\n cgroup-parent=ctr-cgroup\n detach=true\n rm=true\n group-add=1000,1001\n"
" launch-command=bash,test_script.sh,arg1,arg2",
PRIVILEGED_CONTAINERS_DISABLED));
PRIVILEGED_DOCKER_CONTAINERS_DISABLED));
// invalid rw mount
bad_file_cmd_vec.push_back(std::make_pair<std::string, int>(
@ -1518,7 +1519,7 @@ namespace ContainerExecutor {
" network=bridge\n devices=/dev/test:/dev/test\n"
" cap-add=CHOWN,SETUID\n cgroup-parent=ctr-cgroup\n detach=true\n rm=true\n"
" launch-command=bash,test_script.sh,arg1,arg2",
static_cast<int>(INVALID_DOCKER_RW_MOUNT)));
static_cast<int>(INVALID_RW_MOUNT)));
// invalid ro mount
bad_file_cmd_vec.push_back(std::make_pair<std::string, int>(
@ -1528,7 +1529,7 @@ namespace ContainerExecutor {
" network=bridge\n devices=/dev/test:/dev/test\n"
" cap-add=CHOWN,SETUID\n cgroup-parent=ctr-cgroup\n detach=true\n rm=true\n"
" launch-command=bash,test_script.sh,arg1,arg2",
static_cast<int>(INVALID_DOCKER_RO_MOUNT)));
static_cast<int>(INVALID_RO_MOUNT)));
// invalid capability
bad_file_cmd_vec.push_back(std::make_pair<std::string, int>(
@ -1548,7 +1549,7 @@ namespace ContainerExecutor {
" network=bridge\n devices=/dev/dev1:/dev/dev1\n privileged=true\n"
" cap-add=CHOWN,SETUID\n cgroup-parent=ctr-cgroup\n detach=true\n rm=true\n"
" launch-command=bash,test_script.sh,arg1,arg2",
static_cast<int>(PRIVILEGED_CONTAINERS_DISABLED)));
static_cast<int>(PRIVILEGED_DOCKER_CONTAINERS_DISABLED)));
// invalid network
bad_file_cmd_vec.push_back(std::make_pair<std::string, int>(
@ -1690,7 +1691,7 @@ namespace ContainerExecutor {
" network=bridge\n devices=/dev/test:/dev/test\n privileged=true\n"
" cap-add=CHOWN,SETUID\n cgroup-parent=ctr-cgroup\n detach=true\n rm=true\n"
" launch-command=bash,test_script.sh,arg1,arg2",
static_cast<int>(PRIVILEGED_CONTAINERS_DISABLED)));
static_cast<int>(PRIVILEGED_DOCKER_CONTAINERS_DISABLED)));
run_docker_command_test(file_cmd_vec, bad_file_cmd_vec, get_docker_run_command);
free_configuration(&container_executor_cfg);
@ -1978,10 +1979,10 @@ namespace ContainerExecutor {
std::vector<std::pair<std::string, int> > bad_file_cmd_vec;
bad_file_cmd_vec.push_back(std::make_pair<std::string, int>(
"[docker-command-execution]\n docker-command=run\n image=image-id",
static_cast<int>(INCORRECT_COMMAND)));
static_cast<int>(INCORRECT_DOCKER_COMMAND)));
bad_file_cmd_vec.push_back(std::make_pair<std::string, int>(
"docker-command=images\n image=image-id",
static_cast<int>(INCORRECT_COMMAND)));
static_cast<int>(INCORRECT_DOCKER_COMMAND)));
run_docker_command_test(file_cmd_vec, bad_file_cmd_vec,
get_docker_images_command);