Add vsftpd service

This commit is contained in:
Sander van der Burg 2021-09-13 22:44:01 +02:00
parent 465225cc62
commit fc920c4556
6 changed files with 221 additions and 0 deletions

View File

@ -197,4 +197,14 @@ in
inherit createManagedProcess runtimeDir tmpDir libDir forceDisableUserChange callingUser;
inherit (pkgs) lib xinetd writeTextFile;
};
vsftpd = import ./vsftpd {
inherit createManagedProcess;
inherit (pkgs) vsftpd;
};
simpleVsftpd = import ./vsftpd/simple.nix {
inherit createManagedProcess forceDisableUserChange logDir libDir callingUser callingGroup;
inherit (pkgs) stdenv vsftpd writeTextFile lib;
};
}

View File

@ -0,0 +1,31 @@
{createManagedProcess, vsftpd}:
{instanceSuffix ? "", instanceName ? "vsftpd${instanceSuffix}", initialize ? "", configFile}:
let
user = instanceName;
group = instanceName;
in
createManagedProcess {
inherit instanceName initialize;
foregroundProcess = "${vsftpd}/bin/vsftpd";
args = [ configFile ];
credentials = {
groups = {
"${group}" = {};
};
users = {
"${user}" = {
inherit group;
description = "vsftpd user";
};
};
};
overrides = {
sysvinit = {
runlevels = [ 3 4 5 ];
};
};
}

View File

@ -0,0 +1,78 @@
{createManagedProcess, stdenv, vsftpd, writeTextFile, lib, logDir, libDir, forceDisableUserChange, callingUser, callingGroup}:
{ instanceSuffix ? ""
, instanceName ? "vsftpd${instanceSuffix}"
, dataPort ? 20
, listenPort ? dataPort + 1
, options ? {}
, enableAnonymousUser ? false
, anonymousUsername ? "ftp"
, anonymousRoot ? if forceDisableUserChange then "/home/${callingUser}" else "/home/${anonymousUsername}"
}:
let
user = instanceName;
group = instanceName;
vsftpdLogDir = "${logDir}/${instanceName}";
configFile = writeTextFile {
name = "vsftpd.conf";
text =
lib.optionalString (stdenv.isLinux) ''
seccomp_sandbox=NO
''
+
''
vsftpd_log_file=${vsftpdLogDir}/vsftpd.log
xferlog_file=${vsftpdLogDir}/xferlog
'' +
(if forceDisableUserChange then ''
run_as_launching_user=YES
ftp_username=${callingUser}
'' else ''
nopriv_user=${user}
ftp_username=${if enableAnonymousUser then anonymousUsername else "nobody"}
pam_service_name=vsftpd
secure_chroot_dir=/var/empty
'')
+ ''
ftp_data_port=${toString dataPort}
listen_port=${toString listenPort}
''
+ lib.optionalString enableAnonymousUser ''
anon_root=${anonymousRoot}
''
+ lib.concatMapStrings (name:
let
value = builtins.getAttr name options;
in
"${name}=${toString value}\n"
) (builtins.attrNames options);
};
in
import ./default.nix {
inherit createManagedProcess vsftpd;
} {
inherit instanceSuffix instanceName;
# When running as unprivileged user, we need to make a copy of the config file and make the calling user the owner
configFile = if forceDisableUserChange then "${libDir}/${instanceName}/vsftpd.conf" else configFile;
initialize =
''
mkdir -p ${vsftpdLogDir}
''
+
# Make the unprivileged user the owner of the config file
lib.optionalString forceDisableUserChange
(let
dynamicConfigFile = "${libDir}/${instanceName}/vsftpd.conf";
in
''
mkdir -p ${libDir}/${instanceName}
cp ${configFile} ${dynamicConfigFile}
chmod u+w ${dynamicConfigFile}
chown ${callingUser}:${callingGroup} ${dynamicConfigFile}
'');
}

View File

@ -104,4 +104,8 @@ in
xinetd-extendable = import ./xinetd/extendable {
inherit pkgs processManagers profiles testService nix-processmgmt;
};
vsftpd = import ./vsftpd {
inherit pkgs processManagers profiles testService nix-processmgmt;
};
}

44
tests/vsftpd/default.nix Normal file
View File

@ -0,0 +1,44 @@
{ pkgs, testService, processManagers, profiles, nix-processmgmt }:
testService {
exprFile = ./processes.nix;
extraParams = {
inherit nix-processmgmt;
};
nixosConfig = {
users.users.ftp = {
description = "Anonymous FTP user";
isNormalUser = true;
createHome = true;
password = "secret";
};
};
systemPackages = [ pkgs.inetutils ];
readiness = {instanceName, instance, ...}:
''
machine.wait_for_open_port(${toString instance.listenPort})
'';
tests = {instanceName, instance, forceDisableUserChange, ...}:
if forceDisableUserChange then ''
machine.succeed("echo test > /home/unprivileged/test.txt")
machine.succeed("chown unprivileged:users /home/unprivileged/test.txt")
machine.succeed('(echo "user anonymous foobar"; echo "ls") | ftp -n 127.0.0.1 ${toString instance.listenPort} >&2')
machine.succeed("curl --fail ftp://anonymous@localhost:${toString instance.listenPort}/test.txt -o test.txt")
machine.succeed("grep test test.txt")
machine.succeed("rm test.txt")
'' else ''
machine.succeed("echo test > /home/ftp/test.txt")
machine.succeed("chown ftp:users /home/ftp/test.txt")
machine.succeed("chmod a-w /home/ftp")
machine.succeed('(echo "user anonymous foobar"; echo "ls") | ftp -n 127.0.0.1 ${pkgs.lib.optionalString (instance.listenPort != 21) (toString instance.listenPort)} >&2')
machine.succeed("curl -v --fail ftp://anonymous@localhost${pkgs.lib.optionalString (instance.listenPort != 21) ":${toString instance.listenPort}"}/test.txt -o test.txt 2>&1")
machine.succeed("grep test test.txt")
machine.succeed("rm test.txt")
'';
inherit processManagers profiles;
}

View File

@ -0,0 +1,54 @@
{ pkgs ? import <nixpkgs> { inherit system; }
, system ? builtins.currentSystem
, stateDir ? "/var"
, runtimeDir ? "${stateDir}/run"
, logDir ? "${stateDir}/log"
, spoolDir ? "${stateDir}/spool"
, cacheDir ? "${stateDir}/cache"
, libDir ? "${stateDir}/lib"
, tmpDir ? (if stateDir == "/var" then "/tmp" else "${stateDir}/tmp")
, forceDisableUserChange ? false
, callingUser ? null
, callingGroup ? null
, processManager
, nix-processmgmt ? ../../../nix-processmgmt
}:
let
constructors = import ../../services-agnostic/constructors.nix {
inherit pkgs stateDir runtimeDir logDir tmpDir cacheDir libDir spoolDir forceDisableUserChange callingUser callingGroup processManager nix-processmgmt;
};
in
{
vsftpd = rec {
dataPort = if forceDisableUserChange then 2000 else 20;
listenPort = if forceDisableUserChange then 2001 else 21;
pkg = constructors.simpleVsftpd {
inherit dataPort listenPort;
enableAnonymousUser = true;
options = {
dual_log_enable = "YES";
local_enable = "YES";
anon_world_readable_only = "NO";
};
};
};
vsftpd-secondary = rec {
dataPort = if forceDisableUserChange then 2010 else 30;
listenPort = if forceDisableUserChange then 2011 else 31;
pkg = constructors.simpleVsftpd {
inherit dataPort listenPort;
enableAnonymousUser = true;
instanceSuffix = "-secondary";
options = {
dual_log_enable = "YES";
local_enable = "YES";
anon_world_readable_only = "NO";
};
};
};
}