export: enforce a quota

This commit is contained in:
2023-09-01 03:37:33 +00:00
parent 15e09573d5
commit 4fdf74fdbe
5 changed files with 42 additions and 41 deletions

View File

@@ -5,21 +5,33 @@
./sftpgo.nix ./sftpgo.nix
]; ];
users.groups.export = {};
fileSystems."/var/export/media" = { fileSystems."/var/export/media" = {
# everything in here could be considered publicly readable (based on the viewer's legal jurisdiction) # everything in here could be considered publicly readable (based on the viewer's legal jurisdiction)
device = "/var/lib/uninsane/media"; device = "/var/lib/uninsane/media";
options = [ "rbind" ]; options = [ "rbind" ];
}; };
fileSystems."/var/export/playground" = { # fileSystems."/var/export/playground" = {
device = config.fileSystems."/mnt/persist/ext".device; # device = config.fileSystems."/mnt/persist/ext".device;
fsType = "btrfs"; # fsType = "btrfs";
options = [ # options = [
"subvol=export-playground" # "subvol=export-playground"
"compress=zstd" # "compress=zstd"
"defaults" # "defaults"
]; # ];
}; # };
# N.B.: the backing directory should be manually created here **as a btrfs subvolume** and with a quota.
# - `sudo btrfs subvolume create /mnt/persist/ext/persist/var/export/playground`
# - `sudo btrfs quota enable /mnt/persist/ext/persist/var/export/playground`
# - `sudo btrfs quota rescan -sw /mnt/persist/ext/persist/var/export/playground`
# to adjust the limits (which apply at the block layer, i.e. post-compression):
# - `sudo btrfs qgroup limit 20G /mnt/persist/ext/persist/var/export/playground`
# to query the quota/status:
# - `sudo btrfs qgroup show -re /var/export/playground`
sane.persist.sys.ext = [
{ user = "root"; group = "export"; mode = "0775"; path = "/var/export/playground"; }
];
sane.fs."/var/export/README.md" = { sane.fs."/var/export/README.md" = {
wantedBy = [ "nfs.service" "sftpgo.service" ]; wantedBy = [ "nfs.service" "sftpgo.service" ];
@@ -29,14 +41,14 @@
''; '';
}; };
# sane.fs."/var/lib/sftpgo/export/playground/README.md" = { sane.fs."/var/export/playground/README.md" = {
# wantedBy = [ "nfs.service" "sftpgo.service" ]; wantedBy = [ "nfs.service" "sftpgo.service" ];
# file.text = '' file.text = ''
# this directory is intentionally read+write by anyone. this directory is intentionally read+write by anyone.
# there are no rules, except a server-level quota: there are no rules, except a shared quota:
# - share files - share files
# - write poetry - write poetry
# - be a friendly troll - be a friendly troll
# ''; '';
# }; };
} }

View File

@@ -54,8 +54,9 @@
# - fsid: must be zero for the root export # - fsid: must be zero for the root export
# - mountpoint[=/path]: only export the directory if it's a mountpoint. used to avoid exporting failed mounts. # - mountpoint[=/path]: only export the directory if it's a mountpoint. used to avoid exporting failed mounts.
# #
# 10.0.0.0/8 to export (readonly) both to LAN (unencrypted) and wg vpn (encrypted) # 10.0.0.0/8 to export both to LAN (readonly, unencrypted) and wg vpn (read-write, encrypted)
services.nfs.server.exports = '' 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) /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
} }

View File

@@ -9,9 +9,6 @@
# #
# TODO: change umask so sftpgo-created files default to 644. # TODO: change umask so sftpgo-created files default to 644.
# - it does indeed appear that the 600 is not something sftpgo is explicitly doing. # - it does indeed appear that the 600 is not something sftpgo is explicitly doing.
#
# TODO: enforce a "quota" by placing /playground on a btrfs subvolume
# - sane.persist API could expose a `subvolume` option to make this feel natural
{ config, lib, pkgs, sane-lib, ... }: { config, lib, pkgs, sane-lib, ... }:
@@ -126,6 +123,7 @@ in
services.sftpgo = { services.sftpgo = {
enable = true; enable = true;
group = "export";
settings = { settings = {
ftpd = { ftpd = {
bindings = [ bindings = [
@@ -172,22 +170,10 @@ in
}; };
}; };
# fileSystems."/var/lib/sftpgo/export/media" = { users.users.sftpgo.extraGroups = [ "export" ];
# # everything in here could be considered publicly readable (based on the viewer's legal jurisdiction)
# device = "/var/lib/uninsane/media"; systemd.services.sftpgo.serviceConfig = {
# options = [ "rbind" ]; ReadOnlyPaths = [ "/var/export" ];
# }; ReadWritePaths = [ "/var/export/playground" ];
# sane.persist.sys.plaintext = [ };
# { user = "sftpgo"; group = "sftpgo"; path = "/var/lib/sftpgo/export/playground"; }
# ];
# sane.fs."/var/lib/sftpgo/export/playground/README.md" = {
# wantedBy = [ "sftpgo.service" ];
# file.text = ''
# this directory is intentionally read+write by anyone.
# there are no rules, except a server-level quota:
# - share files
# - write poetry
# - be a friendly troll
# '';
# };
} }

View File

@@ -44,6 +44,7 @@
sane.ids.sftpgo.gid = 2410; sane.ids.sftpgo.gid = 2410;
sane.ids.trust-dns.uid = 2411; sane.ids.trust-dns.uid = 2411;
sane.ids.trust-dns.gid = 2411; sane.ids.trust-dns.gid = 2411;
sane.ids.export.gid = 2412;
sane.ids.colin.uid = 1000; sane.ids.colin.uid = 1000;
sane.ids.guest.uid = 1100; sane.ids.guest.uid = 1100;

View File

@@ -16,6 +16,7 @@
group = "users"; group = "users";
extraGroups = [ extraGroups = [
"dialout" # required for modem access (moby) "dialout" # required for modem access (moby)
"export" # to read filesystem exports (servo)
"feedbackd" "feedbackd"
"input" # for /dev/input/<xyz>: sxmo "input" # for /dev/input/<xyz>: sxmo
"networkmanager" "networkmanager"