common/fs: reduce the servo ftp mounts to just one ftp mount, plus a collection of bind mounts

simpler, more reliable, and less resource intensive!
This commit is contained in:
2024-10-12 04:28:41 +00:00
parent b166de34ef
commit af7faeaafe

View File

@@ -2,36 +2,58 @@
let let
fsOpts = import ./fs-opts.nix; fsOpts = import ./fs-opts.nix;
commonOptions = fsOpts.ftp ++ fsOpts.noauto; commonOptions = fsOpts.ftp ++ fsOpts.noauto;
mountpoint = "/mnt/.servo_ftp";
systemdName = utils.escapeSystemdPath mountpoint;
device = "curlftpfs#ftp://servo-hn:/";
fsType = "fuse3";
options = commonOptions ++ [
# systemd (or maybe fuse?) swallows stderr of mount units with no obvious fix.
# instead, use this flag to log the mount output to disk
"stderr_path=/var/log/curlftpfs/servo-hn.stderr"
];
remoteServo = subdir: let remoteServo = subdir: let
mountpoint = "/mnt/servo/${subdir}"; systemdBindName = utils.escapeSystemdPath "/mnt/servo/${subdir}";
systemdName = utils.escapeSystemdPath mountpoint;
device = "curlftpfs#ftp://servo-hn:/${subdir}";
fsType = "fuse3";
options = commonOptions ++ [
# systemd (or maybe fuse?) swallows stderr of mount units with no obvious fix.
# instead, use this flag to log the mount output to disk
"stderr_path=/var/log/curlftpfs/${builtins.replaceStrings [ "/" ] [ "-" ] subdir}.stderr"
];
in { in {
fileSystems."${mountpoint}" = { # sane.fs."/mnt/servo/${subdir}".mount.bind = "/mnt/.servo_ftp/${subdir}";
systemd.mounts = [{
where = "/mnt/servo/${subdir}";
what = "/mnt/.servo_ftp/${subdir}";
options = "bind,nofail";
type = "auto";
after = [ "${systemdName}.mount" ];
upheldBy = [ "${systemdName}.mount" ]; #< start this mount whenever the underlying becomes available
bindsTo = [ "${systemdName}.mount" ]; #< stop this mount whenever the underlying disappears
}];
};
in
lib.mkMerge [
{
sane.programs.curlftpfs.enableFor.system = true;
system.fsPackages = [
config.sane.programs.curlftpfs.package
];
sane.fs."/var/log/curlftpfs".dir.acl.mode = "0777";
fileSystems."/mnt/.servo_ftp" = {
inherit device fsType options; inherit device fsType options;
noCheck = true; noCheck = true;
}; };
systemd.services.servo-ftp-reachable.unitConfig.BindsTo = [ "${systemdName}.mount" ];
systemd.mounts = [{ systemd.mounts = [{
where = mountpoint; where = mountpoint;
what = device; what = device;
type = fsType; type = fsType;
options = lib.concatStringsSep "," options; options = lib.concatStringsSep "," options;
wantedBy = [ "servo-ftp-reachable.service" ]; wantedBy = [ "default.target" ];
after = [ "servo-ftp-reachable.service" ]; after = [ "network-online.target" ];
requires = [ "servo-ftp-reachable.service" ]; requires = [ "network-online.target" ];
bindsTo = [ "servo-ftp-reachable.service" ];
#VVV patch so that when the mount fails, we start a timer to remount it. #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 # and for a disconnection after a good mount (onSuccess), restart the timer to be more aggressive
# unitConfig.OnFailure = [ "${systemdName}.timer" ]; unitConfig.OnFailure = [ "${systemdName}.timer" ];
# unitConfig.OnSuccess = [ "${systemdName}-restart-timer.target" ]; unitConfig.OnSuccess = [ "${systemdName}-restart-timer.target" ];
mountConfig.TimeoutSec = "10s"; mountConfig.TimeoutSec = "10s";
mountConfig.ExecSearchPath = [ "/run/current-system/sw/bin" ]; mountConfig.ExecSearchPath = [ "/run/current-system/sw/bin" ];
@@ -68,106 +90,37 @@ let
# mountConfig.RestrictNamespaces = true; # mountConfig.RestrictNamespaces = true;
}]; }];
}; systemd.targets."${systemdName}-restart-timer" = {
in # hack unit which, when started, stops the timer (if running), and then starts it again.
lib.mkMerge [ after = [ "${systemdName}.timer" ];
{ conflicts = [ "${systemdName}.timer" ];
sane.programs.curlftpfs.enableFor.system = true; upholds = [ "${systemdName}.timer" ];
system.fsPackages = [ unitConfig.StopWhenUnneeded = true;
config.sane.programs.curlftpfs.package };
]; systemd.timers."${systemdName}" = {
timerConfig.Unit = "${systemdName}.mount";
sane.fs."/var/log/curlftpfs".dir.acl.mode = "0777"; timerConfig.AccuracySec = "2s";
timerConfig.OnActiveSec = [
systemd.services.servo-ftp-reachable = { # try to remount at these timestamps, backing off gradually
wantedBy = [ "default.target" ]; # there seems to be an implicit mount attempt at t=0.
after = [ "network-online.target" ]; "10s"
serviceConfig.ExecSearchPath = [ "/run/current-system/sw/bin" ]; "30s"
serviceConfig.ExecStart = lib.escapeShellArgs [ "60s"
"curlftpfs" "120s"
"ftp://servo-hn:/" ];
"/dev/null" # cap the backoff to a fixed interval.
"-o" timerConfig.OnUnitActiveSec = [ "120s" ];
(lib.concatStringsSep "," ([
"exit_after_connect"
] ++ commonOptions))
];
serviceConfig.RemainAfterExit = true;
serviceConfig.Type = "oneshot";
serviceConfig.RestartOn = "always";
serviceConfig.RestartSec = "10s";
serviceConfig.RestartMaxDelaySec = "120s";
serviceConfig.RestartSteps = 3;
# hardening (systemd-analyze security mnt-servo-playground-reachable.service)
serviceConfig.AmbientCapabilities = "";
serviceConfig.CapabilityBoundingSet = "";
serviceConfig.DynamicUser = true;
serviceConfig.LockPersonality = true;
serviceConfig.MemoryDenyWriteExecute = true;
serviceConfig.NoNewPrivileges = true;
serviceConfig.PrivateDevices = true;
serviceConfig.PrivateMounts = true;
serviceConfig.PrivateTmp = true;
serviceConfig.PrivateUsers = true;
serviceConfig.ProcSubset = "all";
serviceConfig.ProtectClock = true;
serviceConfig.ProtectControlGroups = true;
serviceConfig.ProtectHome = true;
serviceConfig.ProtectKernelModules = true;
serviceConfig.ProtectProc = "invisible";
serviceConfig.ProtectSystem = "strict";
serviceConfig.RemoveIPC = true;
serviceConfig.RestrictAddressFamilies = "AF_UNIX AF_INET AF_INET6";
# serviceConfig.RestrictFileSystems = "@common-block @basic-api"; #< NOPE
serviceConfig.RestrictRealtime = true;
serviceConfig.RestrictSUIDSGID = true;
serviceConfig.SystemCallArchitectures = "native";
serviceConfig.SystemCallFilter = [
"@system-service"
"@mount"
"~@chown"
"~@cpu-emulation"
"~@keyring"
# "~@privileged" #< NOPE
"~@resources"
# could remove some more probably
];
serviceConfig.IPAddressDeny = "any";
serviceConfig.IPAddressAllow = "10.0.10.5";
serviceConfig.DevicePolicy = "closed";
# exceptions
serviceConfig.ProtectHostname = false;
serviceConfig.ProtectKernelLogs = false;
serviceConfig.ProtectKernelTunables = false;
}; };
# systemd.targets."${systemdName}-restart-timer" = {
# # hack unit which, when started, stops the timer (if running), and then starts it again.
# after = [ "${systemdName}.timer" ];
# conflicts = [ "${systemdName}.timer" ];
# upholds = [ "${systemdName}.timer" ];
# unitConfig.StopWhenUnneeded = true;
# };
# systemd.timers."${systemdName}" = {
# timerConfig.Unit = "${systemdName}.mount";
# timerConfig.AccuracySec = "2s";
# timerConfig.OnActiveSec = [
# # try to remount at these timestamps, backing off gradually
# # there seems to be an implicit mount attempt at t=0.
# "10s"
# "30s"
# "60s"
# "120s"
# ];
# # cap the backoff to a fixed interval.
# timerConfig.OnUnitActiveSec = [ "120s" ];
# };
} }
# this granularity of servo media mounts is necessary to support sandboxing:
# for flaky mounts, we can only bind the mountpoint itself into the sandbox, # this granularity of servo media mounts is necessary to support sandboxing. consider:
# so it's either this or unconditionally bind all of media/. # 1. servo offline
# 2. launch a long-running app
# 3. servo comes online
# in order for the servo mount to be propagated into the app's namespace, we need to bind
# the root mountpoint into the app namespace. if we wish to only grant the app selective access
# to servo, we must create *multiple* mountpoints: /mnt/servo/FOO directories which always exist,
# and are individually bound to /mnt/.servo_ftp/FOO as the latter becomes available.
(remoteServo "media/archive") (remoteServo "media/archive")
(remoteServo "media/Books") (remoteServo "media/Books")
(remoteServo "media/collections") (remoteServo "media/collections")