admin: Add an unlock command, and libostree API

I'm trying to improve the developer experience on OSTree-managed
systems, and I had an epiphany the other day - there's no reason we
have to be absolutely against mutating the current rootfs live.  The
key should be making it easy to rollback/reset to a known good state.

I see this command as useful for two related but distinct workflows:

 - `ostree admin unlock` will assume you're doing "development".  The
   semantics hare are that we mount an overlayfs on `/usr`, but the
   overlay data is in `/var/tmp`, and is thus discarded on reboot.
 - `ostree admin unlock --hotfix` first clones your current deployment,
   then creates an overlayfs over `/usr` persistent
   to this deployment.  Persistent in that now the initramfs switchroot
   tool knows how to mount it as well.  In this model, if you want
   to discard the hotfix, at the moment you roll back/reboot into
   the clone.

Note originally, I tried using `rofiles-fuse` over `/usr` for this,
but then everything immediately explodes because the default (at least
CentOS 7) SELinux policy denies tons of things (including `sshd_t`
access to `fusefs_t`).  Sigh.

So the switch to `overlayfs` came after experimentation.  It still
seems to have some issues...specifically `unix_chkpwd` is broken,
possibly because it's setuid?  Basically I can't ssh in anymore.

But I *can* `rpm -Uvh strace.rpm` which is handy.

NOTE: I haven't tested the hotfix path fully yet, specifically
the initramfs bits.
This commit is contained in:
Colin Walters committed 2016-03-23 11:09:09 -04:00
1 parent 0b1d301d81
commit 09238da065
20 files changed
+691 -47

No files matched your search

+19
View File
@@ -26,6 +26,10 @@
#include <errno.h>
#include <string.h>
#include <stdio.h>
#include <sys/mount.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/statvfs.h>
#include "ostree-mount-util.h"
@@ -48,3 +52,18 @@ perrorv (const char *format, ...)
return 0;
}
int
path_is_on_readonly_fs (char *path)
{
struct statvfs stvfsbuf;
if (statvfs (path, &stvfsbuf) == -1)
{
perrorv ("statvfs(%s): ", path);
exit (EXIT_FAILURE);
}
return (stvfsbuf.f_flag & ST_RDONLY) != 0;
}
+2
View File
@@ -22,3 +22,5 @@
#pragma once
int perrorv (const char *format, ...) __attribute__ ((format (printf, 1, 2)));
int path_is_on_readonly_fs (char *path);
+58 -10
View File
@@ -111,7 +111,6 @@ touch_run_ostree (void)
int
main(int argc, char *argv[])
{
const char *readonly_bind_mounts[] = { "/usr", NULL };
const char *root_mountpoint = NULL;
char *ostree_target = NULL;
char *deploy_path = NULL;
@@ -119,7 +118,7 @@ main(int argc, char *argv[])
char destpath[PATH_MAX];
char newroot[PATH_MAX];
struct stat stbuf;
int i;
int orig_cwd_dfd;
if (argc < 2)
{
@@ -211,21 +210,70 @@ main(int argc, char *argv[])
}
}
/* Set up any read-only bind mounts (notably /usr) */
for (i = 0; readonly_bind_mounts[i] != NULL; i++)
/* Here we do a dance to chdir to the newroot so that we can have
* the potential overlayfs mount points not look ugly. However...I
* think we could do this a lot earlier and make all of the mounts
* here just be relative.
*/
orig_cwd_dfd = openat (AT_FDCWD, ".", O_RDONLY | O_NONBLOCK | O_DIRECTORY | O_CLOEXEC | O_NOCTTY);
if (orig_cwd_dfd < 0)
{
snprintf (destpath, sizeof(destpath), "%s%s", newroot, readonly_bind_mounts[i]);
if (mount (destpath, destpath, NULL, MS_BIND, NULL) < 0)
perrorv ("failed to open .");
exit (EXIT_FAILURE);
}
if (chdir (newroot) < 0)
{
perrorv ("failed to chdir to newroot");
exit (EXIT_FAILURE);
}
/* Do we have a persistent overlayfs for /usr? If so, mount it now. */
if (lstat (".usr-ovl-work", &stbuf) == 0)
{
const char usr_ovl_options[] = "lowerdir=usr,upperdir=.usr-ovl-upper,workdir=.usr-ovl-work";
/* Except overlayfs barfs if we try to mount it on a read-only
* filesystem. For this use case I think admins are going to be
* okay if we remount the rootfs here, rather than waiting until
* later boot and `systemd-remount-fs.service`.
*/
if (path_is_on_readonly_fs ("."))
{
perrorv ("failed to bind mount (class:readonly) %s", destpath);
exit (EXIT_FAILURE);
if (mount (".", ".", NULL, MS_REMOUNT | MS_SILENT, NULL) < 0)
{
perrorv ("Failed to remount rootfs writable (for overlayfs)");
exit (EXIT_FAILURE);
}
}
if (mount (destpath, destpath, NULL, MS_BIND | MS_REMOUNT | MS_RDONLY, NULL) < 0)
if (mount ("overlay", "usr", "overlay", 0, usr_ovl_options) < 0)
{
perrorv ("failed to bind mount (class:readonly) %s", destpath);
perrorv ("failed to mount /usr overlayfs");
exit (EXIT_FAILURE);
}
}
else
{
/* Otherwise, a read-only bind mount for /usr */
if (mount ("usr", "usr", NULL, MS_BIND, NULL) < 0)
{
perrorv ("failed to bind mount (class:readonly) /usr");
exit (EXIT_FAILURE);
}
if (mount ("usr", "usr", NULL, MS_BIND | MS_REMOUNT | MS_RDONLY, NULL) < 0)
{
perrorv ("failed to bind mount (class:readonly) /usr");
exit (EXIT_FAILURE);
}
}
if (fchdir (orig_cwd_dfd) < 0)
{
perrorv ("failed to chdir to orig root");
exit (EXIT_FAILURE);
}
(void) close (orig_cwd_dfd);
touch_run_ostree ();
-14
View File
@@ -37,20 +37,6 @@
#include "ostree-mount-util.h"
static int
path_is_on_readonly_fs (char *path)
{
struct statvfs stvfsbuf;
if (statvfs (path, &stvfsbuf) == -1)
{
perrorv ("statvfs(%s): ", path);
exit (EXIT_FAILURE);
}
return (stvfsbuf.f_flag & ST_RDONLY) != 0;
}
/* Having a writeable /var is necessary for full system functioning.
* If /var isn't writeable, we mount tmpfs over it. While this is
* somewhat outside of ostree's scope, having all /var twiddling