diff --git a/man/ostree-admin-init-fs.xml b/man/ostree-admin-init-fs.xml
index ead3d325d4..e06d2222f2 100644
--- a/man/ostree-admin-init-fs.xml
+++ b/man/ostree-admin-init-fs.xml
@@ -87,7 +87,15 @@ License along with this library. If not, see .
should only be mounted in the final deployment root. The main exception
is /boot, which may need to be mounted in some setups
before the target root.
-
+
+
+ Epoch 2 is the same as 1, except that the toplevel ostree
+ directory is mode 0700, denying access from unprivileged code. This
+ is a new recommended best practice as it avoids access to old configuration
+ files in /etc in previous deployments, as well as
+ potentially old setuid binaries in /ostree/repo.
+
+
diff --git a/src/ostree/ot-admin-builtin-init-fs.c b/src/ostree/ot-admin-builtin-init-fs.c
index 7662f47954..5815a8373b 100644
--- a/src/ostree/ot-admin-builtin-init-fs.c
+++ b/src/ostree/ot-admin-builtin-init-fs.c
@@ -99,6 +99,14 @@ ot_admin_builtin_init_fs (int argc, char **argv, OstreeCommandInvocation *invoca
return FALSE;
}
}
+ else if (opt_epoch == 2)
+ {
+ /* In epoch 2, ostree is 0700 - no access from unprivileged code. See
+ * https://github.com/ostreedev/ostree/issues/3211
+ */
+ if (!glnx_shutil_mkdir_p_at (root_dfd, "ostree", 0700, cancellable, error))
+ return FALSE;
+ }
g_autoptr (GFile) dir = g_file_new_for_path (sysroot_path);
g_autoptr (OstreeSysroot) sysroot = ostree_sysroot_new (dir);
diff --git a/tests/admin-test.sh b/tests/admin-test.sh
index 2cd8473270..e655c710df 100644
--- a/tests/admin-test.sh
+++ b/tests/admin-test.sh
@@ -29,6 +29,10 @@ for flag in --modern --epoch=1; do
assert_not_has_dir sysrootmin/home
rm sysrootmin -rf
done
+mkdir sysrootmin
+${CMD_PREFIX} ostree admin init-fs --epoch=2 sysrootmin
+assert_streq "$(stat -c '%f' sysrootmin/ostree)" 41c0
+assert_not_has_dir sysrootmin/home
echo "ok init-fs"
function validate_bootloader() {