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)