diff --git a/.github/workflows/builds.yml b/.github/workflows/builds.yml index 3344479e37..81eb6ef472 100644 --- a/.github/workflows/builds.yml +++ b/.github/workflows/builds.yml @@ -85,7 +85,7 @@ jobs: mv ../lxc_* out/ - name: Upload resulting build - uses: actions/upload-artifact@v5 + uses: actions/upload-artifact@v6 continue-on-error: true with: name: ${{ matrix.os }} diff --git a/.github/workflows/fuzzing.yml b/.github/workflows/fuzzing.yml index d221dcfd16..efd074feb9 100644 --- a/.github/workflows/fuzzing.yml +++ b/.github/workflows/fuzzing.yml @@ -37,7 +37,7 @@ jobs: sanitizer: ${{ matrix.sanitizer }} - name: Upload Crash - uses: actions/upload-artifact@v5 + uses: actions/upload-artifact@v6 if: failure() && steps.build.outcome == 'success' with: name: ${{ matrix.sanitizer }}-artifacts diff --git a/doc/lxc-create.sgml.in b/doc/lxc-create.sgml.in index 5ad6c7f29f..28b1820e6f 100644 --- a/doc/lxc-create.sgml.in +++ b/doc/lxc-create.sgml.in @@ -139,6 +139,7 @@ You can specify the following options : --rbdname RBDNAME will create a blockdevice named RBDNAME rather than the default, which is the container name. --rbdpool POOL will create the blockdevice in the pool named POOL, rather than the default, which is 'lxc'. + --rbduser RBDUSER will specify the ceph user RBDUSER creating the blockdevice, rather than the default, which is 'admin'. If backingstore is 'best', then lxc will try, in order, btrfs, diff --git a/doc/lxc.sgml.in b/doc/lxc.sgml.in index f4c5848ff3..4505db806c 100644 --- a/doc/lxc.sgml.in +++ b/doc/lxc.sgml.in @@ -206,6 +206,21 @@ rootfs + + Unprivileged containers + + Unprivileged LXC containers run without root host-level privileges in a + user namespace, mapping container UID 0 to a non-root host ID, which + strictly limits the accessible devices and filesystems of the + container. In order to mount a rootfs in an unprivileged container, the + mapped host user must have execute permissions for all directories + along the path to and including the rootfs. Additionally, all files and + directories under the rootfs must be owned by the correct user ID and + group ID. The correct user ID and group ID are the host IDs mapped to + the container root(UID 0) in lxc.idmap. + + + Creating / Destroying containers diff --git a/doc/meson.build b/doc/meson.build index 2932314d9f..79d52fecc2 100644 --- a/doc/meson.build +++ b/doc/meson.build @@ -72,3 +72,16 @@ if want_mans install_dir: join_paths(mandir, 'man' + page[1])) endforeach endif + +# API Docs (Doxygen) +if want_api_docs + doxygen = find_program('doxygen', required: true) + + api_docs = custom_target( + 'api-docs', + input: files('api/Doxyfile'), + output: 'api-docs.stamp', + command: [doxygen, '@INPUT@'], + build_by_default: true + ) +endif \ No newline at end of file diff --git a/meson.build b/meson.build index 4b3a8f07f1..40973279e7 100644 --- a/meson.build +++ b/meson.build @@ -133,6 +133,7 @@ want_install_init = get_option('install-init-files') want_io_uring = get_option('io-uring-event-loop') want_pam_cgroup = get_option('pam-cgroup') want_mans = get_option('man') +want_api_docs = get_option('api-docs') want_tests = get_option('tests') want_tools = get_option('tools') want_tools_multicall = get_option('tools-multicall') @@ -341,6 +342,12 @@ if not sgml2man.found() endif endif +## API Docs. +doxygen = find_program('doxygen', required: false) +if not doxygen.found() and want_api_docs + error('missing required doxygen dependency') +endif + ## Threads. threads = dependency('threads') liblxc_dependencies += threads diff --git a/meson_options.txt b/meson_options.txt index f00e7406da..f93ab1f607 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -31,6 +31,10 @@ option('io-uring-event-loop', type: 'boolean', value: false, option('man', type: 'boolean', value: true, description: 'build and install manpages') +# was --{disable,enable}-api-docs in autotools +option('api-docs', type: 'boolean', value: false, + description: 'build and install API documentation (Doxygen)') + # was --{disable,enable}-pam in autotools option('pam-cgroup', type: 'boolean', value: false, description: 'build and install the pam cgroup module') diff --git a/src/lxc/initutils.c b/src/lxc/initutils.c index 2081692475..181f5841c2 100644 --- a/src/lxc/initutils.c +++ b/src/lxc/initutils.c @@ -49,6 +49,7 @@ const char *lxc_global_config_value(const char *option_name) { "lxc.bdev.lvm.thin_pool", DEFAULT_THIN_POOL }, { "lxc.bdev.zfs.root", DEFAULT_ZFSROOT }, { "lxc.bdev.rbd.rbdpool", DEFAULT_RBDPOOL }, + { "lxc.bdev.rbd.rbduser", DEFAULT_RBDUSER }, { "lxc.lxcpath", NULL }, { "lxc.default_config", NULL }, { "lxc.cgroup.pattern", NULL }, diff --git a/src/lxc/initutils.h b/src/lxc/initutils.h index 2e749063f0..6ae0997ace 100644 --- a/src/lxc/initutils.h +++ b/src/lxc/initutils.h @@ -25,6 +25,7 @@ #define DEFAULT_THIN_POOL "lxc" #define DEFAULT_ZFSROOT "lxc" #define DEFAULT_RBDPOOL "lxc" +#define DEFAULT_RBDUSER NULL #ifndef PR_SET_MM #define PR_SET_MM 35 diff --git a/src/lxc/lxccontainer.c b/src/lxc/lxccontainer.c index 6f110ea3b7..ea3a31bb4a 100644 --- a/src/lxc/lxccontainer.c +++ b/src/lxc/lxccontainer.c @@ -3468,6 +3468,10 @@ static void copy_rdepends(struct lxc_container *c, struct lxc_container *c0) return; } + if (!file_exists(path0)) { + return; + } + if (copy_file(path0, path1) < 0) { INFO("Error copying reverse dependencies"); return; diff --git a/src/lxc/lxccontainer.h b/src/lxc/lxccontainer.h index 2d90fe8d8e..ee94840bfe 100644 --- a/src/lxc/lxccontainer.h +++ b/src/lxc/lxccontainer.h @@ -936,6 +936,7 @@ struct bdev_specs { struct { char *rbdname; /*!< RBD image name */ char *rbdpool; /*!< Ceph pool name */ + char *rbduser; /*!< Ceph user name*/ } rbd; }; diff --git a/src/lxc/start.c b/src/lxc/start.c index 9311d61452..b5ca683dbd 100644 --- a/src/lxc/start.c +++ b/src/lxc/start.c @@ -1307,9 +1307,6 @@ static int do_start(void *data) if (!handler->conf->root_nsgid_map) nsgid = handler->conf->init_gid; - /* Drop groups only after we switched to a valid gid in the new - * user namespace. - */ if (!lxc_drop_groups() && (handler->am_root || errno != EPERM)) goto out_warn_father; @@ -1606,17 +1603,19 @@ static int do_start(void *data) if (lxc_proc_cap_is_set(CAP_SETGID, CAP_EFFECTIVE)) #endif { - if (handler->conf->init_groups.size > 0) { - if (!lxc_setgroups(handler->conf->init_groups.list, - handler->conf->init_groups.size)) - goto out_warn_father; - } else { + if (handler->conf->init_groups.size == 0) { if (!lxc_drop_groups()) goto out_warn_father; } } } + if (handler->conf->init_groups.size > 0) { + if (!lxc_setgroups(handler->conf->init_groups.list, + handler->conf->init_groups.size)) + goto out_warn_father; + } + if (!lxc_switch_uid_gid(new_uid, new_gid)) goto out_warn_father; diff --git a/src/lxc/storage/rbd.c b/src/lxc/storage/rbd.c index c6d8204658..50227f48fa 100644 --- a/src/lxc/storage/rbd.c +++ b/src/lxc/storage/rbd.c @@ -22,6 +22,7 @@ lxc_log_define(rbd, lxc); struct rbd_args { const char *osd_pool_name; + const char *rbd_user; const char *rbd_name; const char *size; }; @@ -30,8 +31,14 @@ static int rbd_create_wrapper(void *data) { struct rbd_args *args = data; - execlp("rbd", "rbd", "create", "--pool", args->osd_pool_name, + if (args->rbd_user){ + execlp("rbd", "rbd", "create", "--id", args->rbd_user, "--pool", + args->osd_pool_name, args->rbd_name, "--size", args->size, + (char *)NULL); + } else { + execlp("rbd", "rbd", "create", "--pool", args->osd_pool_name, args->rbd_name, "--size", args->size, (char *)NULL); + } return -1; } @@ -40,9 +47,13 @@ static int rbd_map_wrapper(void *data) { struct rbd_args *args = data; - execlp("rbd", "rbd", "map", "--pool", args->osd_pool_name, + if (args->rbd_user){ + execlp("rbd", "rbd", "map", "--id", args->rbd_user, "--pool", + args->osd_pool_name, args->rbd_name, (char *)NULL); + } else { + execlp("rbd", "rbd", "map", "--pool", args->osd_pool_name, args->rbd_name, (char *)NULL); - + } return -1; } @@ -50,7 +61,13 @@ static int rbd_unmap_wrapper(void *data) { struct rbd_args *args = data; - execlp("rbd", "rbd", "unmap", args->rbd_name, (char *)NULL); + if (args->rbd_user){ + execlp("rbd", "rbd", "unmap", "--id", args->rbd_user, args->rbd_name, + (char *)NULL); + } else { + execlp("rbd", "rbd", "unmap", args->rbd_name, (char *)NULL); + } + return -1; } @@ -59,7 +76,12 @@ static int rbd_delete_wrapper(void *data) { struct rbd_args *args = data; - execlp("rbd", "rbd", "rm", args->rbd_name, (char *)NULL); + if (args->rbd_user){ + execlp("rbd", "rbd", "rm", "--id", args->rbd_user, args->rbd_name, + (char *)NULL); + } else { + execlp("rbd", "rbd", "rm", args->rbd_name, (char *)NULL); + } return -1; } @@ -83,6 +105,7 @@ int rbd_create(struct lxc_storage *bdev, const char *dest, const char *n, const char *cmd_args[2]; char cmd_output[PATH_MAX]; const char *rbdname = n; + const char *rbduser; struct rbd_args args = {0}; if (!specs) @@ -94,6 +117,10 @@ int rbd_create(struct lxc_storage *bdev, const char *dest, const char *n, if (specs->rbd.rbdname) rbdname = specs->rbd.rbdname; + + rbduser = specs->rbd.rbduser; + if (!rbduser) + rbduser = lxc_global_config_value("lxc.bdev.rbd.rbduser"); /* source device /dev/rbd/lxc/ctn */ len = strlen(rbdpool) + strlen(rbdname) + 4 + 11; @@ -123,6 +150,7 @@ int rbd_create(struct lxc_storage *bdev, const char *dest, const char *n, args.osd_pool_name = rbdpool; args.rbd_name = rbdname; + args.rbd_user = rbduser; args.size = sz; ret = run_command(cmd_output, sizeof(cmd_output), rbd_create_wrapper, (void *)&args); @@ -179,6 +207,8 @@ int rbd_destroy(struct lxc_storage *orig) struct rbd_args args = {0}; size_t len; + args.rbd_user = lxc_global_config_value("lxc.bdev.rbd.rbduser"); + src = lxc_storage_get_path(orig->src, orig->type); if (file_exists(src)) { args.rbd_name = src; diff --git a/src/lxc/tools/arguments.h b/src/lxc/tools/arguments.h index 92510ecbca..3c9ae4365b 100644 --- a/src/lxc/tools/arguments.h +++ b/src/lxc/tools/arguments.h @@ -73,7 +73,7 @@ struct lxc_arguments { char *fstype; uint64_t fssize; char *lvname, *vgname, *thinpool; - char *rbdname, *rbdpool; + char *rbdname, *rbdpool, *rbduser; char *zfsroot, *lowerdir, *dir; /* lxc-execute and lxc-unshare */ diff --git a/src/lxc/tools/lxc_create.c b/src/lxc/tools/lxc_create.c index 41756078c6..15d8b10427 100644 --- a/src/lxc/tools/lxc_create.c +++ b/src/lxc/tools/lxc_create.c @@ -36,6 +36,7 @@ static const struct option my_longopts[] = { {"dir", required_argument, 0, '6'}, {"rbdname", required_argument, 0, '7'}, {"rbdpool", required_argument, 0, '8'}, + {"rbduser", required_argument, 0, '9'}, LXC_COMMON_OPTIONS }; @@ -67,6 +68,8 @@ Options :\n\ (Default: container name)\n\ --rbdpool=POOL Use Ceph RBD pool name POOL\n\ (Default: lxc)\n\ + --rbduser=RBDUSER Use Ceph user RBDUSER for auth\n\ + (Default: admin)\n\ \n\ BDEV option for ZFS (with -B/--bdev zfs) :\n\ --zfsroot=PATH Create zfs under given zfsroot\n\ @@ -128,6 +131,9 @@ static int my_parser(struct lxc_arguments *args, int c, char *arg) case '8': args->rbdpool = arg; break; + case '9': + args->rbduser = arg; + break; } return 0; } @@ -175,7 +181,7 @@ static bool validate_bdev_args(struct lxc_arguments *args) } if (strncmp(args->bdevtype, "rbd", strlen(args->bdevtype)) != 0) - if (args->rbdname || args->rbdpool) { + if (args->rbdname || args->rbdpool ) { ERROR("--rbdname and --rbdpool are only valid with -B rbd"); return false; } @@ -303,6 +309,9 @@ int lxc_create_main(int argc, char *argv[]) if (my_args.rbdpool) spec.rbd.rbdpool = my_args.rbdpool; + + if(my_args.rbduser) + spec.rbd.rbduser = my_args.rbduser; } if (my_args.dir)