diff --git a/configs/nixos/AGENTS.md b/configs/nixos/AGENTS.md index eaffab5..3c757c8 100644 --- a/configs/nixos/AGENTS.md +++ b/configs/nixos/AGENTS.md @@ -46,6 +46,10 @@ - `sops.secrets..owner` must name a user known on the decrypting host. Do not set the owner to a guest-only service user unless the host also declares that user. Use a host-visible owner plus a shared group mode when the guest service can read the decrypted secret file through its group. +- A read-only guest bind mount cannot contain nested writable bind mounts unless their mount-point + directories already exist inside the read-only source. + For aggregate Copyparty volumes such as `/srv` with nested `/srv/inbox`, keep the guest bind mount + writable and enforce read-only behavior through Copyparty access plus service `ReadWritePaths`. - Pikachu deploys from the monorepo checkout copied to `~/dev`. Do not use the old `~/wrk/dotfiles` path for remote rebuild commands. - Do not hand-author `hardware-configuration.nix` for a host that has not been generated on that machine. diff --git a/configs/nixos/guests/copyparty.nix b/configs/nixos/guests/copyparty.nix index ac5dffb..17de0f9 100644 --- a/configs/nixos/guests/copyparty.nix +++ b/configs/nixos/guests/copyparty.nix @@ -24,6 +24,12 @@ let description = "Copyparty access map from permission string to account names."; }; + readOnly = lib.mkOption { + type = lib.types.bool; + default = false; + description = "Serve this Copyparty volume read-only."; + }; + flags = lib.mkOption { type = lib.types.listOf lib.types.str; default = [ ]; @@ -94,7 +100,7 @@ in inherit (cfg.reverseProxy) trustedSources; }; volumes = lib.mapAttrs (_urlPath: volume: { - inherit (volume) path access flags; + inherit (volume) path access flags readOnly; }) cfg.volumes; }; diff --git a/configs/nixos/hosts/pikachu/default.nix b/configs/nixos/hosts/pikachu/default.nix index cc5b648..2853ea6 100644 --- a/configs/nixos/hosts/pikachu/default.nix +++ b/configs/nixos/hosts/pikachu/default.nix @@ -158,6 +158,12 @@ trustedSources = [ "${registry.address.${config.networking.hostName}."reverse-proxy"}/32" ]; }; volumes = { + "/" = { + hostPath = "/pikachu/data/copyparty/root"; + path = "/srv"; + access.r = [ "alexion" ]; + readOnly = true; + }; "/inbox" = { hostPath = "/pikachu/__inbox"; path = "/srv/inbox"; diff --git a/configs/nixos/modules/services/copyparty.nix b/configs/nixos/modules/services/copyparty.nix index fd04482..38854c7 100644 --- a/configs/nixos/modules/services/copyparty.nix +++ b/configs/nixos/modules/services/copyparty.nix @@ -23,6 +23,12 @@ let description = "Copyparty access map from permission string to account names."; }; + readOnly = lib.mkOption { + type = lib.types.bool; + default = false; + description = "Serve this volume read-only."; + }; + flags = lib.mkOption { type = lib.types.listOf lib.types.str; default = [ ]; @@ -196,7 +202,7 @@ in PrivateTmp = true; ProtectSystem = "strict"; ProtectHome = true; - ReadWritePaths = [ cfg.stateDir ] ++ map (volume: volume.path) (lib.attrValues cfg.volumes); + ReadWritePaths = [ cfg.stateDir ] ++ map (volume: volume.path) (lib.filter (volume: !volume.readOnly) (lib.attrValues cfg.volumes)); }; };