1 { lib, pkgs, config, ... }:
3 package = pkgs.pure-ftpd.override { ldapFtpId = "immaeFtp"; };
4 pure-ftpd-enabled = config.myServices.ftp.pure-ftpd.enable;
5 proftpd-enabled = config.myServices.ftp.proftpd.enable;
9 myServices.ftp.enable = lib.mkOption {
10 type = lib.types.bool;
13 Whether to enable ftp.
16 myServices.ftp.pure-ftpd.enable = lib.mkOption {
17 type = lib.types.bool;
20 Whether to enable pure-ftpd.
23 myServices.ftp.proftpd.enable = lib.mkOption {
24 type = lib.types.bool;
27 Whether to enable proftpd.
32 config = lib.mkIf config.myServices.ftp.enable {
33 services.borgBackup.profiles.global.ignoredPaths = [
35 "proftpd/authorized_keys"
37 myServices.dns.zones."immae.eu".subdomains.ftp =
38 with config.myServices.dns.helpers; ips servers.eldiron.ips.main;
40 myServices.chatonsProperties.services.espace-de-stockage = {
41 file.datetime = "2022-08-22T01:00:00";
43 name = "Espace de stockage";
44 description = "Compte FTP/SFTP";
45 logo = if pure-ftpd-enabled
46 then "https://www.pureftpd.org/project/pure-ftpd/images/favicon.png"
47 else if proftpd-enabled
48 then "http://proftpd.org/proftpd.png"
50 website = "ftp.immae.eu";
52 status.description = "OK";
53 registration."" = ["MEMBER" "CLIENT"];
54 registration.load = "OPEN";
55 install.type = "PACKAGE";
57 software = if pure-ftpd-enabled then {
59 website = "https://www.pureftpd.org/project/pure-ftpd/";
60 license.url = "https://github.com/jedisct1/pure-ftpd/blob/master/COPYING";
61 license.name = "MIT Licence";
62 version = package.version;
63 source.url = "https://github.com/jedisct1/pure-ftpd/";
65 } else if proftpd-enabled then {
67 website = "http://proftpd.org/";
68 license.url = "https://github.com/proftpd/proftpd/blob/master/COPYING";
69 license.name = "GNU General Public License v2.0";
70 version = pkgs.proftpd.version;
71 source.url = "https://github.com/proftpd/proftpd/";
75 #myServices.chatonsProperties.services.ftp = {
76 # file.datetime = "2022-08-22T01:00:00";
78 # name = "Comptes FTP";
79 # description = "Compte FTP/SFTP";
80 # logo = if pure-ftpd-enabled
81 # then "https://www.pureftpd.org/project/pure-ftpd/images/favicon.png"
82 # else if proftpd-enabled
83 # then "http://proftpd.org/proftpd.png"
85 # website = "ftp.immae.eu";
86 # status.level = "OK";
87 # status.description = "OK";
88 # registration."" = ["MEMBER" "CLIENT"];
89 # registration.load = "OPEN";
90 # install.type = "PACKAGE";
92 # software = if pure-ftpd-enabled then {
94 # website = "https://www.pureftpd.org/project/pure-ftpd/";
95 # license.url = "https://github.com/jedisct1/pure-ftpd/blob/master/COPYING";
96 # license.name = "MIT Licence";
97 # version = package.version;
98 # source.url = "https://github.com/jedisct1/pure-ftpd/";
99 # } else if proftpd-enabled then {
101 # website = "http://proftpd.org/";
102 # license.url = "https://github.com/proftpd/proftpd/blob/master/COPYING";
103 # license.name = "GNU General Public License v2.0";
104 # version = pkgs.proftpd.version;
105 # source.url = "https://github.com/proftpd/proftpd/";
108 security.acme.certs."ftp" = {
109 domain = "eldiron.immae.eu";
110 # FIXME: make it global
111 extraLegoRunFlags = ["--preferred-chain" "ISRG Root X1"];
112 extraLegoRenewFlags = ["--preferred-chain" "ISRG Root X1"];
113 postRun = (lib.optionalString pure-ftpd-enabled ''
114 systemctl restart pure-ftpd.service
115 '') + (lib.optionalString proftpd-enabled ''
116 systemctl restart proftpd.service
118 extraDomainNames = [ "ftp.immae.eu" ];
123 allowedTCPPorts = [ 21 115 ];
124 allowedTCPPortRanges = [ { from = 40000; to = 50000; } ];
129 uid = config.ids.uids.ftp; # 8
131 description = "Anonymous FTP user";
132 home = "/homeless-shelter";
133 extraGroups = [ "keys" ];
136 users.groups.ftp.gid = config.ids.gids.ftp;
138 system.activationScripts.ftp = ''
139 install -m 0755 -o ftp -g ftp -d /var/lib/ftp
140 '' + (lib.optionalString proftpd-enabled ''
141 install -m 0755 -o nobody -g nogroup -d /var/lib/proftpd/authorized_keys
144 secrets.keys."pure-ftpd-ldap" = lib.mkIf pure-ftpd-enabled {
145 permissions = "0400";
149 LDAPServer ${config.myEnv.ftp.ldap.host}
152 LDAPBaseDN ${config.myEnv.ftp.ldap.base}
153 LDAPBindDN ${config.myEnv.ftp.ldap.dn}
154 LDAPBindPW ${config.myEnv.ftp.ldap.password}
156 LDAPForceDefaultUID False
158 LDAPForceDefaultGID False
159 LDAPFilter ${config.myEnv.ftp.ldap.pure-ftpd_filter}
163 # Pas de possibilite de donner l'Uid/Gid !
164 # Compile dans pure-ftpd directement avec immaeFtpUid / immaeFtpGid
165 LDAPHomeDir immaeFtpDirectory
168 secrets.keys."proftpd-ldap.conf" = lib.mkIf proftpd-enabled {
169 permissions = "0400";
173 LDAPServer ldaps://${config.myEnv.ftp.ldap.host}:636/??sub
176 LDAPBindDN "${config.myEnv.ftp.ldap.dn}" "${config.myEnv.ftp.ldap.password}"
177 LDAPSearchScope subtree
181 LDAPForceDefaultUID off
182 LDAPForceDefaultGID off
183 LDAPAttr gidNumber immaeFtpGid
184 LDAPAttr uidNumber immaeFtpUid
185 LDAPAttr homeDirectory immaeFtpDirectory
186 LDAPUsers "${config.myEnv.ftp.ldap.base}" "${config.myEnv.ftp.ldap.proftpd_filter}"
187 LDAPGroups "${config.myEnv.ftp.ldap.base}"
191 services.filesWatcher.pure-ftpd = lib.mkIf pure-ftpd-enabled {
193 paths = [ config.secrets.fullPaths."pure-ftpd-ldap" ];
195 services.filesWatcher.proftpd = lib.mkIf proftpd-enabled {
197 paths = [ config.secrets.fullPaths."proftpd-ldap.conf" ];
200 systemd.services.pure-ftpd = let
201 configFile = pkgs.writeText "pure-ftpd.conf" ''
202 PassivePortRange 40000 50000
206 BrokenClientsCompatibility yes
217 LDAPConfigFile ${config.secrets.fullPaths."pure-ftpd-ldap"}
218 LimitRecursion 10000 8
219 AnonymousCanCreateDirs no
227 ProhibitDotFilesWrite no
228 ProhibitDotFilesRead no
230 AnonymousCantUpload no
234 CertFile ${config.security.acme.certs.ftp.directory}/full.pem
236 in lib.mkIf pure-ftpd-enabled {
237 description = "Pure-FTPd server";
238 wantedBy = [ "multi-user.target" ];
239 after = [ "network.target" ];
241 serviceConfig.ExecStart = "${package}/bin/pure-ftpd ${configFile}";
242 serviceConfig.Type = "forking";
243 serviceConfig.PIDFile = "/run/pure-ftpd.pid";
246 systemd.services.proftpd = let
247 configFile = pkgs.writeText "proftpd.conf" ''
249 ServerType standalone
259 # Set the user and group under which the server will run.
270 TLSProtocol TLSv1.1 TLSv1.2 TLSv1.3
272 TLSCertificateChainFile ${config.security.acme.certs.ftp.directory}/fullchain.pem
273 TLSECCertificateFile ${config.security.acme.certs.ftp.directory}/cert.pem
274 TLSECCertificateKeyFile ${config.security.acme.certs.ftp.directory}/key.pem
276 PidFile /run/proftpd/proftpd.pid
278 ScoreboardFile /run/proftpd/proftpd.scoreboard
280 PassivePorts 40000 50000
282 Include ${config.secrets.fullPaths."proftpd-ldap.conf"}
284 RequireValidShell off
286 # Bar use of SITE CHMOD by default
291 <VirtualHost 0.0.0.0>
300 SFTPHostKey /etc/ssh/ssh_host_ed25519_key
301 SFTPHostKey /etc/ssh/ssh_host_rsa_key
302 Include ${config.secrets.fullPaths."proftpd-ldap.conf"}
303 RequireValidShell off
304 SFTPAuthorizedUserKeys file:/var/lib/proftpd/authorized_keys/%u
305 SFTPAuthMethods password publickey
307 SFTPOptions IgnoreSFTPSetOwners
308 AllowChrootSymlinks off
311 in lib.mkIf proftpd-enabled {
312 description = "ProFTPD server";
313 wantedBy = [ "multi-user.target" ];
314 after = [ "network.target" ];
316 serviceConfig.ExecStart = "${pkgs.proftpd}/bin/proftpd -c ${configFile}";
317 serviceConfig.Type = "forking";
318 serviceConfig.PIDFile = "/run/proftpd/proftpd.pid";
319 serviceConfig.RuntimeDirectory = "proftpd";
322 services.cron.systemCronJobs = lib.mkIf proftpd-enabled [
323 "*/2 * * * * nobody ${./ftp_sync.sh}"
326 myServices.monitoring.fromMasterActivatedPlugins = [ "ftp" ];
327 myServices.monitoring.fromMasterObjects.service = [
329 service_description = "ftp has access to database for authentication";
330 host_name = config.hostEnv.fqdn;
331 use = "external-service";
332 check_command = "check_ftp_database";
334 servicegroups = "webstatus-remote-services";
335 _webstatus_name = "FTP";
336 _webstatus_url = "ftp.immae.eu";