From ab035dcd5b5e5d4f00787fab18eb9de546650160 Mon Sep 17 00:00:00 2001 From: Katherina Walshe-Grey Date: Sat, 28 Sep 2024 05:08:12 +0100 Subject: [PATCH] kilgharrah: set up ftp server --- hosts/kilgharrah/default.nix | 1 + hosts/kilgharrah/ftp.nix | 75 +++++++++++++++++++++++++++++++++++ secrets.nix | 1 + secrets/ftp-userDb-qenya.age | Bin 0 -> 12720 bytes 4 files changed, 77 insertions(+) create mode 100644 hosts/kilgharrah/ftp.nix create mode 100644 secrets/ftp-userDb-qenya.age diff --git a/hosts/kilgharrah/default.nix b/hosts/kilgharrah/default.nix index 75dd2ec..7740df5 100644 --- a/hosts/kilgharrah/default.nix +++ b/hosts/kilgharrah/default.nix @@ -8,6 +8,7 @@ ./networking.nix ./datasets.nix + ./ftp.nix ]; nixpkgs.hostPlatform = "x86_64-linux"; diff --git a/hosts/kilgharrah/ftp.nix b/hosts/kilgharrah/ftp.nix new file mode 100644 index 0000000..23fe390 --- /dev/null +++ b/hosts/kilgharrah/ftp.nix @@ -0,0 +1,75 @@ +{ config, lib, pkgs, ... }: + +{ + randomcat.services.zfs.datasets = { + "rpool_albion/srv" = { mountpoint = "none"; }; + "rpool_albion/srv/ftp" = { mountpoint = "/srv/ftp"; }; + }; + + age.secrets.ftp-userDb-qenya = { + # To update this, see the nixos docs for services.vsftpd.userDbPath. Note + # that the command it gives to create a userDb, if applied to an *existing* + # userDb, will *add* the entries from the source file, overwriting any + # entries with the same username but leaving other existing entries intact. + # Also note the database format does not salt hashes. + file = ../../secrets/ftp-userDb-qenya.age; + + # we have to specify this manually because pam_userdb strips the extension + path = "/etc/vsftpd/userDb.db"; + }; + + services.vsftpd = { + enable = true; + localUsers = true; + forceLocalLoginsSSL = true; + forceLocalDataSSL = true; + rsaCertFile = "${config.security.acme.certs."ftp.qenya.tel".directory}/fullchain.pem"; + rsaKeyFile = "${config.security.acme.certs."ftp.qenya.tel".directory}/key.pem"; + + enableVirtualUsers = true; + userlistDeny = false; # turn userlist from a denylist into an allowlist + userlist = [ "qenya" ]; # this is just a list of the users in the userDb + userDbPath = "/etc/vsftpd/userDb"; + + localRoot = "/srv/ftp"; + + extraConfig = '' + # nothing in the default cipher suite is enabled in modern ssl clients! + ssl_ciphers=HIGH + + # set this to something firewallable + pasv_min_port=51000 + pasv_max_port=51099 + + # don't bother with upgrading to TLS, just listen on FTPS only + implicit_ssl=YES + listen_port=990 + ''; + }; + + services.nginx = { + enable = true; + virtualHosts = { + "ftp.qenya.tel" = { + forceSSL = true; + useACMEHost = "ftp.qenya.tel"; + locations."/".return = "503"; + }; + }; + }; + + security.acme.certs = { + "ftp.qenya.tel" = { + webroot = "/var/lib/acme/acme-challenge"; + group = "acme_ftp.qenya.tel"; + }; + }; + + users.groups."acme_ftp.qenya.tel".members = [ + "vsftpd" # not configurable in the vsftpd nixos module + config.services.nginx.group + ]; + + networking.firewall.allowedTCPPorts = [ 990 80 443 ]; + networking.firewall.allowedTCPPortRanges = [{ from = 51000; to = 51099; }]; +} diff --git a/secrets.nix b/secrets.nix index d59c4e7..1db2c04 100644 --- a/secrets.nix +++ b/secrets.nix @@ -4,6 +4,7 @@ let commonKeys = keys.users.qenya; secrets = with keys; { + ftp-userDb-qenya = [ machines.kilgharrah ]; user-password-kilgharrah-qenya = [ machines.kilgharrah ]; user-password-tohru-qenya = [ machines.tohru ]; wireguard-peer-orm = [ machines.orm ]; diff --git a/secrets/ftp-userDb-qenya.age b/secrets/ftp-userDb-qenya.age new file mode 100644 index 0000000000000000000000000000000000000000..3d40119b47ab80fefd0623c655a85d5960f07551 GIT binary patch literal 12720 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