impermanence: large refactor, and experimental bind mounting of things from ~/private
This commit is contained in:
parent
bace7403e7
commit
327e6b536f
|
@ -18,7 +18,7 @@
|
|||
sane.packages.enableConsolePkgs = true;
|
||||
sane.packages.enableSystemPkgs = true;
|
||||
|
||||
sane.impermanence.dirs = [
|
||||
sane.impermanence.dirs.sys.plaintext = [
|
||||
"/var/log"
|
||||
"/var/backup" # for e.g. postgres dumps
|
||||
# TODO: move elsewhere
|
||||
|
|
|
@ -71,17 +71,19 @@ in
|
|||
|
||||
security.pam.mount.enable = true;
|
||||
|
||||
sane.impermanence.home-dirs = [
|
||||
# cache is probably too big to fit on the tmpfs
|
||||
# { directory = ".cache"; store = "crypt-clearedonboot"; }
|
||||
{ directory = ".cache/mozilla"; store = "crypt-clearedonboot"; }
|
||||
sane.impermanence.dirs.home.plaintext = [
|
||||
".cargo"
|
||||
".rustup"
|
||||
# TODO: move this to ~/private!
|
||||
".local/share/keyrings"
|
||||
];
|
||||
sane.impermanence.dirs.home.cryptClearOnBoot = [
|
||||
# cache is probably too big to fit on the tmpfs
|
||||
# ".cache"
|
||||
".cache/mozilla"
|
||||
];
|
||||
|
||||
sane.impermanence.dirs = mkIf cfg.guest.enable [
|
||||
sane.impermanence.dirs.sys.plaintext = mkIf cfg.guest.enable [
|
||||
{ user = "guest"; group = "users"; directory = "/home/guest"; }
|
||||
];
|
||||
users.users.guest = mkIf cfg.guest.enable {
|
||||
|
|
|
@ -52,7 +52,7 @@
|
|||
remotePlay.openFirewall = true; # Open ports in the firewall for Steam Remote Play
|
||||
dedicatedServer.openFirewall = true; # Open ports in the firewall for Source Dedicated Server
|
||||
};
|
||||
sane.impermanence.home-dirs = [
|
||||
sane.impermanence.dirs.home.plaintext = [
|
||||
".steam"
|
||||
".local/share/Steam"
|
||||
];
|
||||
|
|
|
@ -36,11 +36,12 @@
|
|||
];
|
||||
};
|
||||
|
||||
sane.impermanence.dirs = [
|
||||
sane.impermanence.dirs.sys.plaintext = [
|
||||
# TODO: this is overly broad; only need media and share directories to be persisted
|
||||
{ user = "colin"; group = "users"; directory = "/var/lib/uninsane"; }
|
||||
];
|
||||
# direct these media directories to external storage
|
||||
# TODO: convert to sane.fs
|
||||
environment.persistence."/nix/persist/ext/persist" = {
|
||||
directories = [
|
||||
({
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
# XXX: avatar support works in MUCs but not DMs
|
||||
# lib.mkIf false
|
||||
{
|
||||
sane.impermanence.dirs = [
|
||||
sane.impermanence.dirs.sys.plaintext = [
|
||||
{ user = "ejabberd"; group = "ejabberd"; directory = "/var/lib/ejabberd"; }
|
||||
];
|
||||
networking.firewall.allowedTCPPorts = [
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
owner = config.users.users.freshrss.name;
|
||||
mode = "400";
|
||||
};
|
||||
sane.impermanence.dirs = [
|
||||
sane.impermanence.dirs.sys.plaintext = [
|
||||
{ user = "freshrss"; group = "freshrss"; directory = "/var/lib/freshrss"; }
|
||||
];
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{ config, pkgs, lib, ... }:
|
||||
|
||||
{
|
||||
sane.impermanence.dirs = [
|
||||
sane.impermanence.dirs.sys.plaintext = [
|
||||
# TODO: mode? could be more granular
|
||||
{ user = "git"; group = "gitea"; directory = "/var/lib/gitea"; }
|
||||
];
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
|
||||
lib.mkIf false # i don't actively use ipfs anymore
|
||||
{
|
||||
sane.impermanence.dirs = [
|
||||
sane.impermanence.dirs.sys.plaintext = [
|
||||
# TODO: mode? could be more granular
|
||||
{ user = "261"; group = "261"; directory = "/var/lib/ipfs"; }
|
||||
];
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{ ... }:
|
||||
|
||||
{
|
||||
sane.impermanence.dirs = [
|
||||
sane.impermanence.dirs.sys.plaintext = [
|
||||
# TODO: mode? we only need this to save Indexer creds ==> migrate to config?
|
||||
{ user = "root"; group = "root"; directory = "/var/lib/jackett"; }
|
||||
];
|
||||
|
|
|
@ -7,7 +7,7 @@ lib.mkIf false
|
|||
networking.firewall.allowedUDPPorts = [
|
||||
1900 7359 # DLNA: https://jellyfin.org/docs/general/networking/index.html
|
||||
];
|
||||
sane.impermanence.dirs = [
|
||||
sane.impermanence.dirs.sys.plaintext = [
|
||||
# TODO: mode? could be more granular
|
||||
{ user = "jellyfin"; group = "jellyfin"; directory = "/var/lib/jellyfin"; }
|
||||
];
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
# ./irc.nix
|
||||
];
|
||||
|
||||
sane.impermanence.dirs = [
|
||||
sane.impermanence.dirs.sys.plaintext = [
|
||||
{ user = "matrix-synapse"; group = "matrix-synapse"; directory = "/var/lib/matrix-synapse"; }
|
||||
];
|
||||
services.matrix-synapse.enable = true;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{ lib, ... }:
|
||||
{
|
||||
sane.impermanence.dirs = [
|
||||
sane.impermanence.dirs.sys.plaintext = [
|
||||
{ user = "matrix-synapse"; group = "matrix-synapse"; directory = "/var/lib/mx-puppet-discord"; }
|
||||
];
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{ config, lib, ... }:
|
||||
|
||||
{
|
||||
sane.impermanence.dirs = [
|
||||
sane.impermanence.dirs.sys.plaintext = [
|
||||
# TODO: mode?
|
||||
# user and group are both "matrix-appservice-irc"
|
||||
{ user = "993"; group = "992"; directory = "/var/lib/matrix-appservice-irc"; }
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{ ... }:
|
||||
|
||||
{
|
||||
sane.impermanence.dirs = [
|
||||
sane.impermanence.dirs.sys.plaintext = [
|
||||
{ user = "navidrome"; group = "navidrome"; directory = "/var/lib/private/navidrome"; }
|
||||
];
|
||||
services.navidrome.enable = true;
|
||||
|
|
|
@ -122,7 +122,7 @@ in
|
|||
|
||||
users.users.acme.uid = config.sane.allocations.acme-uid;
|
||||
users.groups.acme.gid = config.sane.allocations.acme-gid;
|
||||
sane.impermanence.dirs = [
|
||||
sane.impermanence.dirs.sys.plaintext = [
|
||||
# TODO: mode?
|
||||
{ user = "acme"; group = "acme"; directory = "/var/lib/acme"; }
|
||||
{ user = "colin"; group = "users"; directory = "/var/www/sites"; }
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
{ config, pkgs, ... }:
|
||||
|
||||
{
|
||||
sane.impermanence.dirs = [
|
||||
sane.impermanence.dirs.sys.plaintext = [
|
||||
# TODO: mode? could be more granular
|
||||
{ user = "pleroma"; group = "pleroma"; directory = "/var/lib/pleroma"; }
|
||||
];
|
||||
|
|
|
@ -16,7 +16,7 @@ let
|
|||
};
|
||||
in
|
||||
{
|
||||
sane.impermanence.dirs = [
|
||||
sane.impermanence.dirs.sys.plaintext = [
|
||||
# TODO: mode? could be more granular
|
||||
{ user = "opendkim"; group = "opendkim"; directory = "/var/lib/opendkim"; }
|
||||
{ user = "root"; group = "root"; directory = "/var/lib/postfix"; }
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{ ... }:
|
||||
|
||||
{
|
||||
sane.impermanence.dirs = [
|
||||
sane.impermanence.dirs.sys.plaintext = [
|
||||
# TODO: mode?
|
||||
{ user = "postgres"; group = "postgres"; directory = "/var/lib/postgresql"; }
|
||||
];
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
# nixnet runs ejabberd, so revisiting that.
|
||||
lib.mkIf false
|
||||
{
|
||||
sane.impermanence.dirs = [
|
||||
sane.impermanence.dirs.sys.plaintext = [
|
||||
{ user = "prosody"; group = "prosody"; directory = "/var/lib/prosody"; }
|
||||
];
|
||||
networking.firewall.allowedTCPPorts = [
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{ pkgs, ... }:
|
||||
|
||||
{
|
||||
sane.impermanence.dirs = [
|
||||
sane.impermanence.dirs.sys.plaintext = [
|
||||
# TODO: mode? we need this specifically for the stats tracking in .config/
|
||||
{ user = "transmission"; group = "transmission"; directory = "/var/lib/transmission"; }
|
||||
];
|
||||
|
|
|
@ -91,6 +91,11 @@ let
|
|||
description = "fs path to bind-mount from";
|
||||
default = null;
|
||||
};
|
||||
extraOptions = mkOption {
|
||||
type = types.listOf types.str;
|
||||
description = "extra fstab options for this mount";
|
||||
default = [];
|
||||
};
|
||||
unit = mkOption {
|
||||
type = types.str;
|
||||
description = "name of the systemd unit which mounts this path";
|
||||
|
@ -126,12 +131,14 @@ let
|
|||
device = opt.mount.bind;
|
||||
options = [
|
||||
"bind"
|
||||
# x-systemd options documented here:
|
||||
# - <https://www.freedesktop.org/software/systemd/man/systemd.mount.html>
|
||||
# we can't mount this until after the underlying path is prepared.
|
||||
# if the underlying path disappears, this mount will be stopped.
|
||||
"x-systemd.requires=${underlying.dir.unit}"
|
||||
# the mount depends on its target directory being prepared
|
||||
"x-systemd.requires=${opt.dir.unit}"
|
||||
];
|
||||
] ++ opt.mount.extraOptions;
|
||||
noCheck = true;
|
||||
};
|
||||
});
|
||||
|
|
|
@ -11,8 +11,6 @@ let
|
|||
cfg = config.sane.home-manager;
|
||||
# extract `pkg` from `sane.packages.enabledUserPkgs`
|
||||
pkg-list = pkgspec: builtins.map (e: e.pkg) pkgspec;
|
||||
# extract `private` from `sane.packages.enabledUserPkgs`
|
||||
private-list = pkgspec: builtins.concatLists (builtins.map (e: e.private) pkgspec);
|
||||
feeds = import ./feeds.nix { inherit lib; };
|
||||
in
|
||||
{
|
||||
|
@ -50,9 +48,10 @@ in
|
|||
};
|
||||
|
||||
config = lib.mkIf cfg.enable {
|
||||
sane.impermanence.home-dirs = [
|
||||
sane.impermanence.dirs.home.plaintext = [
|
||||
"archive"
|
||||
"dev"
|
||||
# TODO: records should be private
|
||||
"records"
|
||||
"ref"
|
||||
"tmp"
|
||||
|
@ -90,15 +89,7 @@ in
|
|||
};
|
||||
|
||||
|
||||
home.file = let
|
||||
privates = builtins.listToAttrs (
|
||||
builtins.map (path: {
|
||||
name = path;
|
||||
value = { source = config.lib.file.mkOutOfStoreSymlink "/home/colin/private/${path}"; };
|
||||
})
|
||||
(private-list sysconfig.sane.packages.enabledUserPkgs)
|
||||
);
|
||||
in {
|
||||
home.file = {
|
||||
# convenience
|
||||
"knowledge".source = config.lib.file.mkOutOfStoreSymlink "/home/colin/private/knowledge";
|
||||
"nixos".source = config.lib.file.mkOutOfStoreSymlink "/home/colin/dev/nixos";
|
||||
|
@ -108,7 +99,7 @@ in
|
|||
|
||||
# used by password managers, e.g. unix `pass`
|
||||
".password-store".source = config.lib.file.mkOutOfStoreSymlink "/home/colin/knowledge/secrets/accounts";
|
||||
} // privates;
|
||||
};
|
||||
|
||||
# XDG defines things like ~/Desktop, ~/Downloads, etc.
|
||||
# these clutter the home, so i mostly don't use them.
|
||||
|
|
|
@ -2,7 +2,8 @@
|
|||
|
||||
lib.mkIf config.sane.home-manager.enable
|
||||
{
|
||||
sane.impermanence.home-dirs = [ ".cache/vim-swap" ];
|
||||
# private because there could be sensitive things in the swap
|
||||
sane.impermanence.dirs.home.private = [ ".cache/vim-swap" ];
|
||||
|
||||
home-manager.users.colin.programs.neovim = {
|
||||
# neovim: https://github.com/neovim/neovim
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
lib.mkIf config.sane.home-manager.enable
|
||||
{
|
||||
sane.impermanence.home-dirs = [
|
||||
sane.impermanence.dirs.home.plaintext = [
|
||||
# we don't need to full zsh dir -- just the history file --
|
||||
# but zsh will sometimes backup the history file and we get fewer errors if we do proper mounts instead of symlinks.
|
||||
# TODO: should be private?
|
||||
|
|
|
@ -8,9 +8,20 @@ with lib;
|
|||
let
|
||||
cfg = config.sane.impermanence;
|
||||
|
||||
stores = {
|
||||
"crypt-clearedonboot" = "/mnt/impermanence/crypt/clearedonboot";
|
||||
"persist" = "/nix/persist";
|
||||
storeType = types.submodule {
|
||||
options = {
|
||||
mountpt = mkOption {
|
||||
type = types.str;
|
||||
};
|
||||
prefix = mkOption {
|
||||
type = types.str;
|
||||
default = "/";
|
||||
};
|
||||
extraOptions = mkOption {
|
||||
type = types.listOf types.str;
|
||||
default = [];
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
# split the string path into a list of string components.
|
||||
|
@ -22,17 +33,18 @@ let
|
|||
# return a string path, with leading slash but no trailing slash
|
||||
joinPathAbs = comps: "/" + (builtins.concatStringsSep "/" comps);
|
||||
concatPaths = paths: joinPathAbs (builtins.concatLists (builtins.map (p: splitPath p) paths));
|
||||
# return the path from `from` to `to`, but generally in absolute form.
|
||||
# e.g. `pathFrom "/home/colin" "/home/colin/foo/bar"` -> "/foo/bar"
|
||||
pathFrom = from: to:
|
||||
assert lib.hasPrefix from to;
|
||||
lib.removePrefix from to;
|
||||
|
||||
# options for a single mountpoint / persistence
|
||||
dirEntry = types.submodule {
|
||||
dirEntryOptions = {
|
||||
options = {
|
||||
directory = mkOption {
|
||||
type = types.str;
|
||||
};
|
||||
store = mkOption {
|
||||
default = "persist";
|
||||
type = types.enum (builtins.attrNames stores);
|
||||
};
|
||||
user = mkOption {
|
||||
type = types.nullOr types.str;
|
||||
default = null;
|
||||
|
@ -47,22 +59,88 @@ let
|
|||
};
|
||||
};
|
||||
};
|
||||
contextualizedDir = types.submodule dirEntryOptions;
|
||||
# allow "bar/baz" as shorthand for { directory = "bar/baz"; }
|
||||
coercedDirEntry = types.coercedTo types.str (d: { directory = d; }) dirEntry;
|
||||
contextualizedDirOrShorthand = types.coercedTo
|
||||
types.str
|
||||
(d: { directory = d; })
|
||||
contextualizedDir;
|
||||
|
||||
# expand user options with more context
|
||||
ingestDirEntry = relativeTo: opt: {
|
||||
inherit (opt) user group mode;
|
||||
directory = concatPaths [ relativeTo opt.directory ];
|
||||
# entry whose `directory` is always an absolute fs path
|
||||
# and has an associated `store`
|
||||
contextFreeDir = types.submodule [
|
||||
dirEntryOptions
|
||||
{
|
||||
options = {
|
||||
store = mkOption {
|
||||
type = storeType;
|
||||
};
|
||||
};
|
||||
}
|
||||
];
|
||||
|
||||
# resolve the store
|
||||
store = stores."${opt.store}";
|
||||
};
|
||||
|
||||
ingestDirEntries = relativeTo: opts: builtins.map (ingestDirEntry relativeTo) opts;
|
||||
ingested-home-dirs = ingestDirEntries "/home/colin" cfg.home-dirs;
|
||||
ingested-sys-dirs = ingestDirEntries "/" cfg.dirs;
|
||||
ingested-dirs = ingested-home-dirs ++ ingested-sys-dirs;
|
||||
dirsModule = types.submodule ({ config, ... }: {
|
||||
options = {
|
||||
home = mkOption {
|
||||
description = "directories to persist to disk, relative to a user's home ~";
|
||||
default = {};
|
||||
type = types.submodule {
|
||||
options = {
|
||||
plaintext = mkOption {
|
||||
default = [];
|
||||
type = types.listOf contextualizedDirOrShorthand;
|
||||
description = "directories to persist in cleartext";
|
||||
};
|
||||
private = mkOption {
|
||||
default = [];
|
||||
type = types.listOf contextualizedDirOrShorthand;
|
||||
description = "directories to store encrypted to the user's login password and auto-decrypt on login";
|
||||
};
|
||||
cryptClearOnBoot = mkOption {
|
||||
default = [];
|
||||
type = types.listOf contextualizedDirOrShorthand;
|
||||
description = ''
|
||||
directories to store encrypted to an auto-generated in-memory key and
|
||||
wiped on boot. the main use is for sensitive cache dirs too large to fit in memory.
|
||||
'';
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
sys = mkOption {
|
||||
description = "directories to persist to disk, relative to the fs root /";
|
||||
default = {};
|
||||
type = types.submodule {
|
||||
options = {
|
||||
plaintext = mkOption {
|
||||
default = [];
|
||||
type = types.listOf contextualizedDirOrShorthand;
|
||||
description = "list of directories (and optional config) to persist to disk in plaintext, relative to the fs root /";
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
all = mkOption {
|
||||
type = types.listOf contextFreeDir;
|
||||
description = "all directories known to the config. auto-computed: users should not set this directly.";
|
||||
};
|
||||
};
|
||||
config = let
|
||||
mapDirs = relativeTo: store: dirs: (map
|
||||
(d: {
|
||||
inherit (d) user group mode;
|
||||
directory = concatPaths [ relativeTo d.directory ];
|
||||
store = cfg.stores."${store}";
|
||||
})
|
||||
dirs
|
||||
);
|
||||
in {
|
||||
all = (mapDirs "/home/colin" "plaintext" config.home.plaintext)
|
||||
++ (mapDirs "/home/colin" "private" config.home.private)
|
||||
++ (mapDirs "/home/colin" "cryptClearOnBoot" config.home.cryptClearOnBoot)
|
||||
++ (mapDirs "/" "plaintext" config.sys.plaintext);
|
||||
};
|
||||
});
|
||||
in
|
||||
{
|
||||
options = {
|
||||
|
@ -73,23 +151,21 @@ in
|
|||
sane.impermanence.root-on-tmpfs = mkOption {
|
||||
default = false;
|
||||
type = types.bool;
|
||||
description = "define / to be a tmpfs. make sure to mount some other device to /nix";
|
||||
};
|
||||
sane.impermanence.home-dirs = mkOption {
|
||||
default = [];
|
||||
type = types.listOf coercedDirEntry;
|
||||
description = "list of directories (and optional config) to persist to disk, relative to the user's home ~";
|
||||
description = "define / fs root to be a tmpfs. make sure to mount some other device to /nix";
|
||||
};
|
||||
sane.impermanence.dirs = mkOption {
|
||||
default = [];
|
||||
type = types.listOf coercedDirEntry;
|
||||
description = "list of directories (and optional config) to persist to disk, relative to the fs root /";
|
||||
type = dirsModule;
|
||||
default = {};
|
||||
};
|
||||
sane.impermanence.stores = mkOption {
|
||||
type = types.attrsOf storeType;
|
||||
default = {};
|
||||
};
|
||||
};
|
||||
|
||||
imports = [
|
||||
./crypt.nix
|
||||
./root-on-tmpfs.nix
|
||||
./stores
|
||||
];
|
||||
|
||||
config = mkIf cfg.enable (lib.mkMerge [
|
||||
|
@ -100,6 +176,7 @@ in
|
|||
group = config.users.users.colin.group;
|
||||
mode = config.users.users.colin.homeMode;
|
||||
};
|
||||
|
||||
# N.B.: we have a similar problem with all mounts:
|
||||
# <crypt>/.cache/mozilla won't inherit <plain>/.cache perms.
|
||||
# this is less of a problem though, since we don't really support overlapping mounts like that in the first place.
|
||||
|
@ -113,7 +190,9 @@ in
|
|||
(
|
||||
let cfgFor = opt:
|
||||
let
|
||||
backing-path = concatPaths [ opt.store opt.directory ];
|
||||
store = opt.store;
|
||||
store-rel-path = pathFrom store.prefix opt.directory;
|
||||
backing-path = concatPaths [ store.mountpt store-rel-path ];
|
||||
|
||||
# pass through the perm/mode overrides
|
||||
dir-acl = {
|
||||
|
@ -127,13 +206,14 @@ in
|
|||
# inherit perms & make sure we don't mount until after the mount point is setup correctly.
|
||||
dir.acl = dir-acl;
|
||||
mount.bind = backing-path;
|
||||
mount.extraOptions = store.extraOptions;
|
||||
};
|
||||
sane.fs."${backing-path}" = {
|
||||
# ensure the backing path has same perms as the mount point
|
||||
dir.acl = config.sane.fs."${opt.directory}".dir.acl;
|
||||
};
|
||||
};
|
||||
cfgs = builtins.map cfgFor ingested-dirs;
|
||||
cfgs = builtins.map cfgFor cfg.dirs.all;
|
||||
in {
|
||||
sane.fs = lib.mkMerge (catAttrs "fs" (catAttrs "sane" cfgs));
|
||||
}
|
||||
|
|
|
@ -29,9 +29,13 @@ let
|
|||
fi
|
||||
'';
|
||||
};
|
||||
private-mount-unit = ''${utils.escapeSystemdPath "/home/colin/private"}.mount'';
|
||||
in lib.mkIf config.sane.impermanence.enable
|
||||
in
|
||||
lib.mkIf config.sane.impermanence.enable
|
||||
{
|
||||
sane.impermanence.stores."cryptClearOnBoot" = {
|
||||
mountpt = "/mnt/impermanence/crypt/clearedonboot";
|
||||
};
|
||||
|
||||
systemd.services."prepareEncryptedClearedOnBoot" = rec {
|
||||
description = "prepare keys for ${store.device}";
|
||||
serviceConfig.ExecStart = ''
|
||||
|
@ -80,39 +84,6 @@ in lib.mkIf config.sane.impermanence.enable
|
|||
dir.reverseDepends = [ store.mount-unit ];
|
||||
};
|
||||
|
||||
fileSystems."/home/colin/private" = {
|
||||
device = "/nix/persist/home/colin/private";
|
||||
fsType = "fuse.gocryptfs";
|
||||
options = [
|
||||
"noauto" # don't try to mount, until the user logs in!
|
||||
"allow_other" # root ends up being the user that mounts this, so need to make it visible to `colin`.
|
||||
"nodev"
|
||||
"nosuid"
|
||||
"quiet"
|
||||
"defaults"
|
||||
];
|
||||
noCheck = true;
|
||||
};
|
||||
sane.fs."/home/colin/private" = {
|
||||
dir.reverseDepends = [
|
||||
# mounting relies on the mountpoint first being created.
|
||||
private-mount-unit
|
||||
# ensure the directory is created during boot, and before user logs in.
|
||||
"multi-user.target"
|
||||
];
|
||||
# HACK: this fs entry is provided by the mount unit.
|
||||
unit = private-mount-unit;
|
||||
};
|
||||
sane.fs."/nix/persist/home/colin/private" = {
|
||||
dir.reverseDepends = [
|
||||
# the mount unit relies on the source having first been created.
|
||||
# (it also relies on the cryptfs having been seeded -- which we can't verify here).
|
||||
private-mount-unit
|
||||
# ensure the directory is created during boot, and before user logs in.
|
||||
"multi-user.target"
|
||||
];
|
||||
};
|
||||
|
||||
# TODO: could add this *specifically* to the .mount file for the encrypted fs?
|
||||
environment.systemPackages = [ pkgs.gocryptfs ]; # fuse needs to find gocryptfs
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
{ config, lib, ... }:
|
||||
|
||||
let
|
||||
cfg = config.sane.impermanence;
|
||||
in
|
||||
{
|
||||
imports = [
|
||||
./crypt.nix
|
||||
./private.nix
|
||||
];
|
||||
|
||||
config = lib.mkIf cfg.enable {
|
||||
sane.impermanence.stores."plaintext" = {
|
||||
mountpt = "/nix/persist";
|
||||
};
|
||||
};
|
||||
}
|
|
@ -0,0 +1,61 @@
|
|||
{ config, lib, pkgs, utils, ... }:
|
||||
|
||||
let
|
||||
private-mount-unit = ''${utils.escapeSystemdPath "/home/colin/private"}.mount'';
|
||||
in lib.mkIf config.sane.impermanence.enable
|
||||
{
|
||||
sane.impermanence.stores."private" = {
|
||||
mountpt = "/home/colin/private";
|
||||
# files stored under here *must* have the /home/colin prefix.
|
||||
# internally, this prefix is removed so that e.g.
|
||||
# /home/colin/foo/bar when stored in `private` is visible at
|
||||
# /home/colin/private/foo/bar
|
||||
prefix = "/home/colin";
|
||||
# fstab options inherited by all members of the store
|
||||
extraOptions = let
|
||||
private-unit = config.sane.fs."/home/colin/private".unit;
|
||||
in [
|
||||
"noauto"
|
||||
# auto mount when ~/private is mounted
|
||||
"x-systemd.wanted-by=${private-unit}"
|
||||
];
|
||||
};
|
||||
|
||||
fileSystems."/home/colin/private" = {
|
||||
device = "/nix/persist/home/colin/private";
|
||||
fsType = "fuse.gocryptfs";
|
||||
options = [
|
||||
"noauto" # don't try to mount, until the user logs in!
|
||||
"allow_other" # root ends up being the user that mounts this, so need to make it visible to `colin`.
|
||||
"nodev"
|
||||
"nosuid"
|
||||
"quiet"
|
||||
"defaults"
|
||||
];
|
||||
noCheck = true;
|
||||
};
|
||||
|
||||
sane.fs."/home/colin/private" = {
|
||||
dir.reverseDepends = [
|
||||
# mounting relies on the mountpoint first being created.
|
||||
private-mount-unit
|
||||
# ensure the directory is created during boot, and before user logs in.
|
||||
"multi-user.target"
|
||||
];
|
||||
# HACK: this fs entry is provided by the mount unit.
|
||||
unit = private-mount-unit;
|
||||
};
|
||||
sane.fs."/nix/persist/home/colin/private" = {
|
||||
dir.reverseDepends = [
|
||||
# the mount unit relies on the source having first been created.
|
||||
# (it also relies on the cryptfs having been seeded -- which we can't verify here).
|
||||
private-mount-unit
|
||||
# ensure the directory is created during boot, and before user logs in.
|
||||
"multi-user.target"
|
||||
];
|
||||
};
|
||||
|
||||
# TODO: could add this *specifically* to the .mount file for the encrypted fs?
|
||||
environment.systemPackages = [ pkgs.gocryptfs ]; # fuse needs to find gocryptfs
|
||||
}
|
||||
|
|
@ -307,7 +307,8 @@ in
|
|||
|
||||
config = {
|
||||
environment.systemPackages = mkIf cfg.enableSystemPkgs systemPkgs;
|
||||
sane.impermanence.home-dirs = concatLists (map (p: p.dir) cfg.enabledUserPkgs);
|
||||
sane.impermanence.dirs.home.plaintext = concatLists (map (p: p.dir) cfg.enabledUserPkgs);
|
||||
sane.impermanence.dirs.home.private = concatLists (map (p: p.private) cfg.enabledUserPkgs);
|
||||
# XXX: this might not be necessary. try removing this and cacert.unbundled?
|
||||
environment.etc."ssl/certs".source = mkIf cfg.enableSystemPkgs "${pkgs.cacert.unbundled}/etc/ssl/certs/*";
|
||||
};
|
||||
|
|
|
@ -15,7 +15,8 @@ in
|
|||
|
||||
config = mkIf cfg.enable {
|
||||
# we need this mostly because of the size of duplicity's cache
|
||||
sane.impermanence.dirs = [ "/var/lib/duplicity" ];
|
||||
# TODO: move to cryptClearOnBoot and update perms
|
||||
sane.impermanence.dirs.sys.plaintext = [ "/var/lib/duplicity" ];
|
||||
|
||||
services.duplicity.enable = true;
|
||||
services.duplicity.targetUrl = "$DUPLICITY_URL";
|
||||
|
|
Loading…
Reference in New Issue