2022-12-29 16:38:58 +00:00
|
|
|
# borrows from:
|
|
|
|
# https://xeiaso.net/blog/paranoid-nixos-2021-07-18
|
|
|
|
# https://elis.nu/blog/2020/05/nixos-tmpfs-as-root/
|
|
|
|
# https://github.com/nix-community/impermanence
|
2023-01-03 14:55:27 +00:00
|
|
|
{ config, lib, pkgs, utils, sane-lib, ... }:
|
2022-12-29 16:38:58 +00:00
|
|
|
|
|
|
|
with lib;
|
|
|
|
let
|
2023-01-03 14:55:27 +00:00
|
|
|
path = sane-lib.path;
|
2023-01-04 00:59:52 +00:00
|
|
|
sane-types = sane-lib.types;
|
2023-01-06 10:04:51 +00:00
|
|
|
cfg = config.sane.persist;
|
2023-01-03 03:04:17 +00:00
|
|
|
|
2023-01-03 07:04:49 +00:00
|
|
|
storeType = types.submodule {
|
|
|
|
options = {
|
2023-01-04 01:54:13 +00:00
|
|
|
storeDescription = mkOption {
|
|
|
|
type = types.nullOr types.str;
|
|
|
|
default = null;
|
|
|
|
description = ''
|
|
|
|
an optional description of the store, which is rendered like
|
|
|
|
{store.name}: {store.storeDescription}
|
|
|
|
for example, a store named "private" could have description "ecnrypted to the user's password and decrypted on login".
|
|
|
|
'';
|
|
|
|
};
|
2023-01-04 12:19:32 +00:00
|
|
|
origin = mkOption {
|
2023-01-03 07:04:49 +00:00
|
|
|
type = types.str;
|
|
|
|
};
|
|
|
|
prefix = mkOption {
|
|
|
|
type = types.str;
|
|
|
|
default = "/";
|
2023-01-03 07:45:19 +00:00
|
|
|
description = ''
|
|
|
|
optional prefix to strip from children when stored here.
|
|
|
|
for example, prefix="/var/private" and mountpoint="/mnt/crypt/private"
|
|
|
|
would cause /var/private/www/root to be stored at /mnt/crypt/private/www/root instead of
|
|
|
|
/mnt/crypt/private/var/private/www/root.
|
|
|
|
'';
|
2023-01-03 07:04:49 +00:00
|
|
|
};
|
2023-01-04 11:22:26 +00:00
|
|
|
defaultOrdering.wantedBeforeBy = mkOption {
|
2023-01-03 07:04:49 +00:00
|
|
|
type = types.listOf types.str;
|
2023-01-04 11:22:26 +00:00
|
|
|
default = [ "local-fs.target" ];
|
2023-01-03 07:45:19 +00:00
|
|
|
description = ''
|
2023-01-04 11:22:26 +00:00
|
|
|
list of units or targets which would prefer that everything in this store
|
|
|
|
be initialized before they run, but failing to do so should not error the items in this list.
|
|
|
|
'';
|
|
|
|
};
|
|
|
|
defaultOrdering.wantedBy = mkOption {
|
|
|
|
type = types.listOf types.str;
|
|
|
|
default = [ ];
|
|
|
|
description = ''
|
|
|
|
list of units or targets which, upon activation, should activate all units in this store.
|
2023-01-03 07:45:19 +00:00
|
|
|
'';
|
2023-01-03 07:04:49 +00:00
|
|
|
};
|
|
|
|
};
|
2023-01-03 03:04:17 +00:00
|
|
|
};
|
2022-12-29 16:38:58 +00:00
|
|
|
|
2022-12-31 09:09:51 +00:00
|
|
|
# options for a single mountpoint / persistence
|
2023-01-03 07:04:49 +00:00
|
|
|
dirEntryOptions = {
|
2022-12-29 16:38:58 +00:00
|
|
|
options = {
|
2022-12-31 09:09:51 +00:00
|
|
|
directory = mkOption {
|
|
|
|
type = types.str;
|
|
|
|
};
|
2023-01-04 00:59:52 +00:00
|
|
|
inherit (sane-types.aclOverrideMod.options) user group mode;
|
2022-12-29 16:38:58 +00:00
|
|
|
};
|
|
|
|
};
|
2023-01-03 07:04:49 +00:00
|
|
|
contextualizedDir = types.submodule dirEntryOptions;
|
2022-12-31 09:09:51 +00:00
|
|
|
# allow "bar/baz" as shorthand for { directory = "bar/baz"; }
|
2023-01-03 07:04:49 +00:00
|
|
|
contextualizedDirOrShorthand = types.coercedTo
|
|
|
|
types.str
|
|
|
|
(d: { directory = d; })
|
|
|
|
contextualizedDir;
|
2022-12-29 16:38:58 +00:00
|
|
|
|
2023-01-03 07:04:49 +00:00
|
|
|
# entry whose `directory` is always an absolute fs path
|
|
|
|
# and has an associated `store`
|
|
|
|
contextFreeDir = types.submodule [
|
|
|
|
dirEntryOptions
|
|
|
|
{
|
|
|
|
options = {
|
|
|
|
store = mkOption {
|
|
|
|
type = storeType;
|
|
|
|
};
|
|
|
|
};
|
|
|
|
}
|
|
|
|
];
|
2022-12-29 16:38:58 +00:00
|
|
|
|
2023-01-06 11:52:28 +00:00
|
|
|
contextFreeDirSpec = types.submodule {
|
|
|
|
options = {
|
|
|
|
inherit (sane-types.aclOverrideMod.options) user group mode;
|
|
|
|
store = mkOption {
|
|
|
|
type = storeType;
|
|
|
|
};
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
2023-01-06 10:35:32 +00:00
|
|
|
|
|
|
|
# attrset from { "${storeName}" = [ dirEntry ] }
|
|
|
|
# the user can specify something like:
|
2023-01-06 10:31:01 +00:00
|
|
|
# <option>.private.".cache/vim" = { mode = "0700"; };
|
|
|
|
# to place ".cache/vim" into the private store and create with the appropriate mode
|
2023-01-06 10:35:32 +00:00
|
|
|
dirsSubModule = types.attrsOf (types.listOf contextualizedDirOrShorthand);
|
2022-12-29 16:38:58 +00:00
|
|
|
in
|
|
|
|
{
|
|
|
|
options = {
|
2023-01-06 10:04:51 +00:00
|
|
|
sane.persist.enable = mkOption {
|
2022-12-29 16:38:58 +00:00
|
|
|
default = false;
|
|
|
|
type = types.bool;
|
|
|
|
};
|
2023-01-06 10:04:51 +00:00
|
|
|
sane.persist.root-on-tmpfs = mkOption {
|
2022-12-29 16:38:58 +00:00
|
|
|
default = false;
|
|
|
|
type = types.bool;
|
2023-01-03 07:04:49 +00:00
|
|
|
description = "define / fs root to be a tmpfs. make sure to mount some other device to /nix";
|
2022-12-31 09:09:51 +00:00
|
|
|
};
|
2023-01-06 11:29:13 +00:00
|
|
|
sane.persist.home = mkOption {
|
|
|
|
description = "directories to persist to disk, relative to a user's home ~";
|
|
|
|
default = {};
|
|
|
|
type = dirsSubModule;
|
|
|
|
};
|
|
|
|
sane.persist.sys = mkOption {
|
|
|
|
description = "directories to persist to disk, relative to the fs root /";
|
2023-01-03 07:04:49 +00:00
|
|
|
default = {};
|
2023-01-06 11:29:13 +00:00
|
|
|
type = dirsSubModule;
|
2023-01-03 07:04:49 +00:00
|
|
|
};
|
2023-01-06 11:24:11 +00:00
|
|
|
sane.persist.all = mkOption {
|
2023-01-06 11:52:28 +00:00
|
|
|
type = types.attrsOf contextFreeDirSpec;
|
2023-01-06 11:24:11 +00:00
|
|
|
description = "all directories known to the config. auto-computed: users should not set this directly.";
|
|
|
|
};
|
2023-01-06 10:04:51 +00:00
|
|
|
sane.persist.stores = mkOption {
|
2023-01-03 07:04:49 +00:00
|
|
|
type = types.attrsOf storeType;
|
|
|
|
default = {};
|
2023-01-03 07:45:19 +00:00
|
|
|
description = ''
|
|
|
|
map from human-friendly name to a fs sub-tree from which files are linked into the logical fs.
|
|
|
|
'';
|
2022-12-31 09:09:51 +00:00
|
|
|
};
|
2022-12-29 16:38:58 +00:00
|
|
|
};
|
|
|
|
|
2022-12-30 04:35:34 +00:00
|
|
|
imports = [
|
2023-01-06 11:24:11 +00:00
|
|
|
./computed.nix
|
2022-12-30 04:35:34 +00:00
|
|
|
./root-on-tmpfs.nix
|
2023-01-03 07:04:49 +00:00
|
|
|
./stores
|
2022-12-30 04:35:34 +00:00
|
|
|
];
|
2022-12-29 16:38:58 +00:00
|
|
|
|
2023-01-03 08:25:43 +00:00
|
|
|
config = let
|
2023-01-06 11:52:28 +00:00
|
|
|
cfgFor = fspath: opt:
|
2023-01-03 08:25:43 +00:00
|
|
|
let
|
|
|
|
store = opt.store;
|
2023-01-06 09:56:06 +00:00
|
|
|
fsPathToStoreRelPath = fspath: path.from store.prefix fspath;
|
|
|
|
fsPathToBackingPath = fspath: path.concat [ store.origin (fsPathToStoreRelPath fspath) ];
|
2022-12-29 16:38:58 +00:00
|
|
|
|
2023-01-03 08:25:43 +00:00
|
|
|
# pass through the perm/mode overrides
|
2023-01-04 01:19:22 +00:00
|
|
|
dir-acl = sane-lib.filterNonNull {
|
|
|
|
inherit (opt) user group mode;
|
2022-12-29 16:38:58 +00:00
|
|
|
};
|
2023-01-06 09:56:06 +00:00
|
|
|
in [
|
|
|
|
{
|
|
|
|
# create destination dir, with correct perms
|
2023-01-06 11:52:28 +00:00
|
|
|
sane.fs."${fspath}" = {
|
2023-01-06 09:56:06 +00:00
|
|
|
# inherit perms & make sure we don't mount until after the mount point is setup correctly.
|
|
|
|
dir.acl = dir-acl;
|
2023-01-06 11:52:28 +00:00
|
|
|
mount.bind = fsPathToBackingPath fspath;
|
2023-01-06 09:56:06 +00:00
|
|
|
inherit (store.defaultOrdering) wantedBy wantedBeforeBy;
|
|
|
|
};
|
|
|
|
|
|
|
|
# create the backing path as a dir
|
2023-01-06 11:52:28 +00:00
|
|
|
sane.fs."${fsPathToBackingPath fspath}".dir = {};
|
2023-01-06 09:56:06 +00:00
|
|
|
}
|
|
|
|
{
|
|
|
|
# default each item along the backing path to have the same acl as the location it would be mounted.
|
2023-01-06 11:52:28 +00:00
|
|
|
sane.fs = sane-lib.mapToAttrs (fsSubpath: {
|
|
|
|
name = fsPathToBackingPath fsSubpath;
|
|
|
|
value.generated.acl = config.sane.fs."${fsSubpath}".generated.acl;
|
|
|
|
}) (path.walk store.prefix fspath);
|
2023-01-06 09:56:06 +00:00
|
|
|
}
|
|
|
|
];
|
2023-01-06 11:52:28 +00:00
|
|
|
configsPerPath = lib.mapAttrsToList cfgFor cfg.all;
|
|
|
|
allConfigs = builtins.concatLists configsPerPath;
|
2023-01-03 08:25:43 +00:00
|
|
|
in mkIf cfg.enable {
|
2023-01-06 11:52:28 +00:00
|
|
|
sane.fs = lib.mkMerge (map (c: c.sane.fs) allConfigs);
|
2023-01-03 08:25:43 +00:00
|
|
|
};
|
2022-12-29 16:38:58 +00:00
|
|
|
}
|
|
|
|
|