From c5ea37649d728630df34e1af22908b8e8124f772 Mon Sep 17 00:00:00 2001 From: wujing Date: Mon, 13 Apr 2020 09:11:21 -0400 Subject: [PATCH 15/49] add masked paths and readonly paths Signed-off-by: wujing --- src/lxc/conf.c | 139 +++++++++++++++++++++++++++++++++++++++++++++++++++++- src/lxc/conf.h | 12 +++++ src/lxc/confile.c | 106 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 256 insertions(+), 1 deletion(-) diff --git a/src/lxc/conf.c b/src/lxc/conf.c index a904348..fce241b 100644 --- a/src/lxc/conf.c +++ b/src/lxc/conf.c @@ -1275,6 +1275,96 @@ static int lxc_mount_rootfs(struct lxc_conf *conf) return 0; } +#ifdef HAVE_ISULAD +// maskPath masks the top of the specified path inside a container to avoid +// security issues from processes reading information from non-namespace aware +// mounts ( proc/kcore ). +static bool mask_path(const char *path) +{ + int ret; + + if (!path) + return true; + + ret = mount("/dev/null", path, "", MS_BIND, ""); + if (ret < 0 && errno != ENOENT) { + if (errno == ENOTDIR) { + ret = mount("tmpfs", path, "tmpfs", MS_RDONLY, ""); + if (ret < 0) + goto error; + return true; + } + goto error; + } + return true; + +error: + SYSERROR("Failed to mask path \"%s\": %s", path, strerror(errno)); + return false; +} + +// remount_readonly will bind over the top of an existing path and ensure that it is read-only. +static bool remount_readonly(const char *path) +{ + int ret, i; + + if (!path) + return true; + + for (i = 0; i < 5; i++) { + ret = mount("", path, "", MS_REMOUNT | MS_RDONLY, ""); + if (ret < 0 && errno != ENOENT) { + if (errno == EINVAL) { + // Probably not a mountpoint, use bind-mount + ret = mount(path, path, "", MS_BIND, ""); + if (ret < 0) + goto on_error; + ret = mount(path, path, "", MS_BIND | MS_REMOUNT | MS_RDONLY | MS_REC | \ + MS_NOEXEC | MS_NOSUID | MS_NODEV, ""); + if (ret < 0) + goto on_error; + } else if (errno == EBUSY) { + DEBUG("Try to mount \"%s\" to readonly after 100ms.", path); + usleep(100 * 1000); + continue; + } else { + goto on_error; + } + } + return true; + } + +on_error: + SYSERROR("Unable to mount \"%s\" to readonly", path); + return false; +} + +// isulad: setup rootfs masked paths +static int setup_rootfs_maskedpaths(struct lxc_list *maskedpaths) +{ + struct lxc_list *it; + + lxc_list_for_each(it, maskedpaths) { + if (!mask_path((char *)it->elem)) + return -1; + } + + return 0; +} +// isulad: setup rootfs ro paths +static int setup_rootfs_ropaths(struct lxc_list *ropaths) +{ + struct lxc_list *it; + + lxc_list_for_each(it, ropaths) { + if (!remount_readonly((char *)it->elem)) + return -1; + } + + return 0; +} +#endif + int lxc_chroot(const struct lxc_rootfs *rootfs) { __do_free char *nroot = NULL; @@ -2666,8 +2756,9 @@ struct lxc_conf *lxc_conf_init(void) seccomp_conf_init(new); #ifdef HAVE_ISULAD - /* isulad add begin */ lxc_list_init(&new->populate_devs); + lxc_list_init(&new->rootfs.maskedpaths); + lxc_list_init(&new->rootfs.ropaths); new->exit_fd = -1; new->umask = 0027; /*default umask 0027*/ new->console.init_fifo[0] = NULL; @@ -3690,6 +3781,22 @@ int lxc_setup(struct lxc_handler *handler) return log_error(-1, "Failed to setup sysctl parameters"); } +#ifdef HAVE_ISULAD + // isulad: setup rootfs masked paths + if (!lxc_list_empty(&lxc_conf->rootfs.maskedpaths)) { + if (setup_rootfs_maskedpaths(&lxc_conf->rootfs.maskedpaths)) { + return log_error(-1, "failed to setup maskedpaths"); + } + } + + // isulad: setup rootfs ro paths + if (!lxc_list_empty(&lxc_conf->rootfs.ropaths)) { + if (setup_rootfs_ropaths(&lxc_conf->rootfs.ropaths)) { + return log_error(-1, "failed to setup readonlypaths"); + } + } +#endif + if (!lxc_list_empty(&lxc_conf->keepcaps)) { if (!lxc_list_empty(&lxc_conf->caps)) return log_error(-1, "Container requests lxc.cap.drop and lxc.cap.keep: either use lxc.cap.drop or lxc.cap.keep, not both"); @@ -4103,6 +4210,8 @@ void lxc_conf_free(struct lxc_conf *conf) } lxc_clear_init_args(conf); lxc_clear_populate_devices(conf); + lxc_clear_rootfs_masked_paths(conf); + lxc_clear_rootfs_ro_paths(conf); #endif free(conf); } @@ -4945,4 +5054,32 @@ int lxc_clear_populate_devices(struct lxc_conf *c) return 0; } +/*isulad: clear rootfs masked paths*/ +int lxc_clear_rootfs_masked_paths(struct lxc_conf *c) +{ + struct lxc_list *it = NULL; + struct lxc_list *next = NULL; + + lxc_list_for_each_safe(it, &c->rootfs.maskedpaths, next) { + lxc_list_del(it); + free(it->elem); + free(it); + } + return 0; +} + +/*isulad: clear rootfs ro paths*/ +int lxc_clear_rootfs_ro_paths(struct lxc_conf *c) +{ + struct lxc_list *it = NULL; + struct lxc_list *next = NULL; + + lxc_list_for_each_safe(it, &c->rootfs.ropaths, next) { + lxc_list_del(it); + free(it->elem); + free(it); + } + return 0; +} + #endif diff --git a/src/lxc/conf.h b/src/lxc/conf.h index 52460a3..482fe0d 100644 --- a/src/lxc/conf.h +++ b/src/lxc/conf.h @@ -143,6 +143,8 @@ struct lxc_tty_info { * @mountflags : the portion of @options that are flags * @data : the portion of @options that are not flags * @managed : whether it is managed by LXC + * @maskedpaths: A list of paths to be msked over inside the container + * @ropaths : A list of paths to be remounted with readonly inside the container */ struct lxc_rootfs { char *path; @@ -152,6 +154,14 @@ struct lxc_rootfs { unsigned long mountflags; char *data; bool managed; + +#ifdef HAVE_ISULAD + /* isulad: maskedpaths */ + struct lxc_list maskedpaths; + /* isulad: ropaths */ + struct lxc_list ropaths; +#endif + }; /* @@ -511,5 +521,7 @@ extern int userns_exec_minimal(const struct lxc_conf *conf, #ifdef HAVE_ISULAD int lxc_clear_init_args(struct lxc_conf *lxc_conf); int lxc_clear_populate_devices(struct lxc_conf *c); +int lxc_clear_rootfs_masked_paths(struct lxc_conf *c); +int lxc_clear_rootfs_ro_paths(struct lxc_conf *c); #endif #endif /* __LXC_CONF_H */ diff --git a/src/lxc/confile.c b/src/lxc/confile.c index 2df269a..bf0fdf0 100644 --- a/src/lxc/confile.c +++ b/src/lxc/confile.c @@ -151,6 +151,8 @@ lxc_config_define(proc); lxc_config_define(init_args); lxc_config_define(populate_device); lxc_config_define(umask); +lxc_config_define(rootfs_masked_paths); +lxc_config_define(rootfs_ro_paths); #endif /* @@ -268,6 +270,8 @@ static struct lxc_config_t config_jump_table[] = { { "lxc.isulad.init.args", set_config_init_args, get_config_init_args, clr_config_init_args, }, { "lxc.isulad.populate.device", set_config_populate_device, get_config_populate_device, clr_config_populate_device, }, { "lxc.isulad.umask", set_config_umask, get_config_umask, clr_config_umask, }, + { "lxc.isulad.rootfs.maskedpaths", set_config_rootfs_masked_paths, get_config_rootfs_masked_paths, clr_config_rootfs_masked_paths, }, + { "lxc.isulad.rootfs.ropaths", set_config_rootfs_ro_paths, get_config_rootfs_ro_paths, clr_config_rootfs_ro_paths, }, #endif }; @@ -6311,4 +6315,106 @@ static inline int clr_config_umask(const char *key, struct lxc_conf *c, return 0; } +/* isulad: set config for rootfs masked paths */ +static int set_config_rootfs_masked_paths(const char *key, const char *value, + struct lxc_conf *lxc_conf, void *data) +{ + struct lxc_list *list_item = NULL; + + if (lxc_config_value_empty(value)) + return lxc_clear_rootfs_masked_paths(lxc_conf); + + list_item = malloc(sizeof(*list_item)); + if (list_item == NULL) + goto on_error; + + list_item->elem = safe_strdup(value); + + lxc_list_add_tail(&lxc_conf->rootfs.maskedpaths, list_item); + + return 0; + +on_error: + free(list_item); + + return -1; +} + +// isulad: get config rootfs masked paths +static int get_config_rootfs_masked_paths(const char *key, char *retv, int inlen, + struct lxc_conf *c, void *data) +{ + int len, fulllen = 0; + struct lxc_list *it = NULL; + + if (!retv) + inlen = 0; + else + memset(retv, 0, inlen); + + lxc_list_for_each(it, &c->rootfs.maskedpaths) { + strprint(retv, inlen, "%s\n", (char *)it->elem); + } + + return fulllen; +} + +/* isulad: set config for rootfs ro paths */ +static int set_config_rootfs_ro_paths(const char *key, const char *value, + struct lxc_conf *lxc_conf, void *data) +{ + struct lxc_list *list_item = NULL; + + if (lxc_config_value_empty(value)) + return lxc_clear_rootfs_ro_paths(lxc_conf); + + list_item = malloc(sizeof(*list_item)); + if (list_item == NULL) + goto on_error; + + list_item->elem = safe_strdup(value); + + lxc_list_add_tail(&lxc_conf->rootfs.ropaths, list_item); + + return 0; + +on_error: + free(list_item); + + return -1; +} + +// isulad: get config rootfs ro paths +static int get_config_rootfs_ro_paths(const char *key, char *retv, int inlen, + struct lxc_conf *c, void *data) +{ + int len, fulllen = 0; + struct lxc_list *it = NULL; + + if (!retv) + inlen = 0; + else + memset(retv, 0, inlen); + + lxc_list_for_each(it, &c->rootfs.ropaths) { + strprint(retv, inlen, "%s\n", (char *)it->elem); + } + + return fulllen; +} + +/* isulad: clr config rootfs masked paths */ +static inline int clr_config_rootfs_masked_paths(const char *key, struct lxc_conf *c, + void *data) +{ + return lxc_clear_rootfs_masked_paths(c); +} + +/* isulad: clr config rootfs ro paths */ +static inline int clr_config_rootfs_ro_paths(const char *key, struct lxc_conf *c, + void *data) +{ + return lxc_clear_rootfs_ro_paths(c); +} + #endif -- 1.8.3.1