{ lib, pkgs, config, ... }: let package = pkgs.pure-ftpd.override { ldapFtpId = "immaeFtp"; }; pure-ftpd-enabled = config.myServices.ftp.pure-ftpd.enable; proftpd-enabled = config.myServices.ftp.proftpd.enable; in { options = { myServices.ftp.enable = lib.mkOption { type = lib.types.bool; default = false; description = '' Whether to enable ftp. ''; }; myServices.ftp.pure-ftpd.enable = lib.mkOption { type = lib.types.bool; default = false; description = '' Whether to enable pure-ftpd. ''; }; myServices.ftp.proftpd.enable = lib.mkOption { type = lib.types.bool; default = true; description = '' Whether to enable proftpd. ''; }; }; config = lib.mkIf config.myServices.ftp.enable { myServices.dns.zones."immae.eu".subdomains.ftp = with config.myServices.dns.helpers; ips servers.eldiron.ips.main; myServices.chatonsProperties.services.espace-de-stockage = { file.datetime = "2022-08-22T01:00:00"; service = { name = "Espace de stockage"; description = "Compte FTP/SFTP"; logo = if pure-ftpd-enabled then "https://www.pureftpd.org/project/pure-ftpd/images/favicon.png" else if proftpd-enabled then "http://proftpd.org/proftpd.png" else ""; website = "ftp.immae.eu"; status.level = "OK"; status.description = "OK"; registration."" = ["MEMBER" "CLIENT"]; registration.load = "OPEN"; install.type = "PACKAGE"; }; software = if pure-ftpd-enabled then { name = "Pure-ftpd"; website = "https://www.pureftpd.org/project/pure-ftpd/"; license.url = "https://github.com/jedisct1/pure-ftpd/blob/master/COPYING"; license.name = "MIT Licence"; version = package.version; source.url = "https://github.com/jedisct1/pure-ftpd/"; modules = "openssh"; } else if proftpd-enabled then { name = "ProFTPD"; website = "http://proftpd.org/"; license.url = "https://github.com/proftpd/proftpd/blob/master/COPYING"; license.name = "GNU General Public License v2.0"; version = pkgs.proftpd.version; source.url = "https://github.com/proftpd/proftpd/"; modules = "openssh"; } else {}; }; #myServices.chatonsProperties.services.ftp = { # file.datetime = "2022-08-22T01:00:00"; # service = { # name = "Comptes FTP"; # description = "Compte FTP/SFTP"; # logo = if pure-ftpd-enabled # then "https://www.pureftpd.org/project/pure-ftpd/images/favicon.png" # else if proftpd-enabled # then "http://proftpd.org/proftpd.png" # else ""; # website = "ftp.immae.eu"; # status.level = "OK"; # status.description = "OK"; # registration."" = ["MEMBER" "CLIENT"]; # registration.load = "OPEN"; # install.type = "PACKAGE"; # }; # software = if pure-ftpd-enabled then { # name = "Pure-ftpd"; # website = "https://www.pureftpd.org/project/pure-ftpd/"; # license.url = "https://github.com/jedisct1/pure-ftpd/blob/master/COPYING"; # license.name = "MIT Licence"; # version = package.version; # source.url = "https://github.com/jedisct1/pure-ftpd/"; # } else if proftpd-enabled then { # name = "ProFTPD"; # website = "http://proftpd.org/"; # license.url = "https://github.com/proftpd/proftpd/blob/master/COPYING"; # license.name = "GNU General Public License v2.0"; # version = pkgs.proftpd.version; # source.url = "https://github.com/proftpd/proftpd/"; # } else {}; #}; security.acme.certs."ftp" = { domain = "eldiron.immae.eu"; # FIXME: make it global extraLegoRunFlags = ["--preferred-chain" "ISRG Root X1"]; extraLegoRenewFlags = ["--preferred-chain" "ISRG Root X1"]; postRun = (lib.optionalString pure-ftpd-enabled '' systemctl restart pure-ftpd.service '') + (lib.optionalString proftpd-enabled '' systemctl restart proftpd.service ''); extraDomainNames = [ "ftp.immae.eu" ]; }; networking = { firewall = { allowedTCPPorts = [ 21 115 ]; allowedTCPPortRanges = [ { from = 40000; to = 50000; } ]; }; }; users.users.ftp = { uid = config.ids.uids.ftp; # 8 group = "ftp"; description = "Anonymous FTP user"; home = "/homeless-shelter"; extraGroups = [ "keys" ]; }; users.groups.ftp.gid = config.ids.gids.ftp; system.activationScripts.ftp = '' install -m 0755 -o ftp -g ftp -d /var/lib/ftp '' + (lib.optionalString proftpd-enabled '' install -m 0755 -o nobody -g nogroup -d /var/lib/proftpd/authorized_keys ''); secrets.keys."pure-ftpd-ldap" = lib.mkIf pure-ftpd-enabled { permissions = "0400"; user = "ftp"; group = "ftp"; text = '' LDAPServer ${config.myEnv.ftp.ldap.host} LDAPPort 389 LDAPUseTLS True LDAPBaseDN ${config.myEnv.ftp.ldap.base} LDAPBindDN ${config.myEnv.ftp.ldap.dn} LDAPBindPW ${config.myEnv.ftp.ldap.password} LDAPDefaultUID 500 LDAPForceDefaultUID False LDAPDefaultGID 100 LDAPForceDefaultGID False LDAPFilter ${config.myEnv.ftp.ldap.pure-ftpd_filter} LDAPAuthMethod BIND # Pas de possibilite de donner l'Uid/Gid ! # Compile dans pure-ftpd directement avec immaeFtpUid / immaeFtpGid LDAPHomeDir immaeFtpDirectory ''; }; secrets.keys."proftpd-ldap.conf" = lib.mkIf proftpd-enabled { permissions = "0400"; user = "ftp"; group = "ftp"; text = '' LDAPServer ldaps://${config.myEnv.ftp.ldap.host}:636/??sub LDAPUseTLS on LDAPAuthBinds on LDAPBindDN "${config.myEnv.ftp.ldap.dn}" "${config.myEnv.ftp.ldap.password}" LDAPSearchScope subtree LDAPAuthBinds on LDAPDefaultGID 100 LDAPDefaultUID 500 LDAPForceDefaultUID off LDAPForceDefaultGID off LDAPAttr gidNumber immaeFtpGid LDAPAttr uidNumber immaeFtpUid LDAPAttr homeDirectory immaeFtpDirectory LDAPUsers "${config.myEnv.ftp.ldap.base}" "${config.myEnv.ftp.ldap.proftpd_filter}" LDAPGroups "${config.myEnv.ftp.ldap.base}" ''; }; services.filesWatcher.pure-ftpd = lib.mkIf pure-ftpd-enabled { restart = true; paths = [ config.secrets.fullPaths."pure-ftpd-ldap" ]; }; services.filesWatcher.proftpd = lib.mkIf proftpd-enabled { restart = true; paths = [ config.secrets.fullPaths."proftpd-ldap.conf" ]; }; systemd.services.pure-ftpd = let configFile = pkgs.writeText "pure-ftpd.conf" '' PassivePortRange 40000 50000 Bind 42 ChrootEveryone yes CreateHomeDir yes BrokenClientsCompatibility yes MaxClientsNumber 50 Daemonize yes MaxClientsPerIP 8 VerboseLog no DisplayDotFiles yes AnonymousOnly no NoAnonymous no SyslogFacility ftp DontResolve yes MaxIdleTime 15 LDAPConfigFile ${config.secrets.fullPaths."pure-ftpd-ldap"} LimitRecursion 10000 8 AnonymousCanCreateDirs no MaxLoad 4 AntiWarez yes Umask 133:022 # ftp MinUID 8 AllowUserFXP no AllowAnonymousFXP no ProhibitDotFilesWrite no ProhibitDotFilesRead no AutoRename no AnonymousCantUpload no MaxDiskUsage 99 CustomerProof yes TLS 1 CertFile ${config.security.acme.certs.ftp.directory}/full.pem ''; in lib.mkIf pure-ftpd-enabled { description = "Pure-FTPd server"; wantedBy = [ "multi-user.target" ]; after = [ "network.target" ]; serviceConfig.ExecStart = "${package}/bin/pure-ftpd ${configFile}"; serviceConfig.Type = "forking"; serviceConfig.PIDFile = "/run/pure-ftpd.pid"; }; systemd.services.proftpd = let configFile = pkgs.writeText "proftpd.conf" '' ServerName "ProFTPD" ServerType standalone DefaultServer on Port 21 UseIPv6 on Umask 022 MaxInstances 30 MaxClients 50 MaxClientsPerHost 8 # Set the user and group under which the server will run. User ftp Group ftp CreateHome on DefaultRoot ~ AllowOverwrite on TLSEngine on TLSRequired off TLSProtocol TLSv1.1 TLSv1.2 TLSv1.3 TLSCertificateChainFile ${config.security.acme.certs.ftp.directory}/fullchain.pem TLSECCertificateFile ${config.security.acme.certs.ftp.directory}/cert.pem TLSECCertificateKeyFile ${config.security.acme.certs.ftp.directory}/key.pem TLSRenegotiate none PidFile /run/proftpd/proftpd.pid ScoreboardFile /run/proftpd/proftpd.scoreboard PassivePorts 40000 50000 #DebugLevel 10 Include ${config.secrets.fullPaths."proftpd-ldap.conf"} RequireValidShell off # Bar use of SITE CHMOD by default DenyAll Umask 022 Port 115 SFTPEngine on CreateHome on DefaultRoot ~ AllowOverwrite on SFTPHostKey /etc/ssh/ssh_host_ed25519_key SFTPHostKey /etc/ssh/ssh_host_rsa_key Include ${config.secrets.fullPaths."proftpd-ldap.conf"} RequireValidShell off SFTPAuthorizedUserKeys file:/var/lib/proftpd/authorized_keys/%u SFTPAuthMethods password publickey SFTPOptions IgnoreSFTPSetOwners AllowChrootSymlinks off ''; in lib.mkIf proftpd-enabled { description = "ProFTPD server"; wantedBy = [ "multi-user.target" ]; after = [ "network.target" ]; serviceConfig.ExecStart = "${pkgs.proftpd}/bin/proftpd -c ${configFile}"; serviceConfig.Type = "forking"; serviceConfig.PIDFile = "/run/proftpd/proftpd.pid"; serviceConfig.RuntimeDirectory = "proftpd"; }; services.cron.systemCronJobs = lib.mkIf proftpd-enabled [ "*/2 * * * * nobody ${./ftp_sync.sh}" ]; myServices.monitoring.fromMasterActivatedPlugins = [ "ftp" ]; myServices.monitoring.fromMasterObjects.service = [ { service_description = "ftp has access to database for authentication"; host_name = config.hostEnv.fqdn; use = "external-service"; check_command = "check_ftp_database"; servicegroups = "webstatus-remote-services"; _webstatus_name = "FTP"; _webstatus_url = "ftp.immae.eu"; } ]; }; }