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:
1 parent
0b1d301d81
commit
09238da065
20 files changed
+691
-47
No files matched your search
@@ -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;
|
||||
}
|
||||
|
||||
@@ -22,3 +22,5 @@
|
||||
#pragma once
|
||||
|
||||
int perrorv (const char *format, ...) __attribute__ ((format (printf, 1, 2)));
|
||||
|
||||
int path_is_on_readonly_fs (char *path);
|
||||
@@ -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 ();
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in new issue
Block a user