Skip to content

Commit 587e778

Browse files
committed
Add a new plugin to enable Linux-specific namespace functionality
A plugin is a convenient place to hide Linux-specific functionality. Implemented in this initial version are: - Optional private mounts during scriptlet execution, useful for protecting the system from scriptlets (eg /home) and the scriptlets from themselves (eg insecure /tmp usage) - Optionally disable network access during scriptlet execution Note that at this time, scriplets executed with the embedded Lua interpreter are not covered by this because they run inside the main rpm process instead of forking (#2635). Suggested-by: Johannes Segitz <[email protected]> Fixes: #2632 Fixes: #2665
1 parent 1120c8c commit 587e778

File tree

4 files changed

+86
-0
lines changed

4 files changed

+86
-0
lines changed

docs/man/CMakeLists.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,9 @@ if (ENABLE_PLUGINS)
2828
if (WITH_SELINUX)
2929
list(APPEND manuals rpm-plugin-selinux.8)
3030
endif()
31+
if (HAVE_UNSHARE)
32+
list(APPEND manuals rpm-plugin-unshare.8)
33+
endif()
3134
endif()
3235

3336
foreach(man ${manuals})

macros.in

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1194,6 +1194,11 @@ Supplements: (%{name} = %{version}-%{release} and langpacks-%{1})\
11941194
%__transaction_prioreset %{__plugindir}/prioreset.so
11951195
%__transaction_audit %{__plugindir}/audit.so
11961196
%__transaction_dbus_announce %{__plugindir}/dbus_announce.so
1197+
%__transaction_unshare %{__plugindir}/unshare.so
1198+
1199+
# Unshare specific configuration
1200+
%__transaction_unshare_paths /tmp:/home
1201+
%__transaction_unshare_nonet 1
11971202

11981203
#------------------------------------------------------------------------------
11991204
# Macros for further automated spec %setup and patch application

plugins/CMakeLists.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,10 @@ if(WITH_FSVERITY)
3838
target_include_directories(fsverity PRIVATE ${CMAKE_SOURCE_DIR}/sign)
3939
endif()
4040

41+
if (HAVE_UNSHARE)
42+
add_library(unshare MODULE unshare.c)
43+
endif()
44+
4145
set(plugindir ${CMAKE_INSTALL_FULL_LIBDIR}/rpm-plugins)
4246

4347
get_property(plugins DIRECTORY PROPERTY BUILDSYSTEM_TARGETS)

plugins/unshare.c

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
#include "system.h"
2+
3+
#include <sched.h>
4+
#include <sys/mount.h>
5+
#include <errno.h>
6+
#include <string.h>
7+
8+
#include <rpm/rpmts.h>
9+
#include <rpm/rpmplugin.h>
10+
#include <rpm/rpmlog.h>
11+
#include <rpm/rpmmacro.h>
12+
13+
#include "debug.h"
14+
15+
static ARGV_t private_mounts = NULL;
16+
static int unshare_flags = 0;
17+
18+
static rpmRC unshare_init(rpmPlugin plugin, rpmts ts)
19+
{
20+
char *paths = rpmExpand("%{?__transaction_unshare_paths}", NULL);
21+
private_mounts = argvSplitString(paths, ":", ARGV_SKIPEMPTY);
22+
if (private_mounts)
23+
unshare_flags |= CLONE_NEWNS;
24+
free(paths);
25+
26+
if (rpmExpandNumeric("%{?__transaction_unshare_nonet}"))
27+
unshare_flags |= CLONE_NEWNET;
28+
29+
return RPMRC_OK;
30+
}
31+
32+
static void unshare_cleanup(rpmPlugin plugin)
33+
{
34+
/* ensure clean state for possible next transaction */
35+
private_mounts = argvFree(private_mounts);
36+
unshare_flags = 0;
37+
}
38+
39+
static rpmRC unshare_scriptlet_fork_post(rpmPlugin plugin,
40+
const char *path, int type)
41+
{
42+
rpmRC rc = RPMRC_FAIL;
43+
44+
if (unshare_flags && (unshare(unshare_flags) == -1)) {
45+
rpmlog(RPMLOG_ERR, _("unshare with flags x%x failed: %s\n"),
46+
unshare_flags, strerror(errno));
47+
goto exit;
48+
}
49+
50+
if (private_mounts) {
51+
if (mount("/", "/", NULL, MS_REC | MS_PRIVATE, NULL) == -1) {
52+
rpmlog(RPMLOG_ERR, _("failed to mount private %s: %s\n"),
53+
"/", strerror(errno));
54+
goto exit;
55+
}
56+
for (ARGV_t mnt = private_mounts; mnt && *mnt; mnt++) {
57+
if (mount("none", *mnt, "tmpfs", 0, NULL) == -1) {
58+
rpmlog(RPMLOG_ERR, _("failed to mount private %s: %s\n"),
59+
*mnt, strerror(errno));
60+
goto exit;
61+
}
62+
}
63+
}
64+
rc = RPMRC_OK;
65+
66+
exit:
67+
return rc;
68+
}
69+
70+
struct rpmPluginHooks_s unshare_hooks = {
71+
.init = unshare_init,
72+
.cleanup = unshare_cleanup,
73+
.scriptlet_fork_post = unshare_scriptlet_fork_post,
74+
};

0 commit comments

Comments
 (0)