Skip to content

Commit

Permalink
core,scripts: When no cachedir+unified-core, disable rofiles-fuse
Browse files Browse the repository at this point in the history
This is prep for running inside (unprivileged) Kube containers
as they exist today: #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: ostreedev/ostree#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.
  • Loading branch information
cgwalters committed Oct 12, 2018
1 parent 9461476 commit 9ce765c
Show file tree
Hide file tree
Showing 8 changed files with 97 additions and 14 deletions.
37 changes: 33 additions & 4 deletions src/app/rpmostree-compose-builtin-tree.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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");

Expand Down Expand Up @@ -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;

Expand Down
1 change: 1 addition & 0 deletions src/libpriv/rpmostree-core-private.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
24 changes: 19 additions & 5 deletions src/libpriv/rpmostree-core.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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)
{
Expand Down Expand Up @@ -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)
{
Expand All @@ -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)
{
Expand Down Expand Up @@ -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));

Expand Down Expand Up @@ -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;

Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -3521,7 +3534,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;
}
Expand All @@ -3540,8 +3554,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;
Expand Down
1 change: 1 addition & 0 deletions src/libpriv/rpmostree-core.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down
18 changes: 13 additions & 5 deletions src/libpriv/rpmostree-scripts.c
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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)
{
Expand Down Expand Up @@ -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));
Expand All @@ -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)
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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)
Expand All @@ -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;

Expand All @@ -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)
Expand Down Expand Up @@ -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;
Expand Down
2 changes: 2 additions & 0 deletions src/libpriv/rpmostree-scripts.h
Original file line number Diff line number Diff line change
Expand Up @@ -63,13 +63,15 @@ 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);

gboolean
rpmostree_transfiletriggers_run_sync (Header hdr,
int rootfs_fd,
gboolean enable_rofiles,
guint *out_n_run,
GCancellable *cancellable,
GError **error);
Expand Down
13 changes: 13 additions & 0 deletions tests/common/libtest.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
15 changes: 15 additions & 0 deletions tests/compose-tests/test-basic-unified.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down

0 comments on commit 9ce765c

Please sign in to comment.