repo/commit: Add support for --selinux-policy-from-base

The [dev-overlay](332c6ab3b9/src/cmd-dev-overlay)
script shipped in coreos-assembler mostly exists to deal
with the nontrivial logic around SELinux policy.  Let's make
the use case of "commit some binaries overlaying a base tree, using
the base's selinux policy" just require a magical
`--selinux-policy-from-base` argument to `ostree commit`.

A new C API was added to implement this in the case of `--tree=ref`;
when the base directory is already checked out, we can just reuse
the existing logic that `--selinux-policy` was using.

Requires: https://github.com/ostreedev/ostree/pull/2039
This commit is contained in:
Colin Walters 2020-03-21 14:48:24 +00:00
parent 41d3dfa7b8
commit b3bbbd1542
7 changed files with 121 additions and 16 deletions

View File

@ -379,6 +379,7 @@ ostree_repo_commit_modifier_new
OstreeRepoCommitModifierXattrCallback OstreeRepoCommitModifierXattrCallback
ostree_repo_commit_modifier_set_xattr_callback ostree_repo_commit_modifier_set_xattr_callback
ostree_repo_commit_modifier_set_sepolicy ostree_repo_commit_modifier_set_sepolicy
ostree_repo_commit_modifier_set_sepolicy_from_commit
ostree_repo_commit_modifier_set_devino_cache ostree_repo_commit_modifier_set_devino_cache
ostree_repo_commit_modifier_ref ostree_repo_commit_modifier_ref
ostree_repo_commit_modifier_unref ostree_repo_commit_modifier_unref

View File

@ -20,7 +20,7 @@
/* Add new symbols here. Release commits should copy this section into -released.sym. */ /* Add new symbols here. Release commits should copy this section into -released.sym. */
LIBOSTREE_2020.2 { LIBOSTREE_2020.2 {
global: global:
someostree_symbol_deleteme; ostree_repo_commit_modifier_set_sepolicy_from_commit;
} LIBOSTREE_2020.1; } LIBOSTREE_2020.1;
/* Stub section for the stable release *after* this development one; don't /* Stub section for the stable release *after* this development one; don't

View File

@ -4227,9 +4227,11 @@ ostree_repo_commit_modifier_unref (OstreeRepoCommitModifier *modifier)
if (modifier->xattr_destroy) if (modifier->xattr_destroy)
modifier->xattr_destroy (modifier->xattr_user_data); modifier->xattr_destroy (modifier->xattr_user_data);
g_clear_object (&modifier->sepolicy);
g_clear_pointer (&modifier->devino_cache, (GDestroyNotify)g_hash_table_unref); g_clear_pointer (&modifier->devino_cache, (GDestroyNotify)g_hash_table_unref);
g_clear_object (&modifier->sepolicy);
(void) glnx_tmpdir_delete (&modifier->sepolicy_tmpdir, NULL, NULL);
g_free (modifier); g_free (modifier);
return; return;
} }
@ -4279,6 +4281,60 @@ ostree_repo_commit_modifier_set_sepolicy (OstreeRepoCommitModifier
modifier->sepolicy = sepolicy ? g_object_ref (sepolicy) : NULL; modifier->sepolicy = sepolicy ? g_object_ref (sepolicy) : NULL;
} }
/**
* ostree_repo_commit_modifier_set_sepolicy_from_commit:
* @modifier: Commit modifier
* @repo: OSTree repo containing @rev
* @rev: Find SELinux policy from this base commit
* @cancellable:
* @error:
*
* In many cases, one wants to create a "derived" commit from base commit.
* SELinux policy labels are part of that base commit. This API allows
* one to easily set up SELinux labeling from a base commit.
*/
gboolean
ostree_repo_commit_modifier_set_sepolicy_from_commit (OstreeRepoCommitModifier *modifier,
OstreeRepo *repo,
const char *rev,
GCancellable *cancellable,
GError **error)
{
GLNX_AUTO_PREFIX_ERROR ("setting sepolicy from commit", error);
g_autofree char *commit = NULL;
g_autoptr(GFile) root = NULL;
if (!ostree_repo_read_commit (repo, rev, &root, &commit, cancellable, error))
return FALSE;
const char policypath[] = "usr/etc/selinux";
g_autoptr(GFile) policyroot = g_file_get_child (root, policypath);
if (!g_file_query_exists (policyroot, NULL))
return TRUE; /* No policy, nothing to do */
GLnxTmpDir tmpdir = {0,};
if (!glnx_mkdtemp ("ostree-commit-sepolicy-XXXXXX", 0700, &tmpdir, error))
return FALSE;
if (!glnx_shutil_mkdir_p_at (tmpdir.fd, "usr/etc", 0755, cancellable, error))
return FALSE;
OstreeRepoCheckoutAtOptions coopts = {0,};
coopts.mode = OSTREE_REPO_CHECKOUT_MODE_USER;
coopts.subpath = glnx_strjoina ("/", policypath);
if (!ostree_repo_checkout_at (repo, &coopts, tmpdir.fd, policypath, commit, cancellable, error))
return glnx_prefix_error (error, "policy checkout");
g_autoptr(OstreeSePolicy) policy = ostree_sepolicy_new_at (tmpdir.fd, cancellable, error);
if (!policy)
return glnx_prefix_error (error, "reading policy");
ostree_repo_commit_modifier_set_sepolicy (modifier, policy);
/* Transfer ownership */
modifier->sepolicy_tmpdir = tmpdir;
tmpdir.initialized = FALSE;
return TRUE;
}
/** /**
* ostree_repo_commit_modifier_set_devino_cache: * ostree_repo_commit_modifier_set_devino_cache:
* @modifier: Modifier * @modifier: Modifier

View File

@ -76,6 +76,7 @@ struct OstreeRepoCommitModifier {
GDestroyNotify xattr_destroy; GDestroyNotify xattr_destroy;
gpointer xattr_user_data; gpointer xattr_user_data;
GLnxTmpDir sepolicy_tmpdir;
OstreeSePolicy *sepolicy; OstreeSePolicy *sepolicy;
GHashTable *devino_cache; GHashTable *devino_cache;
}; };

View File

@ -691,6 +691,13 @@ _OSTREE_PUBLIC
void ostree_repo_commit_modifier_set_sepolicy (OstreeRepoCommitModifier *modifier, void ostree_repo_commit_modifier_set_sepolicy (OstreeRepoCommitModifier *modifier,
OstreeSePolicy *sepolicy); OstreeSePolicy *sepolicy);
_OSTREE_PUBLIC
gboolean ostree_repo_commit_modifier_set_sepolicy_from_commit (OstreeRepoCommitModifier *modifier,
OstreeRepo *repo,
const char *commit,
GCancellable *cancellable,
GError **error);
_OSTREE_PUBLIC _OSTREE_PUBLIC
void ostree_repo_commit_modifier_set_devino_cache (OstreeRepoCommitModifier *modifier, void ostree_repo_commit_modifier_set_devino_cache (OstreeRepoCommitModifier *modifier,
OstreeRepoDevInoCache *cache); OstreeRepoDevInoCache *cache);

View File

@ -53,6 +53,7 @@ static gboolean opt_tar_autocreate_parents;
static char *opt_tar_pathname_filter; static char *opt_tar_pathname_filter;
static gboolean opt_no_xattrs; static gboolean opt_no_xattrs;
static char *opt_selinux_policy; static char *opt_selinux_policy;
static gboolean opt_selinux_policy_from_base;
static gboolean opt_canonical_permissions; static gboolean opt_canonical_permissions;
static gboolean opt_consume; static gboolean opt_consume;
static gboolean opt_devino_canonical; static gboolean opt_devino_canonical;
@ -107,6 +108,7 @@ static GOptionEntry options[] = {
{ "canonical-permissions", 0, 0, G_OPTION_ARG_NONE, &opt_canonical_permissions, "Canonicalize permissions in the same way bare-user does for hardlinked files", NULL }, { "canonical-permissions", 0, 0, G_OPTION_ARG_NONE, &opt_canonical_permissions, "Canonicalize permissions in the same way bare-user does for hardlinked files", NULL },
{ "no-xattrs", 0, 0, G_OPTION_ARG_NONE, &opt_no_xattrs, "Do not import extended attributes", NULL }, { "no-xattrs", 0, 0, G_OPTION_ARG_NONE, &opt_no_xattrs, "Do not import extended attributes", NULL },
{ "selinux-policy", 0, 0, G_OPTION_ARG_FILENAME, &opt_selinux_policy, "Set SELinux labels based on policy in root filesystem PATH (may be /)", "PATH" }, { "selinux-policy", 0, 0, G_OPTION_ARG_FILENAME, &opt_selinux_policy, "Set SELinux labels based on policy in root filesystem PATH (may be /)", "PATH" },
{ "selinux-policy-from-base", 'P', 0, G_OPTION_ARG_NONE, &opt_selinux_policy_from_base, "Set SELinux labels based on first --tree argument", NULL },
{ "link-checkout-speedup", 0, 0, G_OPTION_ARG_NONE, &opt_link_checkout_speedup, "Optimize for commits of trees composed of hardlinks into the repository", NULL }, { "link-checkout-speedup", 0, 0, G_OPTION_ARG_NONE, &opt_link_checkout_speedup, "Optimize for commits of trees composed of hardlinks into the repository", NULL },
{ "devino-canonical", 'I', 0, G_OPTION_ARG_NONE, &opt_devino_canonical, "Assume hardlinked objects are unmodified. Implies --link-checkout-speedup", NULL }, { "devino-canonical", 'I', 0, G_OPTION_ARG_NONE, &opt_devino_canonical, "Assume hardlinked objects are unmodified. Implies --link-checkout-speedup", NULL },
{ "tar-autocreate-parents", 0, 0, G_OPTION_ARG_NONE, &opt_tar_autocreate_parents, "When loading tar archives, automatically create parent directories as needed", NULL }, { "tar-autocreate-parents", 0, 0, G_OPTION_ARG_NONE, &opt_tar_autocreate_parents, "When loading tar archives, automatically create parent directories as needed", NULL },
@ -550,6 +552,11 @@ ostree_builtin_commit (int argc, char **argv, OstreeCommandInvocation *invocatio
flags |= OSTREE_REPO_COMMIT_MODIFIER_FLAGS_GENERATE_SIZES; flags |= OSTREE_REPO_COMMIT_MODIFIER_FLAGS_GENERATE_SIZES;
if (opt_disable_fsync) if (opt_disable_fsync)
ostree_repo_set_disable_fsync (repo, TRUE); ostree_repo_set_disable_fsync (repo, TRUE);
if (opt_selinux_policy && opt_selinux_policy_from_base)
{
glnx_throw (error, "Cannot specify both --selinux-policy and --selinux-policy-from-base");
goto out;
}
if (flags != 0 if (flags != 0
|| opt_owner_uid >= 0 || opt_owner_uid >= 0
@ -557,25 +564,13 @@ ostree_builtin_commit (int argc, char **argv, OstreeCommandInvocation *invocatio
|| opt_statoverride_file != NULL || opt_statoverride_file != NULL
|| opt_skiplist_file != NULL || opt_skiplist_file != NULL
|| opt_no_xattrs || opt_no_xattrs
|| opt_selinux_policy) || opt_selinux_policy
|| opt_selinux_policy_from_base)
{ {
filter_data.mode_adds = mode_adds; filter_data.mode_adds = mode_adds;
filter_data.skip_list = skip_list; filter_data.skip_list = skip_list;
modifier = ostree_repo_commit_modifier_new (flags, commit_filter, modifier = ostree_repo_commit_modifier_new (flags, commit_filter,
&filter_data, NULL); &filter_data, NULL);
if (opt_selinux_policy)
{
glnx_autofd int rootfs_dfd = -1;
if (!glnx_opendirat (AT_FDCWD, opt_selinux_policy, TRUE, &rootfs_dfd, error))
{
g_prefix_error (error, "selinux-policy: ");
goto out;
}
policy = ostree_sepolicy_new_at (rootfs_dfd, cancellable, error);
if (!policy)
goto out;
ostree_repo_commit_modifier_set_sepolicy (modifier, policy);
}
} }
if (opt_editor) if (opt_editor)
@ -621,6 +616,7 @@ ostree_builtin_commit (int argc, char **argv, OstreeCommandInvocation *invocatio
g_assert (opt_trees && *opt_trees); g_assert (opt_trees && *opt_trees);
for (tree_iter = (const char *const*)opt_trees; *tree_iter; tree_iter++) for (tree_iter = (const char *const*)opt_trees; *tree_iter; tree_iter++)
{ {
const gboolean first = (tree_iter == (const char *const*)opt_trees);
tree = *tree_iter; tree = *tree_iter;
eq = strchr (tree, '='); eq = strchr (tree, '=');
@ -637,12 +633,33 @@ ostree_builtin_commit (int argc, char **argv, OstreeCommandInvocation *invocatio
g_clear_object (&object_to_commit); g_clear_object (&object_to_commit);
if (strcmp (tree_type, "dir") == 0) if (strcmp (tree_type, "dir") == 0)
{ {
if (first && opt_selinux_policy_from_base)
{
opt_selinux_policy = g_strdup (tree);
opt_selinux_policy_from_base = FALSE;
}
if (first && opt_selinux_policy)
{
g_assert (modifier);
glnx_autofd int rootfs_dfd = -1;
if (!glnx_opendirat (AT_FDCWD, opt_selinux_policy, TRUE, &rootfs_dfd, error))
goto out;
policy = ostree_sepolicy_new_at (rootfs_dfd, cancellable, error);
if (!policy)
goto out;
ostree_repo_commit_modifier_set_sepolicy (modifier, policy);
}
if (!ostree_repo_write_dfd_to_mtree (repo, AT_FDCWD, tree, mtree, modifier, if (!ostree_repo_write_dfd_to_mtree (repo, AT_FDCWD, tree, mtree, modifier,
cancellable, error)) cancellable, error))
goto out; goto out;
} }
else if (strcmp (tree_type, "tar") == 0) else if (strcmp (tree_type, "tar") == 0)
{ {
if (first && opt_selinux_policy_from_base)
{
glnx_throw (error, "Cannot use --selinux-policy-from-base with tar");
goto out;
}
if (!opt_tar_pathname_filter) if (!opt_tar_pathname_filter)
{ {
if (strcmp (tree, "-") == 0) if (strcmp (tree, "-") == 0)
@ -707,6 +724,12 @@ ostree_builtin_commit (int argc, char **argv, OstreeCommandInvocation *invocatio
} }
else if (strcmp (tree_type, "ref") == 0) else if (strcmp (tree_type, "ref") == 0)
{ {
if (first && opt_selinux_policy_from_base)
{
g_assert (modifier);
if (!ostree_repo_commit_modifier_set_sepolicy_from_commit (modifier, repo, tree, cancellable, error))
goto out;
}
if (!ostree_repo_read_commit (repo, tree, &object_to_commit, NULL, cancellable, error)) if (!ostree_repo_read_commit (repo, tree, &object_to_commit, NULL, cancellable, error))
goto out; goto out;

View File

@ -6,6 +6,7 @@ set -xeuo pipefail
. ${KOLA_EXT_DATA}/libinsttest.sh . ${KOLA_EXT_DATA}/libinsttest.sh
require_writable_sysroot require_writable_sysroot
prepare_tmpdir /var/tmp
date date
cd /ostree/repo/tmp cd /ostree/repo/tmp
@ -87,3 +88,19 @@ rm co -rf
ostree refs --delete testbranch ostree refs --delete testbranch
echo "ok checkout selinux and skip-list" echo "ok checkout selinux and skip-list"
date date
mkdir -p usr/{bin,lib,etc}
echo 'somebinary' > usr/bin/somebinary
ls -Z usr/bin/somebinary > lsz.txt
assert_not_file_has_content lsz.txt ':bin_t:'
rm -f lsz.txt
echo 'somelib' > usr/lib/somelib.so
echo 'someconf' > usr/etc/some.conf
ostree commit -b newbase --selinux-policy-from-base --tree=ref=${host_refspec} --tree=dir=$(pwd)
ostree ls -X newbase /usr/bin/somebinary > newls.txt
assert_file_has_content newls.txt ':bin_t:'
ostree ls -X newbase /usr/lib/somelib.so > newls.txt
assert_file_has_content newls.txt ':lib_t:'
ostree ls -X newbase /usr/etc/some.conf > newls.txt
assert_file_has_content newls.txt ':etc_t:'
echo "ok commit --selinux-policy-from-base"