From 357b6ef06e41b4e79e74120caeedbab27cda0fea Mon Sep 17 00:00:00 2001 From: Colin Date: Fri, 1 Sep 2023 10:08:29 +0000 Subject: [PATCH] nfs: expose playground as a read/write dir --- .../by-name/servo/services/export/default.nix | 3 +- hosts/by-name/servo/services/export/nfs.nix | 58 +++++++++++++++++-- hosts/common/ids.nix | 1 + 3 files changed, 55 insertions(+), 7 deletions(-) diff --git a/hosts/by-name/servo/services/export/default.nix b/hosts/by-name/servo/services/export/default.nix index 2401e011..bab437a3 100644 --- a/hosts/by-name/servo/services/export/default.nix +++ b/hosts/by-name/servo/services/export/default.nix @@ -44,8 +44,7 @@ sane.fs."/var/export/playground/README.md" = { wantedBy = [ "nfs.service" "sftpgo.service" ]; file.text = '' - this directory is intentionally read+write by anyone. - there are no rules, except a shared quota: + this directory is intentionally read+write by anyone with access (i.e. on the LAN). - share files - write poetry - be a friendly troll diff --git a/hosts/by-name/servo/services/export/nfs.nix b/hosts/by-name/servo/services/export/nfs.nix index 837ce45b..fde0ef19 100644 --- a/hosts/by-name/servo/services/export/nfs.nix +++ b/hosts/by-name/servo/services/export/nfs.nix @@ -1,8 +1,20 @@ # docs: # - # - +# system files: +# - /etc/exports +# system services: +# - nfs-server.service +# - nfs-idmapd.service +# - nfs-mountd.service +# - nfsdcld.service +# - rpc-statd.service +# - rpcbind.service +# +# TODO: force files to be 755, or 750. +# - could maybe be done with some mount option? -{ ... }: +{ config, lib, ... }: { services.nfs.server.enable = true; @@ -52,11 +64,47 @@ # - no_root_squash, root_squash (default): map requests from uid 0 to user `nobody`. # - crossmnt: reveal filesystems that are mounted under this endpoint # - fsid: must be zero for the root export + # - fsid=root is alias for fsid=0 # - mountpoint[=/path]: only export the directory if it's a mountpoint. used to avoid exporting failed mounts. + # - all_squash: rewrite all client requests such that they come from anonuid/anongid + # - any files a user creates are owned by local anonuid/anongid. + # - users can read any local file which anonuid/anongid would be able to read. + # - users can't chown to/away from anonuid/anongid. + # - users can chmod files they own, to anything (making them unreadable to non-`nfsuser` export users, like FTP). + # - `stat` remains unchanged, returning the real UIDs/GIDs to the client. + # - thus programs which check `uid` or `gid` before trying an operation may incorrectly conclude they can't perform some op. # # 10.0.0.0/8 to export both to LAN (readonly, unencrypted) and wg vpn (read-write, encrypted) - services.nfs.server.exports = '' - /var/export 10.78.79.0/22(ro,crossmnt,fsid=0,subtree_check) 10.0.10.0/24(rw,no_root_squash,crossmnt,fsid=0,subtree_check) - ''; - # TODO: export playground as read-write to LAN, with forced UID/GID mapping to nfsguest/export + services.nfs.server.exports = + let + fmtExport = { export, baseOpts, extraLanOpts ? [], extraVpnOpts ? [] }: + let + always = [ "subtree_check" ]; + lanOpts = always ++ baseOpts ++ extraLanOpts; + vpnOpts = always ++ baseOpts ++ extraVpnOpts; + in "${export} 10.78.79.0/22(${lib.concatStringsSep "," lanOpts}) 10.0.10.0/24(${lib.concatStringsSep "," vpnOpts})"; + in lib.concatStringsSep "\n" [ + (fmtExport { + export = "/var/export"; + baseOpts = [ "crossmnt" "fsid=root" ]; + extraLanOpts = [ "ro" ]; + extraVpnOpts = [ "rw" "no_root_squash" ]; + }) + (fmtExport { + export = "/var/export/playground"; + baseOpts = [ + "mountpoint" + "all_squash" + "rw" + "anonuid=${builtins.toString config.users.users.nfsuser.uid}" + "anongid=${builtins.toString config.users.groups.export.gid}" + ]; + }) + ]; + + users.users.nfsuser = { + description = "virtual user for anonymous NFS operations"; + group = "export"; + isSystemUser = true; + }; } diff --git a/hosts/common/ids.nix b/hosts/common/ids.nix index 6d730984..5172e073 100644 --- a/hosts/common/ids.nix +++ b/hosts/common/ids.nix @@ -45,6 +45,7 @@ sane.ids.trust-dns.uid = 2411; sane.ids.trust-dns.gid = 2411; sane.ids.export.gid = 2412; + sane.ids.nfsuser.uid = 2413; sane.ids.colin.uid = 1000; sane.ids.guest.uid = 1100;