+{ 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
+ <Limit SITE_CHMOD>
+ DenyAll
+ </Limit>
+
+ <VirtualHost 0.0.0.0>
+ 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
+ </VirtualHost>
+ '';
+ 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";
+ }
+
+ ];
+
+ };
+
+}