sane.fs: take in the role of generating systemd.mounts files
This commit is contained in:
@@ -126,11 +126,6 @@ let
|
||||
systemdName = utils.escapeSystemdPath localPath;
|
||||
in {
|
||||
sane.programs.curlftpfs.enableFor.system = true;
|
||||
sane.fs."${localPath}" = sane-lib.fs.wanted {
|
||||
dir.acl.user = "colin";
|
||||
dir.acl.group = "users";
|
||||
dir.acl.mode = "0750";
|
||||
};
|
||||
fileSystems."${localPath}" = {
|
||||
device = "ftp://servo-hn:/${subdir}";
|
||||
noCheck = true;
|
||||
@@ -139,43 +134,37 @@ let
|
||||
# fsType = "nfs";
|
||||
# options = fsOpts.nfs ++ fsOpts.lazyMount;
|
||||
};
|
||||
|
||||
systemd.mounts = let
|
||||
fsEntry = config.fileSystems."${localPath}";
|
||||
in [{
|
||||
#VVV repeat what systemd would ordinarily scrape from /etc/fstab
|
||||
where = localPath;
|
||||
what = fsEntry.device;
|
||||
type = fsEntry.fsType;
|
||||
options = lib.concatStringsSep "," fsEntry.options;
|
||||
after = [ "network-online.target" ];
|
||||
requires = [ "network-online.target" ];
|
||||
wantedBy = [ "default.target" ]; #< TODO: move this into nixos fileSystems
|
||||
sane.fs."${localPath}" = {
|
||||
dir.acl.user = "colin";
|
||||
dir.acl.group = "users";
|
||||
dir.acl.mode = "0750";
|
||||
wantedBy = [ "default.target" ];
|
||||
mount.depends = [ "network-online.target" ];
|
||||
#VVV patch so that when the mount fails, we start a timer to remount it.
|
||||
# and for a disconnection after a good mount (onSuccess), restart the timer to be more aggressive
|
||||
onFailure = [ "${systemdName}.timer" ];
|
||||
onSuccess = [ "${systemdName}-restart-timer.target" ];
|
||||
mount.unitConfig.OnFailure = [ "${systemdName}.timer" ];
|
||||
mount.unitConfig.OnSuccess = [ "${systemdName}-restart-timer.target" ];
|
||||
|
||||
mountConfig.User = "colin";
|
||||
mountConfig.Group = "users";
|
||||
mount.mountConfig.User = "colin";
|
||||
mount.mountConfig.Group = "users";
|
||||
# hardening (systemd-analyze security mnt-servo-playground.mount)
|
||||
mountConfig.AmbientCapabilities = "CAP_SYS_ADMIN";
|
||||
mountConfig.CapabilityBoundingSet = "CAP_SYS_ADMIN";
|
||||
mountConfig.LockPersonality = true;
|
||||
mountConfig.MemoryDenyWriteExecute = true;
|
||||
mountConfig.NoNewPrivileges = true;
|
||||
mountConfig.ProtectClock = true;
|
||||
mountConfig.ProtectHostname = true;
|
||||
mountConfig.RemoveIPC = true;
|
||||
mountConfig.RestrictAddressFamilies = "AF_UNIX AF_INET AF_INET6";
|
||||
mount.mountConfig.AmbientCapabilities = "CAP_SYS_ADMIN";
|
||||
mount.mountConfig.CapabilityBoundingSet = "CAP_SYS_ADMIN";
|
||||
mount.mountConfig.LockPersonality = true;
|
||||
mount.mountConfig.MemoryDenyWriteExecute = true;
|
||||
mount.mountConfig.NoNewPrivileges = true;
|
||||
mount.mountConfig.ProtectClock = true;
|
||||
mount.mountConfig.ProtectHostname = true;
|
||||
mount.mountConfig.RemoveIPC = true;
|
||||
mount.mountConfig.RestrictAddressFamilies = "AF_UNIX AF_INET AF_INET6";
|
||||
#VVV this includes anything it reads from, e.g. /bin/sh; /nix/store/...
|
||||
# see `systemd-analyze filesystems` for a full list
|
||||
mountConfig.RestrictFileSystems = "@common-block devtmpfs fuse";
|
||||
mountConfig.RestrictNamespaces = true;
|
||||
mountConfig.RestrictRealtime = true;
|
||||
mountConfig.RestrictSUIDSGID = true;
|
||||
mountConfig.SystemCallArchitectures = "native";
|
||||
mountConfig.SystemCallFilter = [
|
||||
mount.mountConfig.RestrictFileSystems = "@common-block devtmpfs fuse";
|
||||
mount.mountConfig.RestrictNamespaces = true;
|
||||
mount.mountConfig.RestrictRealtime = true;
|
||||
mount.mountConfig.RestrictSUIDSGID = true;
|
||||
mount.mountConfig.SystemCallArchitectures = "native";
|
||||
mount.mountConfig.SystemCallFilter = [
|
||||
"@system-service"
|
||||
"@mount"
|
||||
"~@chown"
|
||||
@@ -184,71 +173,12 @@ let
|
||||
# could remove almost all io calls, however one has to keep `open`, and `write`, to communicate with the fuse device.
|
||||
# so that's pretty useless as a way to prevent write access
|
||||
];
|
||||
mountConfig.IPAddressDeny = "any";
|
||||
mountConfig.IPAddressAllow = "10.0.10.5";
|
||||
mountConfig.DevicePolicy = "closed"; # only allow /dev/{null,zero,full,random,urandom}
|
||||
mountConfig.DeviceAllow = "/dev/fuse";
|
||||
mount.mountConfig.IPAddressDeny = "any";
|
||||
mount.mountConfig.IPAddressAllow = "10.0.10.5";
|
||||
mount.mountConfig.DevicePolicy = "closed"; # only allow /dev/{null,zero,full,random,urandom}
|
||||
mount.mountConfig.DeviceAllow = "/dev/fuse";
|
||||
};
|
||||
|
||||
# SystemCallFilter=~@aio
|
||||
# SystemCallFilter=~@basic-io
|
||||
# SystemCallFilter=write
|
||||
# SystemCallFilter=writev
|
||||
# SystemCallFilter=_llseek
|
||||
# SystemCallFilter=close
|
||||
# SystemCallFilter=close_range
|
||||
# SystemCallFilter=dup
|
||||
# SystemCallFilter=dup2
|
||||
# SystemCallFilter=dup3
|
||||
# SystemCallFilter=lseek
|
||||
# SystemCallFilter=pread64
|
||||
# SystemCallFilter=preadv
|
||||
# SystemCallFilter=preadv2
|
||||
# SystemCallFilter=read
|
||||
# SystemCallFilter=readv
|
||||
# SystemCallFilter=access
|
||||
# SystemCallFilter=chdir
|
||||
# SystemCallFilter=close
|
||||
# SystemCallFilter=faccessat
|
||||
# SystemCallFilter=faccessat2
|
||||
# SystemCallFilter=fchdir
|
||||
# SystemCallFilter=fcntl
|
||||
# SystemCallFilter=fcntl64
|
||||
# SystemCallFilter=fgetxattr
|
||||
# SystemCallFilter=flistxattr
|
||||
# SystemCallFilter=fstat
|
||||
# SystemCallFilter=fstat64
|
||||
# SystemCallFilter=fstatat64
|
||||
# SystemCallFilter=fstatfs
|
||||
# SystemCallFilter=fstatfs64
|
||||
# SystemCallFilter=futimesat
|
||||
# SystemCallFilter=getcwd
|
||||
# SystemCallFilter=getdents
|
||||
# SystemCallFilter=getdents64
|
||||
# SystemCallFilter=getxattr
|
||||
# SystemCallFilter=lgetxattr
|
||||
# SystemCallFilter=listxattr
|
||||
# SystemCallFilter=llistxattr
|
||||
# SystemCallFilter=lstat
|
||||
# SystemCallFilter=lstat64
|
||||
# SystemCallFilter=newfstatat
|
||||
# SystemCallFilter=oldfstat
|
||||
# SystemCallFilter=oldlstat
|
||||
# SystemCallFilter=oldstat
|
||||
# SystemCallFilter=open
|
||||
# SystemCallFilter=openat
|
||||
# SystemCallFilter=openat2
|
||||
# SystemCallFilter=readlink
|
||||
# SystemCallFilter=readlinkat
|
||||
# SystemCallFilter=stat
|
||||
# SystemCallFilter=stat64
|
||||
# SystemCallFilter=statfs
|
||||
# SystemCallFilter=statfs64
|
||||
# SystemCallFilter=statx
|
||||
# SystemCallFilter=utime
|
||||
# SystemCallFilter=utimensat
|
||||
# SystemCallFilter=utimensat_time64
|
||||
# SystemCallFilter=utimes
|
||||
}];
|
||||
systemd.targets."${systemdName}-restart-timer" = {
|
||||
# hack unit which, when started, stops the timer (if running), and then starts it again.
|
||||
after = [ "${systemdName}.timer" ];
|
||||
|
@@ -234,6 +234,20 @@ let
|
||||
description = "name of the systemd unit which mounts this path";
|
||||
default = mountNameFor path;
|
||||
};
|
||||
mountConfig = mkOption {
|
||||
type = types.attrs;
|
||||
description = ''
|
||||
attrset to add to the [Mount] section of the systemd unit file.
|
||||
'';
|
||||
default = {};
|
||||
};
|
||||
unitConfig = mkOption {
|
||||
type = types.attrs;
|
||||
description = ''
|
||||
attrset to add to the [Unit] section of the systemd unit file.
|
||||
'';
|
||||
default = {};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
@@ -278,7 +292,7 @@ let
|
||||
# - prepare the source directory -- assuming it's not an external device
|
||||
# - satisfy any user-specified prerequisites ("depends")
|
||||
requires = [ opt.generated.unit ]
|
||||
++ (if lib.hasPrefix "/dev/disk/" device then [] else [ underlying.unit ])
|
||||
++ (if lib.hasPrefix "/dev/disk/" device || lib.hasPrefix "ftp://" device then [] else [ underlying.unit ])
|
||||
++ opt.mount.depends;
|
||||
in {
|
||||
fileSystems."${path}" = {
|
||||
@@ -298,6 +312,19 @@ let
|
||||
++ (builtins.map (unit: "x-systemd.wanted-by=${unit}") (opt.wantedBy ++ opt.wantedBeforeBy));
|
||||
noCheck = ifBind true;
|
||||
};
|
||||
systemd.mounts = let
|
||||
fsEntry = config.fileSystems."${path}";
|
||||
in [{
|
||||
where = path;
|
||||
what = if fsEntry.device != null then fsEntry.device else "";
|
||||
type = fsEntry.fsType;
|
||||
options = lib.concatStringsSep "," fsEntry.options;
|
||||
after = requires;
|
||||
requires = requires;
|
||||
before = opt.wantedBeforeBy;
|
||||
wantedBy = opt.wantedBeforeBy;
|
||||
inherit (opt.mount) mountConfig unitConfig;
|
||||
}];
|
||||
});
|
||||
|
||||
|
||||
@@ -358,7 +385,9 @@ in {
|
||||
let
|
||||
configs = lib.mapAttrsToList mkFsConfig cfg;
|
||||
take = f: {
|
||||
systemd.services = f.systemd.services;
|
||||
systemd = f.systemd;
|
||||
# systemd.mounts = f.systemd.mounts;
|
||||
# systemd.services = f.systemd.services;
|
||||
fileSystems = f.fileSystems;
|
||||
};
|
||||
in take (sane-lib.mkTypedMerge take configs);
|
||||
|
@@ -51,50 +51,43 @@ lib.mkIf config.sane.persist.enable
|
||||
noCheck = true;
|
||||
};
|
||||
# let sane.fs know about our fileSystem and automatically add the appropriate dependencies
|
||||
sane.fs."${origin}".mount = { };
|
||||
sane.fs."${backing}" = sane-lib.fs.wantedDir;
|
||||
|
||||
systemd.mounts = let
|
||||
fsEntry = config.fileSystems."${origin}";
|
||||
in [{
|
||||
#VVV repeat what systemd would ordinarily scrape from /etc/fstab
|
||||
where = origin;
|
||||
what = fsEntry.device;
|
||||
type = fsEntry.fsType;
|
||||
options = lib.concatStringsSep "," fsEntry.options;
|
||||
wantedBy = [ "local-fs.target" ];
|
||||
|
||||
sane.fs."${origin}" = {
|
||||
wantedBeforeBy = [ "local-fs.target" ];
|
||||
mount.depends = [
|
||||
config.sane.fs."${backing}".unit
|
||||
];
|
||||
# hardening (systemd-analyze security mnt-persist-ephemeral.mount)
|
||||
mountConfig.AmbientCapabilities = "";
|
||||
mount.mountConfig.AmbientCapabilities = "";
|
||||
# CAP_LEASE is probably not necessary -- does any fs user use leases?
|
||||
mountConfig.CapabilityBoundingSet = "CAP_SYS_ADMIN CAP_DAC_OVERRIDE CAP_DAC_READ_SEARCH CAP_CHOWN CAP_MKNOD CAP_LEASE CAP_SETGID CAP_SETUID CAP_FOWNER";
|
||||
mountConfig.LockPersonality = true;
|
||||
mountConfig.MemoryDenyWriteExecute = true;
|
||||
mountConfig.NoNewPrivileges = true;
|
||||
mountConfig.ProtectClock = true;
|
||||
mountConfig.ProtectHostname = true;
|
||||
mountConfig.RemoveIPC = true;
|
||||
mountConfig.RestrictAddressFamilies = "AF_UNIX"; # "none" works, but then it can't connect to the logger
|
||||
mount.mountConfig.CapabilityBoundingSet = "CAP_SYS_ADMIN CAP_DAC_OVERRIDE CAP_DAC_READ_SEARCH CAP_CHOWN CAP_MKNOD CAP_LEASE CAP_SETGID CAP_SETUID CAP_FOWNER";
|
||||
mount.mountConfig.LockPersonality = true;
|
||||
mount.mountConfig.MemoryDenyWriteExecute = true;
|
||||
mount.mountConfig.NoNewPrivileges = true;
|
||||
mount.mountConfig.ProtectClock = true;
|
||||
mount.mountConfig.ProtectHostname = true;
|
||||
mount.mountConfig.RemoveIPC = true;
|
||||
mount.mountConfig.RestrictAddressFamilies = "AF_UNIX"; # "none" works, but then it can't connect to the logger
|
||||
#VVV this includes anything it reads from, e.g. /bin/sh; /nix/store/...
|
||||
# see `systemd-analyze filesystems` for a full list
|
||||
mountConfig.RestrictFileSystems = "@common-block devtmpfs fuse pipefs";
|
||||
mountConfig.RestrictNamespaces = true;
|
||||
mountConfig.RestrictNetworkInterfaces = "";
|
||||
mountConfig.RestrictRealtime = true;
|
||||
mountConfig.RestrictSUIDSGID = true;
|
||||
mountConfig.SystemCallArchitectures = "native";
|
||||
mountConfig.SystemCallFilter = [
|
||||
mount.mountConfig.RestrictFileSystems = "@common-block devtmpfs fuse pipefs";
|
||||
mount.mountConfig.RestrictNamespaces = true;
|
||||
mount.mountConfig.RestrictNetworkInterfaces = "";
|
||||
mount.mountConfig.RestrictRealtime = true;
|
||||
mount.mountConfig.RestrictSUIDSGID = true;
|
||||
mount.mountConfig.SystemCallArchitectures = "native";
|
||||
mount.mountConfig.SystemCallFilter = [
|
||||
# unfortunately, i need to keep @network-io (accept, bind, connect, listen, recv, send, socket, ...). not sure why (daemon control socket?).
|
||||
"@system-service" "@mount" "~@cpu-emulation" "~@keyring"
|
||||
];
|
||||
mountConfig.IPAddressDeny = "any";
|
||||
mountConfig.DevicePolicy = "closed"; # only allow /dev/{null,zero,full,random,urandom}
|
||||
mountConfig.DeviceAllow = "/dev/fuse";
|
||||
mountConfig.SocketBindDeny = "any";
|
||||
mount.mountConfig.IPAddressDeny = "any";
|
||||
mount.mountConfig.DevicePolicy = "closed"; # only allow /dev/{null,zero,full,random,urandom}
|
||||
mount.mountConfig.DeviceAllow = "/dev/fuse";
|
||||
mount.mountConfig.SocketBindDeny = "any";
|
||||
# note that anything which requires mount namespaces (ProtectHome, ReadWritePaths, ...) does NOT work.
|
||||
# it's in theory possible, via mount propagation, but systemd provides no way for that.
|
||||
# PrivateNetwork = true BREAKS the mount action; i think systemd or udev needs that internally to communicate with the service manager?
|
||||
}];
|
||||
};
|
||||
sane.fs."${backing}".dir = {};
|
||||
|
||||
system.fsPackages = [ gocryptfs-ephemeral ]; # fuse needs to find gocryptfs
|
||||
}
|
||||
|
@@ -92,7 +92,7 @@ lib.mkIf config.sane.persist.enable
|
||||
device = backing;
|
||||
fsType = "fuse.gocryptfs-private";
|
||||
options = [
|
||||
"auto"
|
||||
# "auto"
|
||||
"nofail"
|
||||
# "nodev" # "Unknown parameter 'nodev'". gocryptfs requires this be passed as `-ko nodev`
|
||||
# "noexec" # handful of scripts in ~/knowledge that are executable
|
||||
@@ -112,11 +112,39 @@ lib.mkIf config.sane.persist.enable
|
||||
|
||||
# let sane.fs know about the mount
|
||||
sane.fs."${origin}" = {
|
||||
wantedBy = [ "local-fs.target" ];
|
||||
mount.depends = [
|
||||
config.sane.fs."${backing}".unit
|
||||
config.sane.fs."/run/gocryptfs".unit
|
||||
];
|
||||
wantedBy = [ "local-fs.target" ];
|
||||
# unitConfig.DefaultDependencies = "no";
|
||||
mount.mountConfig.TimeoutSec = "infinity";
|
||||
|
||||
# hardening (systemd-analyze security mnt-persist-private.mount)
|
||||
mount.mountConfig.AmbientCapabilities = "";
|
||||
# CAP_LEASE is probably not necessary -- does any fs user use leases?
|
||||
mount.mountConfig.CapabilityBoundingSet = "CAP_SYS_ADMIN CAP_DAC_OVERRIDE CAP_DAC_READ_SEARCH CAP_CHOWN CAP_MKNOD CAP_LEASE CAP_SETGID CAP_SETUID CAP_FOWNER";
|
||||
mount.mountConfig.LockPersonality = true;
|
||||
mount.mountConfig.MemoryDenyWriteExecute = true;
|
||||
mount.mountConfig.NoNewPrivileges = true;
|
||||
mount.mountConfig.ProtectClock = true;
|
||||
mount.mountConfig.ProtectHostname = true;
|
||||
mount.mountConfig.RemoveIPC = true;
|
||||
mount.mountConfig.RestrictAddressFamilies = "AF_UNIX"; # "none" works, but then it can't connect to the logger
|
||||
mount.mountConfig.RestrictFileSystems = "@common-block devtmpfs fuse pipefs";
|
||||
mount.mountConfig.RestrictNamespaces = true;
|
||||
mount.mountConfig.RestrictNetworkInterfaces = "";
|
||||
mount.mountConfig.RestrictRealtime = true;
|
||||
mount.mountConfig.RestrictSUIDSGID = true;
|
||||
mount.mountConfig.SystemCallArchitectures = "native";
|
||||
mount.mountConfig.SystemCallFilter = [
|
||||
# unfortunately, i need to keep @network-io (accept, bind, connect, listen, recv, send, socket, ...). not sure why (daemon control socket?).
|
||||
"@system-service" "@mount" "~@cpu-emulation" "~@keyring"
|
||||
];
|
||||
mount.mountConfig.IPAddressDeny = "any";
|
||||
mount.mountConfig.DevicePolicy = "closed"; # only allow /dev/{null,zero,full,random,urandom}
|
||||
mount.mountConfig.DeviceAllow = "/dev/fuse";
|
||||
mount.mountConfig.SocketBindDeny = "any";
|
||||
};
|
||||
# it also needs to know that the underlying device is an ordinary folder
|
||||
sane.fs."${backing}".dir.acl.user = config.sane.defaultUser;
|
||||
@@ -125,47 +153,6 @@ lib.mkIf config.sane.persist.enable
|
||||
mode = "0700";
|
||||
};
|
||||
|
||||
systemd.mounts = let
|
||||
fsEntry = config.fileSystems."${origin}";
|
||||
in [{
|
||||
#VVV repeat what systemd would ordinarily scrape from /etc/fstab
|
||||
where = origin;
|
||||
what = fsEntry.device;
|
||||
type = fsEntry.fsType;
|
||||
options = lib.concatStringsSep "," fsEntry.options;
|
||||
after = [ config.sane.fs."${backing}".unit config.sane.fs."/run/gocryptfs".unit ];
|
||||
wants = [ config.sane.fs."${backing}".unit config.sane.fs."/run/gocryptfs".unit ];
|
||||
wantedBy = [ "local-fs.target" ];
|
||||
# before = [ "local-fs.target" ];
|
||||
# unitConfig.DefaultDependencies = "no";
|
||||
|
||||
# hardening (systemd-analyze security mnt-persist-private.mount)
|
||||
mountConfig.AmbientCapabilities = "";
|
||||
# CAP_LEASE is probably not necessary -- does any fs user use leases?
|
||||
mountConfig.CapabilityBoundingSet = "CAP_SYS_ADMIN CAP_DAC_OVERRIDE CAP_DAC_READ_SEARCH CAP_CHOWN CAP_MKNOD CAP_LEASE CAP_SETGID CAP_SETUID CAP_FOWNER";
|
||||
mountConfig.LockPersonality = true;
|
||||
mountConfig.MemoryDenyWriteExecute = true;
|
||||
mountConfig.NoNewPrivileges = true;
|
||||
mountConfig.ProtectClock = true;
|
||||
mountConfig.ProtectHostname = true;
|
||||
mountConfig.RemoveIPC = true;
|
||||
mountConfig.RestrictAddressFamilies = "AF_UNIX"; # "none" works, but then it can't connect to the logger
|
||||
mountConfig.RestrictFileSystems = "@common-block devtmpfs fuse pipefs";
|
||||
mountConfig.RestrictNamespaces = true;
|
||||
mountConfig.RestrictNetworkInterfaces = "";
|
||||
mountConfig.RestrictRealtime = true;
|
||||
mountConfig.RestrictSUIDSGID = true;
|
||||
mountConfig.SystemCallArchitectures = "native";
|
||||
mountConfig.SystemCallFilter = [
|
||||
# unfortunately, i need to keep @network-io (accept, bind, connect, listen, recv, send, socket, ...). not sure why (daemon control socket?).
|
||||
"@system-service" "@mount" "~@cpu-emulation" "~@keyring"
|
||||
];
|
||||
mountConfig.IPAddressDeny = "any";
|
||||
mountConfig.DevicePolicy = "closed"; # only allow /dev/{null,zero,full,random,urandom}
|
||||
mountConfig.DeviceAllow = "/dev/fuse";
|
||||
mountConfig.SocketBindDeny = "any";
|
||||
}];
|
||||
|
||||
system.fsPackages = [ gocryptfs-private ];
|
||||
|
||||
sane.user.services.gocryptfs-private = {
|
||||
|
Reference in New Issue
Block a user