From fbd37f04c5aebc06ab2a2066fd56787a4c71820a Mon Sep 17 00:00:00 2001 From: Filip Schauer Date: Mon, 8 Sep 2025 11:11:31 +0200 Subject: [PATCH 1/3] conf: split `lxc.environment` into `runtime` and `hooks` Introduce `lxc.environment.runtime` to set environment variables only for the container init process and `lxc.environment.hooks` to set environment variables only for hooks. Leave the original `lxc.environment` unchanged. It still applies to everything. Signed-off-by: Filip Schauer --- src/lxc/attach.c | 6 +++- src/lxc/conf.c | 14 +++++---- src/lxc/conf.h | 15 +++++++--- src/lxc/confile.c | 72 +++++++++++++++++++++++++++++++++++++++++------ src/lxc/start.c | 12 ++++++-- 5 files changed, 98 insertions(+), 21 deletions(-) diff --git a/src/lxc/attach.c b/src/lxc/attach.c index 8f2f7a37c3..f22f83f0df 100644 --- a/src/lxc/attach.c +++ b/src/lxc/attach.c @@ -879,7 +879,11 @@ static int lxc_attach_set_environment(struct attach_context *ctx, /* Set container environment variables.*/ if (ctx->container->lxc_conf) { - ret = lxc_set_environment(ctx->container->lxc_conf); + ret = lxc_set_environment(&ctx->container->lxc_conf->environment); + if (ret < 0) + return -1; + + ret = lxc_set_environment(&ctx->container->lxc_conf->environment_runtime); if (ret < 0) return -1; } diff --git a/src/lxc/conf.c b/src/lxc/conf.c index 1899b2806d..7533e28309 100644 --- a/src/lxc/conf.c +++ b/src/lxc/conf.c @@ -3209,6 +3209,8 @@ struct lxc_conf *lxc_conf_init(void) new->root_nsuid_map = NULL; new->root_nsgid_map = NULL; INIT_LIST_HEAD(&new->environment); + INIT_LIST_HEAD(&new->environment_runtime); + INIT_LIST_HEAD(&new->environment_hooks); INIT_LIST_HEAD(&new->limits); INIT_LIST_HEAD(&new->sysctls); INIT_LIST_HEAD(&new->procs); @@ -4239,18 +4241,18 @@ int lxc_clear_groups(struct lxc_conf *c) return 0; } -int lxc_clear_environment(struct lxc_conf *c) +int lxc_clear_environment(struct list_head *environment) { struct environment_entry *env, *nenv; - list_for_each_entry_safe(env, nenv, &c->environment, head) { + list_for_each_entry_safe(env, nenv, environment, head) { list_del(&env->head); free(env->key); free(env->val); free(env); } - INIT_LIST_HEAD(&c->environment); + INIT_LIST_HEAD(environment); return 0; } @@ -4359,7 +4361,7 @@ void lxc_conf_free(struct lxc_conf *conf) lxc_clear_mount_entries(conf); lxc_clear_idmaps(conf); lxc_clear_groups(conf); - lxc_clear_environment(conf); + lxc_clear_environment(&conf->environment); lxc_clear_limits(conf, "lxc.prlimit"); lxc_clear_sysctls(conf, "lxc.sysctl"); lxc_clear_procs(conf, "lxc.proc"); @@ -5210,11 +5212,11 @@ void suggest_default_idmap(void) ERROR("lxc.idmap = g 0 %u %u", gid, grange); } -int lxc_set_environment(const struct lxc_conf *conf) +int lxc_set_environment(const struct list_head *environment) { struct environment_entry *env; - list_for_each_entry(env, &conf->environment, head) { + list_for_each_entry(env, environment, head) { int ret; ret = setenv(env->key, env->val, 1); diff --git a/src/lxc/conf.h b/src/lxc/conf.h index 31cd39e3f7..762d58901d 100644 --- a/src/lxc/conf.h +++ b/src/lxc/conf.h @@ -506,10 +506,17 @@ struct lxc_conf { unsigned int monitor_unshare; unsigned int monitor_signal_pdeath; - /* list of environment variables we'll add to the container when - * started */ + /* list of environment variables to provide to both the container's init + * process and hooks */ struct list_head environment; + /* list of environment variables to provide to the container's init + * process */ + struct list_head environment_runtime; + + /* list of environment variables to provide to container hooks */ + struct list_head environment_hooks; + /* text representation of the config file */ char *unexpanded_config; size_t unexpanded_len; @@ -599,7 +606,7 @@ __hidden extern int lxc_clear_automounts(struct lxc_conf *c); __hidden extern int lxc_clear_hooks(struct lxc_conf *c, const char *key); __hidden extern int lxc_clear_idmaps(struct lxc_conf *c); __hidden extern int lxc_clear_groups(struct lxc_conf *c); -__hidden extern int lxc_clear_environment(struct lxc_conf *c); +__hidden extern int lxc_clear_environment(struct list_head *environment); __hidden extern int lxc_clear_limits(struct lxc_conf *c, const char *key); __hidden extern int lxc_delete_autodev(struct lxc_handler *handler); __hidden extern int lxc_clear_autodev_tmpfs_size(struct lxc_conf *c); @@ -710,7 +717,7 @@ static inline int lxc_personality(personality_t persona) return personality(persona); } -__hidden extern int lxc_set_environment(const struct lxc_conf *conf); +__hidden extern int lxc_set_environment(const struct list_head *environment); __hidden extern int parse_cap(const char *cap_name, __u32 *cap); #endif /* __LXC_CONF_H */ diff --git a/src/lxc/confile.c b/src/lxc/confile.c index 5045741bb0..7163cd1b22 100644 --- a/src/lxc/confile.c +++ b/src/lxc/confile.c @@ -80,6 +80,8 @@ lxc_config_define(console_rotate); lxc_config_define(console_size); lxc_config_define(unsupported_key); lxc_config_define(environment); +lxc_config_define(environment_runtime); +lxc_config_define(environment_hooks); lxc_config_define(ephemeral); lxc_config_define(execute_cmd); lxc_config_define(group); @@ -211,6 +213,8 @@ static struct lxc_config_t config_jump_table[] = { { "lxc.console.rotate", true, set_config_console_rotate, get_config_console_rotate, clr_config_console_rotate, }, { "lxc.console.size", true, set_config_console_size, get_config_console_size, clr_config_console_size, }, { "lxc.sched.core", true, set_config_sched_core, get_config_sched_core, clr_config_sched_core, }, + { "lxc.environment.runtime", true, set_config_environment_runtime, get_config_environment_runtime, clr_config_environment_runtime }, + { "lxc.environment.hooks", true, set_config_environment_hooks, get_config_environment_hooks, clr_config_environment_hooks }, { "lxc.environment", true, set_config_environment, get_config_environment, clr_config_environment, }, { "lxc.ephemeral", true, set_config_ephemeral, get_config_ephemeral, clr_config_ephemeral, }, { "lxc.execute.cmd", true, set_config_execute_cmd, get_config_execute_cmd, clr_config_execute_cmd, }, @@ -1574,15 +1578,15 @@ static int set_config_group(const char *key, const char *value, return 0; } -static int set_config_environment(const char *key, const char *value, - struct lxc_conf *lxc_conf, void *data) +static int set_config_environment_impl(const char *value, + struct list_head *environment) { __do_free char *dup = NULL, *val = NULL; __do_free struct environment_entry *new_env = NULL; char *env_val; if (lxc_config_value_empty(value)) - return lxc_clear_environment(lxc_conf); + return lxc_clear_environment(environment); new_env = zalloc(sizeof(struct environment_entry)); if (!new_env) @@ -1609,12 +1613,30 @@ static int set_config_environment(const char *key, const char *value, new_env->key = move_ptr(dup); new_env->val = move_ptr(val); - list_add_tail(&new_env->head, &lxc_conf->environment); + list_add_tail(&new_env->head, environment); move_ptr(new_env); return 0; } +static int set_config_environment(const char *key, const char *value, + struct lxc_conf *lxc_conf, void *data) +{ + return set_config_environment_impl(value, &lxc_conf->environment); +} + +static int set_config_environment_runtime(const char *key, const char* value, + struct lxc_conf *lxc_conf, void *data) +{ + return set_config_environment_impl(value, &lxc_conf->environment_runtime); +} + +static int set_config_environment_hooks(const char *key, const char* value, + struct lxc_conf *lxc_conf, void *data) +{ + return set_config_environment_impl(value, &lxc_conf->environment_hooks); +} + static int set_config_tty_max(const char *key, const char *value, struct lxc_conf *lxc_conf, void *data) { @@ -4473,8 +4495,8 @@ static int get_config_group(const char *key, char *retv, int inlen, return fulllen; } -static int get_config_environment(const char *key, char *retv, int inlen, - struct lxc_conf *c, void *data) +static int get_config_environment_impl(char *retv, int inlen, + struct list_head *environment) { int len, fulllen = 0; struct environment_entry *env; @@ -4484,13 +4506,32 @@ static int get_config_environment(const char *key, char *retv, int inlen, else memset(retv, 0, inlen); - list_for_each_entry(env, &c->environment, head) { + list_for_each_entry(env, environment, head) { strprint(retv, inlen, "%s=%s\n", env->key, env->val); } return fulllen; } +static int get_config_environment(const char *key, char *retv, int inlen, + struct lxc_conf *c, void *data) +{ + return get_config_environment_impl(retv, inlen, &c->environment); +} + +static int get_config_environment_runtime(const char *key, char *retv, + int inlen, struct lxc_conf *c, + void *data) +{ + return get_config_environment_impl(retv, inlen, &c->environment_runtime); +} + +static int get_config_environment_hooks(const char *key, char *retv, int inlen, + struct lxc_conf *c, void *data) +{ + return get_config_environment_impl(retv, inlen, &c->environment_hooks); +} + static int get_config_execute_cmd(const char *key, char *retv, int inlen, struct lxc_conf *c, void *data) { @@ -5211,7 +5252,19 @@ static inline int clr_config_group(const char *key, struct lxc_conf *c, static inline int clr_config_environment(const char *key, struct lxc_conf *c, void *data) { - return lxc_clear_environment(c); + return lxc_clear_environment(&c->environment); +} + +static inline int clr_config_environment_runtime(const char *key, + struct lxc_conf *c, void *data) +{ + return lxc_clear_environment(&c->environment_runtime); +} + +static inline int clr_config_environment_hooks(const char *key, + struct lxc_conf *c, void *data) +{ + return lxc_clear_environment(&c->environment_hooks); } static inline int clr_config_execute_cmd(const char *key, struct lxc_conf *c, @@ -6607,6 +6660,9 @@ int lxc_list_subkeys(struct lxc_conf *conf, const char *key, char *retv, } else if (strequal(key, "lxc.console")) { strprint(retv, inlen, "logfile\n"); strprint(retv, inlen, "path\n"); + } else if (strequal(key, "lxc.environment")) { + strprint(retv, inlen, "runtime\n"); + strprint(retv, inlen, "hooks\n"); } else if (strequal(key, "lxc.seccomp")) { strprint(retv, inlen, "profile\n"); } else if (strequal(key, "lxc.signal")) { diff --git a/src/lxc/start.c b/src/lxc/start.c index d566dd5ddb..b8559bbebf 100644 --- a/src/lxc/start.c +++ b/src/lxc/start.c @@ -1451,7 +1451,11 @@ static int do_start(void *data) * to allow them to be used by the various hooks, such as the start * hook below. */ - ret = lxc_set_environment(handler->conf); + ret = lxc_set_environment(&handler->conf->environment); + if (ret < 0) + goto out_warn_father; + + ret = lxc_set_environment(&handler->conf->environment_hooks); if (ret < 0) goto out_warn_father; @@ -1552,7 +1556,11 @@ static int do_start(void *data) if (ret < 0) SYSERROR("Failed to clear environment."); - ret = lxc_set_environment(handler->conf); + ret = lxc_set_environment(&handler->conf->environment); + if (ret < 0) + goto out_warn_father; + + ret = lxc_set_environment(&handler->conf->environment_runtime); if (ret < 0) goto out_warn_father; From 9ebe1f11ec66ec6df2b76abc089692e8e324b02b Mon Sep 17 00:00:00 2001 From: Filip Schauer Date: Tue, 16 Sep 2025 11:38:11 +0200 Subject: [PATCH 2/3] api_extensions: add environment_runtime_hooks extension Signed-off-by: Filip Schauer --- doc/api-extensions.md | 7 +++++++ src/lxc/api_extensions.h | 1 + 2 files changed, 8 insertions(+) diff --git a/doc/api-extensions.md b/doc/api-extensions.md index 0e9c724e82..4a0699289c 100644 --- a/doc/api-extensions.md +++ b/doc/api-extensions.md @@ -160,3 +160,10 @@ This adds the new options `cgroup2`, `cgroup2:ro`, `cgroup2:force`, `cgroup2:ro:force` for the `lxc.mount.auto` configuration key. For example, if a user specifies `cgroup2:force` LXC will pre-mount a pure `cgroup2` layout for the container even if the host is running with a hybrid layout. + +## environment\_runtime\_hooks + +This introduces `lxc.environment.runtime` and `lxc.environment.hooks` +configuration keys to allow environment variables to be applied only to the +container init process or only to hooks respectively. +`lxc.environment` remains and still applies to both. diff --git a/src/lxc/api_extensions.h b/src/lxc/api_extensions.h index 6a0b9fe98d..54ca565f86 100644 --- a/src/lxc/api_extensions.h +++ b/src/lxc/api_extensions.h @@ -52,6 +52,7 @@ static char *api_extensions[] = { "idmapped_mounts_v2", "core_scheduling", "cgroup2_auto_mounting", + "environment_runtime_hooks", }; static size_t nr_api_extensions = sizeof(api_extensions) / sizeof(*api_extensions); From e0290fa4aa173f8e5a43adac4e345310bf82ef0f Mon Sep 17 00:00:00 2001 From: Filip Schauer Date: Tue, 16 Sep 2025 11:52:27 +0200 Subject: [PATCH 3/3] doc: add lxc.environment.{runtime, hooks} Signed-off-by: Filip Schauer --- doc/lxc.container.conf.sgml.in | 36 +++++++++++++++++++++++++++++++--- 1 file changed, 33 insertions(+), 3 deletions(-) diff --git a/doc/lxc.container.conf.sgml.in b/doc/lxc.container.conf.sgml.in index 20ae63fd42..39efffbe56 100644 --- a/doc/lxc.container.conf.sgml.in +++ b/doc/lxc.container.conf.sgml.in @@ -2990,7 +2990,14 @@ - This configuration parameter can be specified multiple times; once + Subkeys are available to narrow the scope of environment variables: + lxc.environment.runtime applies only to + the container's init process (and all its descendents), + and lxc.environment.hooks applies only to hooks. + + + + These configuration parameters can be specified multiple times; once for each environment variable you wish to configure. @@ -3001,8 +3008,8 @@ - Specify an environment variable to pass into the container. - Example: + Environment variables applied both to the container init process + and to hooks. Example: lxc.environment = APP_ENV=production @@ -3017,6 +3024,29 @@ + + + + + + + + Environment variables applied only to the container's init + process. + + + + + + + + + + + Environment variables applied only to hooks. + + +