Skip to content

Commit

Permalink
deploy: Try to rebuild policy in new deployment if needed
Browse files Browse the repository at this point in the history
Whenever the user has SELinux enabled and has any local
modules/modifications installed, it is necessary to rebuild the policy
in the final deployment, otherwise ostree will leave the binary policy
files unchanged from last deployment as it detects difference against
the base content (in rpm-ostree case this is the RPM content).

To avoid the situation where the policy binaries go stale once any local
customization of the policy is made, try to rebuild the policy as part
of sysroot_finalize_deployment(). Use the special
--rebuild-if-modules-changed switch, which detects if the input module
files have changed relative to last time the policy was built and skips
the most time-consuming part of the rebuild process if modules are
unchanged (thus making this a relatively cheap operation if the user
hasn't made any modifications to the shipped policy).

As suggested by Jonathan Lebon, this uses bubblewrap (via
g_spawn_sync()) to perform the rebuild inside the deployment's
filesystem tree, which also means that ostree will have a runtime
dependency on bubblewrap.

Partially addresses: coreos/fedora-coreos-tracker#701

Signed-off-by: Ondrej Mosnacek <[email protected]>
  • Loading branch information
WOnder93 authored and cgwalters committed Mar 28, 2022
1 parent 86741ad commit edb4f38
Show file tree
Hide file tree
Showing 2 changed files with 118 additions and 0 deletions.
1 change: 1 addition & 0 deletions ci/gh-install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ case "$ID" in
automake
bison
build-essential
bubblewrap
ca-certificates
cpio
debhelper
Expand Down
117 changes: 117 additions & 0 deletions src/libostree/ostree-sysroot-deploy.c
Original file line number Diff line number Diff line change
Expand Up @@ -2830,6 +2830,118 @@ get_var_dfd (OstreeSysroot *self,
return glnx_opendirat (base_dfd, base_path, TRUE, ret_fd, error);
}

#ifdef HAVE_SELINUX
static void
child_setup_fchdir (gpointer data)
{
int fd = (int) (uintptr_t) data;
int rc __attribute__((unused));

rc = fchdir (fd);
}

/*
* Derived from rpm-ostree's rust/src/bwrap.rs
*/
static gboolean
run_in_deployment (int deployment_dfd,
const gchar * const *child_argv,
gsize child_argc,
gint *exit_status,
gchar **stdout,
GError **error)
{
static const gchar * const COMMON_ARGV[] = {
"/usr/bin/bwrap",
"--dev", "/dev", "--proc", "/proc", "--dir", "/run", "--dir", "/tmp",
"--chdir", "/",
"--die-with-parent",
"--unshare-pid",
"--unshare-uts",
"--unshare-ipc",
"--unshare-cgroup-try",
"--ro-bind", "/sys/block", "/sys/block",
"--ro-bind", "/sys/bus", "/sys/bus",
"--ro-bind", "/sys/class", "/sys/class",
"--ro-bind", "/sys/dev", "/sys/dev",
"--ro-bind", "/sys/devices", "/sys/devices",
"--bind", "usr", "/usr",
"--bind", "etc", "/etc",
"--bind", "var", "/var",
"--symlink", "/usr/lib", "/lib",
"--symlink", "/usr/lib32", "/lib32",
"--symlink", "/usr/lib64", "/lib64",
"--symlink", "/usr/bin", "/bin",
"--symlink", "/usr/sbin", "/sbin",
};
static const gsize COMMON_ARGC = sizeof (COMMON_ARGV) / sizeof (*COMMON_ARGV);

gsize i;
GPtrArray *args = g_ptr_array_sized_new (COMMON_ARGC + child_argc + 1);
g_autofree gchar **args_raw = NULL;

for (i = 0; i < COMMON_ARGC; i++)
g_ptr_array_add (args, (gchar *) COMMON_ARGV[i]);

for (i = 0; i < child_argc; i++)
g_ptr_array_add (args, (gchar *) child_argv[i]);

g_ptr_array_add (args, NULL);

args_raw = (gchar **) g_ptr_array_free (args, FALSE);

return g_spawn_sync (NULL, args_raw, NULL, 0, &child_setup_fchdir,
(gpointer) (uintptr_t) deployment_dfd,
stdout, NULL, exit_status, error);
}

/*
* Run semodule to check if the module content changed after merging /etc
* and rebuild the policy if needed.
*/
static gboolean
sysroot_finalize_selinux_policy (int deployment_dfd, GError **error)
{
struct stat stbuf;
gint exit_status;
g_autofree gchar *stdout = NULL;

if (!glnx_fstatat_allow_noent (deployment_dfd, "etc/selinux/config", &stbuf,
AT_SYMLINK_NOFOLLOW, error))
return FALSE;

/* Skip the SELinux policy refresh if /etc/selinux/config doesn't exist. */
if (errno != 0)
return TRUE;

/*
* Skip the SELinux policy refresh if the --rebuild-if-modules-changed
* flag is not supported by semodule.
*/
static const gchar * const SEMODULE_HELP_ARGV[] = {
"semodule", "--help"
};
static const gsize SEMODULE_HELP_ARGC = sizeof (SEMODULE_HELP_ARGV) / sizeof (*SEMODULE_HELP_ARGV);
if (!run_in_deployment (deployment_dfd, SEMODULE_HELP_ARGV,
SEMODULE_HELP_ARGC, &exit_status, &stdout, error))
return FALSE;
if (!g_spawn_check_exit_status (exit_status, error))
return FALSE;
if (!strstr(stdout, "--rebuild-if-modules-changed"))
return TRUE;

static const gchar * const SEMODULE_REBUILD_ARGV[] = {
"semodule", "-N", "--rebuild-if-modules-changed"
};
static const gsize SEMODULE_REBUILD_ARGC = sizeof (SEMODULE_REBUILD_ARGV) / sizeof (*SEMODULE_REBUILD_ARGV);

if (!run_in_deployment (deployment_dfd, SEMODULE_REBUILD_ARGV,
SEMODULE_REBUILD_ARGC, &exit_status, NULL, error))
return FALSE;
return g_spawn_check_exit_status (exit_status, error);
}
#endif /* HAVE_SELINUX */

static gboolean
sysroot_finalize_deployment (OstreeSysroot *self,
OstreeDeployment *deployment,
Expand Down Expand Up @@ -2866,6 +2978,11 @@ sysroot_finalize_deployment (OstreeSysroot *self,
return FALSE;
}

#ifdef HAVE_SELINUX
if (!sysroot_finalize_selinux_policy(deployment_dfd, error))
return FALSE;
#endif /* HAVE_SELINUX */

const char *osdeploypath = glnx_strjoina ("ostree/deploy/", ostree_deployment_get_osname (deployment));
glnx_autofd int os_deploy_dfd = -1;
if (!glnx_opendirat (self->sysroot_fd, osdeploypath, TRUE, &os_deploy_dfd, error))
Expand Down

0 comments on commit edb4f38

Please sign in to comment.