core,scripts: When no cachedir+unified-core, disable rofiles-fuse

This is prep for running inside (unprivileged) Kube containers
as they exist today: https://github.com/projectatomic/rpm-ostree/issues/1329

Sadly FUSE today uses a suid binary that ends up wanting CAP_SYS_ADMIN.
I think there's some work on FUSE-in-containers but I'm not sure of
the current status.

What rofiles-fuse here is doing here is protecting is the hardlinked
repo imports.  But if `--cachedir` isn't specified, that repository
gets thrown away anyways.  So there's no real value to using FUSE
here.

Also since nothing is cached, disable the devino cache.

We also make use of --force-copy-zerosized that just landed
in libostree: https://github.com/ostreedev/ostree/pull/1752

Down the line ideally we gain the capability to detect if either
unprivileged overlayfs/FUSE are available.  Then if `--cachedir`
is specified we can make things work.

Closes: #1591
Approved by: jlebon
This commit is contained in:
Colin Walters 2018-04-09 14:10:33 -04:00 committed by Atomic Bot
parent 8ba5bda821
commit b3f6f25637
8 changed files with 97 additions and 14 deletions

View File

@ -113,6 +113,7 @@ typedef struct {
int workdir_dfd;
int rootfs_dfd;
int cachedir_dfd;
gboolean unified_core_and_fuse;
OstreeRepo *repo;
OstreeRepo *pkgcache_repo;
OstreeRepoDevInoCache *devino_cache;
@ -340,9 +341,37 @@ install_packages_in_root (RpmOstreeTreeComposeContext *self,
cancellable, error);
if (!self->pkgcache_repo)
return FALSE;
if (!opt_cachedir)
{
/* This is part of enabling rpm-ostree inside Docker/Kubernetes/OpenShift;
* in this case we probably don't have access to FUSE as today it uses a
* suid binary which doesn't have the capabilities it needs.
*
* So this magical bit tells the core to disable FUSE, which we only do
* if --cachedir isn't specified. Another way to say this is that
* running inside an unprivileged container today requires turning off
* some of the rpm-ostree intelligence around caching.
*
* We don't make this actually conditional somehow on running in a
* container since if you're not using a persistent cache there's no
* real advantage to taking the overhead of FUSE. If the hardlinks are
* corrupted, it doesn't matter as they're going to be deleted
* anyways.
*/
rpmostree_context_disable_rofiles (self->corectx);
}
else
{
self->unified_core_and_fuse = TRUE;
/* We also only enable the devino cache if we know we have the FUSE protection
* against mutation of the underlying files.
*/
self->devino_cache = ostree_repo_devino_cache_new ();
rpmostree_context_set_devino_cache (self->corectx, self->devino_cache);
}
rpmostree_context_set_repos (self->corectx, self->repo, self->pkgcache_repo);
self->devino_cache = ostree_repo_devino_cache_new ();
rpmostree_context_set_devino_cache (self->corectx, self->devino_cache);
/* Ensure that the imported packages are labeled with *a* policy if
* possible, even if it's not the final one. This helps avoid duplicating
@ -886,7 +915,7 @@ impl_install_tree (RpmOstreeTreeComposeContext *self,
/* Start postprocessing */
if (!rpmostree_treefile_postprocessing (self->rootfs_dfd, self->treefile_rs, self->treefile,
next_version, opt_unified_core,
next_version, self->unified_core_and_fuse,
cancellable, error))
return glnx_prefix_error (error, "Postprocessing");
@ -999,7 +1028,7 @@ impl_commit_tree (RpmOstreeTreeComposeContext *self,
if (!rpmostree_rootfs_postprocess_common (self->rootfs_dfd, cancellable, error))
return FALSE;
if (!rpmostree_postprocess_final (self->rootfs_dfd, self->treefile, opt_unified_core,
if (!rpmostree_postprocess_final (self->rootfs_dfd, self->treefile, self->unified_core_and_fuse,
cancellable, error))
return FALSE;

View File

@ -42,6 +42,7 @@ struct _RpmOstreeContext {
RpmOstreeContextDnfCachePolicy dnf_cache_policy;
OstreeRepo *ostreerepo;
OstreeRepo *pkgcache_repo;
gboolean enable_rofiles;
OstreeRepoDevInoCache *devino_cache;
gboolean unprivileged;
OstreeSePolicy *sepolicy;

View File

@ -381,6 +381,7 @@ rpmostree_context_init (RpmOstreeContext *self)
{
self->tmprootfs_dfd = -1;
self->dnf_cache_policy = RPMOSTREE_CONTEXT_DNF_CACHE_DEFAULT;
self->enable_rofiles = TRUE;
}
static void
@ -555,11 +556,19 @@ void
rpmostree_context_set_devino_cache (RpmOstreeContext *self,
OstreeRepoDevInoCache *devino_cache)
{
g_assert (self->enable_rofiles);
if (self->devino_cache)
ostree_repo_devino_cache_unref (self->devino_cache);
self->devino_cache = devino_cache ? ostree_repo_devino_cache_ref (devino_cache) : NULL;
}
void
rpmostree_context_disable_rofiles (RpmOstreeContext *self)
{
g_assert (!self->devino_cache);
self->enable_rofiles = FALSE;
}
DnfContext *
rpmostree_context_get_dnf (RpmOstreeContext *self)
{
@ -2577,6 +2586,7 @@ checkout_package (OstreeRepo *repo,
const char *pkg_commit,
GHashTable *files_skip,
OstreeRepoCheckoutOverwriteMode ovwmode,
gboolean force_copy_zerosized,
GCancellable *cancellable,
GError **error)
{
@ -2591,6 +2601,8 @@ checkout_package (OstreeRepo *repo,
/* Always want hardlinks */
opts.no_copy_fallback = TRUE;
/* Used in the no-rofiles-fuse path */
opts.force_copy_zerosized = force_copy_zerosized;
if (files_skip && g_hash_table_size (files_skip) > 0)
{
@ -2632,6 +2644,7 @@ checkout_package_into_root (RpmOstreeContext *self,
if (!checkout_package (pkgcache_repo, dfd, path,
devino_cache, pkg_commit, files_skip, ovwmode,
!self->enable_rofiles,
cancellable, error))
return glnx_prefix_error (error, "Checkout %s", dnf_package_get_nevra (pkg));
@ -2887,7 +2900,7 @@ relabel_in_thread_impl (RpmOstreeContext *self,
g_autoptr(OstreeRepoDevInoCache) cache = ostree_repo_devino_cache_new ();
if (!checkout_package (repo, tmpdir_dfd, pkg_dirname, cache,
commit_csum, NULL, OSTREE_REPO_CHECKOUT_OVERWRITE_NONE,
commit_csum, NULL, OSTREE_REPO_CHECKOUT_OVERWRITE_NONE, FALSE,
cancellable, error))
return FALSE;
@ -3256,7 +3269,7 @@ run_script_sync (RpmOstreeContext *self,
return FALSE;
if (!rpmostree_script_run_sync (pkg, hdr, kind, rootfs_dfd, var_lib_rpm_statedir,
out_n_run, cancellable, error))
self->enable_rofiles, out_n_run, cancellable, error))
return FALSE;
return TRUE;
@ -3530,7 +3543,8 @@ run_all_transfiletriggers (RpmOstreeContext *self,
Header hdr;
while ((hdr = rpmdbNextIterator (mi)) != NULL)
{
if (!rpmostree_transfiletriggers_run_sync (hdr, rootfs_dfd, out_n_run,
if (!rpmostree_transfiletriggers_run_sync (hdr, rootfs_dfd, self->enable_rofiles,
out_n_run,
cancellable, error))
return FALSE;
}
@ -3549,8 +3563,8 @@ run_all_transfiletriggers (RpmOstreeContext *self,
if (!get_package_metainfo (self, path, &hdr, NULL, error))
return FALSE;
if (!rpmostree_transfiletriggers_run_sync (hdr, rootfs_dfd, out_n_run,
cancellable, error))
if (!rpmostree_transfiletriggers_run_sync (hdr, rootfs_dfd, self->enable_rofiles,
out_n_run, cancellable, error))
return FALSE;
}
return TRUE;

View File

@ -121,6 +121,7 @@ void rpmostree_context_set_repos (RpmOstreeContext *self,
OstreeRepo *pkgcache_repo);
void rpmostree_context_set_devino_cache (RpmOstreeContext *self,
OstreeRepoDevInoCache *devino_cache);
void rpmostree_context_disable_rofiles (RpmOstreeContext *self);
void rpmostree_context_set_sepolicy (RpmOstreeContext *self,
OstreeSePolicy *sepolicy);

View File

@ -296,6 +296,7 @@ dump_buffered_output_noerr (const char *prefix,
static gboolean
run_script_in_bwrap_container (int rootfs_fd,
GLnxTmpDir *var_lib_rpm_statedir,
gboolean enable_fuse,
const char *name,
const char *scriptdesc,
const char *interp,
@ -354,8 +355,10 @@ run_script_in_bwrap_container (int rootfs_fd,
* See above for why we special case glibc.
*/
const gboolean is_glibc_locales = strcmp (pkg_script, "glibc-all-langpacks.posttrans") == 0;
RpmOstreeBwrapMutability mutability =
(is_glibc_locales || !enable_fuse) ? RPMOSTREE_BWRAP_MUTATE_FREELY : RPMOSTREE_BWRAP_MUTATE_ROFILES;
bwrap = rpmostree_bwrap_new (rootfs_fd,
is_glibc_locales ? RPMOSTREE_BWRAP_MUTATE_FREELY : RPMOSTREE_BWRAP_MUTATE_ROFILES,
mutability,
error);
if (!bwrap)
goto out;
@ -473,6 +476,7 @@ impl_run_rpm_script (const KnownRpmScriptKind *rpmscript,
Header hdr,
int rootfs_fd,
GLnxTmpDir *var_lib_rpm_statedir,
gboolean enable_fuse,
GCancellable *cancellable,
GError **error)
{
@ -541,7 +545,8 @@ impl_run_rpm_script (const KnownRpmScriptKind *rpmscript,
}
guint64 start_time_ms = g_get_monotonic_time () / 1000;
if (!run_script_in_bwrap_container (rootfs_fd, var_lib_rpm_statedir, dnf_package_get_name (pkg),
if (!run_script_in_bwrap_container (rootfs_fd, var_lib_rpm_statedir, enable_fuse,
dnf_package_get_name (pkg),
rpmscript->desc, interp, script, script_arg,
-1, cancellable, error))
return glnx_prefix_error (error, "Running %s for %s", rpmscript->desc, dnf_package_get_name (pkg));
@ -567,6 +572,7 @@ run_script (const KnownRpmScriptKind *rpmscript,
Header hdr,
int rootfs_fd,
GLnxTmpDir *var_lib_rpm_statedir,
gboolean enable_fuse,
gboolean *out_did_run,
GCancellable *cancellable,
GError **error)
@ -595,7 +601,7 @@ run_script (const KnownRpmScriptKind *rpmscript,
*out_did_run = TRUE;
return impl_run_rpm_script (rpmscript, pkg, hdr, rootfs_fd, var_lib_rpm_statedir,
cancellable, error);
enable_fuse, cancellable, error);
}
#ifdef BUILDOPT_HAVE_RPM_FILETRIGGERS
@ -715,6 +721,7 @@ rpmostree_script_run_sync (DnfPackage *pkg,
RpmOstreeScriptKind kind,
int rootfs_fd,
GLnxTmpDir *var_lib_rpm_statedir,
gboolean enable_fuse,
guint *out_n_run,
GCancellable *cancellable,
GError **error)
@ -737,7 +744,7 @@ rpmostree_script_run_sync (DnfPackage *pkg,
gboolean did_run = FALSE;
if (!run_script (scriptkind, pkg, hdr, rootfs_fd,
var_lib_rpm_statedir,
var_lib_rpm_statedir, enable_fuse,
&did_run, cancellable, error))
return FALSE;
@ -752,6 +759,7 @@ rpmostree_script_run_sync (DnfPackage *pkg,
gboolean
rpmostree_transfiletriggers_run_sync (Header hdr,
int rootfs_fd,
gboolean enable_fuse,
guint *out_n_run,
GCancellable *cancellable,
GError **error)
@ -906,7 +914,7 @@ rpmostree_transfiletriggers_run_sync (Header hdr,
/* Run it, and log the result */
guint64 start_time_ms = g_get_monotonic_time () / 1000;
if (!run_script_in_bwrap_container (rootfs_fd, NULL, pkg_name,
if (!run_script_in_bwrap_container (rootfs_fd, NULL, enable_fuse, pkg_name,
"%transfiletriggerin", interp, script, NULL,
fileno (tmpf_file), cancellable, error))
return FALSE;

View File

@ -63,6 +63,7 @@ rpmostree_script_run_sync (DnfPackage *pkg,
RpmOstreeScriptKind kind,
int rootfs_fd,
GLnxTmpDir *var_lib_rpm_statedir,
gboolean enable_rofiles,
guint *out_n_run,
GCancellable *cancellable,
GError **error);
@ -70,6 +71,7 @@ rpmostree_script_run_sync (DnfPackage *pkg,
gboolean
rpmostree_transfiletriggers_run_sync (Header hdr,
int rootfs_fd,
gboolean enable_rofiles,
guint *out_n_run,
GCancellable *cancellable,
GError **error);

View File

@ -549,3 +549,16 @@ EOF
post "semodule -n -i ${install_dir}/${name}.pp" \
files "${install_dir}/${name}.pp"
}
files_are_hardlinked() {
inode1=$(stat -c %i $1)
inode2=$(stat -c %i $2)
test -n "${inode1}" && test -n "${inode2}"
[ "${inode1}" == "${inode2}" ]
}
assert_files_hardlinked() {
if ! files_are_hardlinked "$1" "$2"; then
fatal "Files '$1' and '$2' are not hardlinked"
fi
}

View File

@ -35,6 +35,21 @@ if ostree --repo=${repobuild} cat ${treeref} /usr/lib/tmpfiles.d/pkg-rpm.conf >
fi
echo "ok autovar"
# And verify we're not hardlinking zero-sized files since this path isn't using
# rofiles-fuse
co=${repobuild}/tmp/usr-etc
ostree --repo=${repobuild} checkout -UHz --subpath=/usr/etc ${treeref} ${co}
# Verify the files exist and are zero-sized
for f in ${co}/sub{u,g}id; do
test -f "$f"
test '!' -s "$f"
done
if files_are_hardlinked ${co}/sub{u,g}id; then
fatal "Hardlinked zero-sized files without cachedir"
fi
rm ${co} -rf
echo "ok no cachedir zero-sized hardlinks"
# And redo it to trigger relabeling
origrev=$(ostree --repo=${repobuild} rev-parse ${treeref})
runcompose --force-nocache --ex-unified-core