|
@@ -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;
|
|
|
}
|