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 myServices.dns.zones."immae.eu".subdomains.ftp =
34 with config.myServices.dns.helpers; ips servers.eldiron.ips.main;
36 myServices.chatonsProperties.services.espace-de-stockage = {
37 file.datetime = "2022-08-22T01:00:00";
39 name = "Espace de stockage";
40 description = "Compte FTP/SFTP";
41 logo = if pure-ftpd-enabled
42 then "https://www.pureftpd.org/project/pure-ftpd/images/favicon.png"
43 else if proftpd-enabled
44 then "http://proftpd.org/proftpd.png"
46 website = "ftp.immae.eu";
48 status.description = "OK";
49 registration."" = ["MEMBER" "CLIENT"];
50 registration.load = "OPEN";
51 install.type = "PACKAGE";
53 software = if pure-ftpd-enabled then {
55 website = "https://www.pureftpd.org/project/pure-ftpd/";
56 license.url = "https://github.com/jedisct1/pure-ftpd/blob/master/COPYING";
57 license.name = "MIT Licence";
58 version = package.version;
59 source.url = "https://github.com/jedisct1/pure-ftpd/";
61 } else if proftpd-enabled then {
63 website = "http://proftpd.org/";
64 license.url = "https://github.com/proftpd/proftpd/blob/master/COPYING";
65 license.name = "GNU General Public License v2.0";
66 version = pkgs.proftpd.version;
67 source.url = "https://github.com/proftpd/proftpd/";
71 #myServices.chatonsProperties.services.ftp = {
72 # file.datetime = "2022-08-22T01:00:00";
74 # name = "Comptes FTP";
75 # description = "Compte FTP/SFTP";
76 # logo = if pure-ftpd-enabled
77 # then "https://www.pureftpd.org/project/pure-ftpd/images/favicon.png"
78 # else if proftpd-enabled
79 # then "http://proftpd.org/proftpd.png"
81 # website = "ftp.immae.eu";
82 # status.level = "OK";
83 # status.description = "OK";
84 # registration."" = ["MEMBER" "CLIENT"];
85 # registration.load = "OPEN";
86 # install.type = "PACKAGE";
88 # software = if pure-ftpd-enabled then {
90 # website = "https://www.pureftpd.org/project/pure-ftpd/";
91 # license.url = "https://github.com/jedisct1/pure-ftpd/blob/master/COPYING";
92 # license.name = "MIT Licence";
93 # version = package.version;
94 # source.url = "https://github.com/jedisct1/pure-ftpd/";
95 # } else if proftpd-enabled then {
97 # website = "http://proftpd.org/";
98 # license.url = "https://github.com/proftpd/proftpd/blob/master/COPYING";
99 # license.name = "GNU General Public License v2.0";
100 # version = pkgs.proftpd.version;
101 # source.url = "https://github.com/proftpd/proftpd/";
104 security.acme.certs."ftp" = {
105 domain = "eldiron.immae.eu";
106 # FIXME: make it global
107 extraLegoRunFlags = ["--preferred-chain" "ISRG Root X1"];
108 extraLegoRenewFlags = ["--preferred-chain" "ISRG Root X1"];
109 postRun = (lib.optionalString pure-ftpd-enabled ''
110 systemctl restart pure-ftpd.service
111 '') + (lib.optionalString proftpd-enabled ''
112 systemctl restart proftpd.service
114 extraDomainNames = [ "ftp.immae.eu" ];
119 allowedTCPPorts = [ 21 115 ];
120 allowedTCPPortRanges = [ { from = 40000; to = 50000; } ];
125 uid = config.ids.uids.ftp; # 8
127 description = "Anonymous FTP user";
128 home = "/homeless-shelter";
129 extraGroups = [ "keys" ];
132 users.groups.ftp.gid = config.ids.gids.ftp;
134 system.activationScripts.ftp = ''
135 install -m 0755 -o ftp -g ftp -d /var/lib/ftp
136 '' + (lib.optionalString proftpd-enabled ''
137 install -m 0755 -o nobody -g nogroup -d /var/lib/proftpd/authorized_keys
140 secrets.keys."pure-ftpd-ldap" = lib.mkIf pure-ftpd-enabled {
141 permissions = "0400";
145 LDAPServer ${config.myEnv.ftp.ldap.host}
148 LDAPBaseDN ${config.myEnv.ftp.ldap.base}
149 LDAPBindDN ${config.myEnv.ftp.ldap.dn}
150 LDAPBindPW ${config.myEnv.ftp.ldap.password}
152 LDAPForceDefaultUID False
154 LDAPForceDefaultGID False
155 LDAPFilter ${config.myEnv.ftp.ldap.pure-ftpd_filter}
159 # Pas de possibilite de donner l'Uid/Gid !
160 # Compile dans pure-ftpd directement avec immaeFtpUid / immaeFtpGid
161 LDAPHomeDir immaeFtpDirectory
164 secrets.keys."proftpd-ldap.conf" = lib.mkIf proftpd-enabled {
165 permissions = "0400";
169 LDAPServer ldaps://${config.myEnv.ftp.ldap.host}:636/??sub
172 LDAPBindDN "${config.myEnv.ftp.ldap.dn}" "${config.myEnv.ftp.ldap.password}"
173 LDAPSearchScope subtree
177 LDAPForceDefaultUID off
178 LDAPForceDefaultGID off
179 LDAPAttr gidNumber immaeFtpGid
180 LDAPAttr uidNumber immaeFtpUid
181 LDAPAttr homeDirectory immaeFtpDirectory
182 LDAPUsers "${config.myEnv.ftp.ldap.base}" "${config.myEnv.ftp.ldap.proftpd_filter}"
183 LDAPGroups "${config.myEnv.ftp.ldap.base}"
187 services.filesWatcher.pure-ftpd = lib.mkIf pure-ftpd-enabled {
189 paths = [ config.secrets.fullPaths."pure-ftpd-ldap" ];
191 services.filesWatcher.proftpd = lib.mkIf proftpd-enabled {
193 paths = [ config.secrets.fullPaths."proftpd-ldap.conf" ];
196 systemd.services.pure-ftpd = let
197 configFile = pkgs.writeText "pure-ftpd.conf" ''
198 PassivePortRange 40000 50000
202 BrokenClientsCompatibility yes
213 LDAPConfigFile ${config.secrets.fullPaths."pure-ftpd-ldap"}
214 LimitRecursion 10000 8
215 AnonymousCanCreateDirs no
223 ProhibitDotFilesWrite no
224 ProhibitDotFilesRead no
226 AnonymousCantUpload no
230 CertFile ${config.security.acme.certs.ftp.directory}/full.pem
232 in lib.mkIf pure-ftpd-enabled {
233 description = "Pure-FTPd server";
234 wantedBy = [ "multi-user.target" ];
235 after = [ "network.target" ];
237 serviceConfig.ExecStart = "${package}/bin/pure-ftpd ${configFile}";
238 serviceConfig.Type = "forking";
239 serviceConfig.PIDFile = "/run/pure-ftpd.pid";
242 systemd.services.proftpd = let
243 configFile = pkgs.writeText "proftpd.conf" ''
245 ServerType standalone
255 # Set the user and group under which the server will run.
266 TLSProtocol TLSv1.1 TLSv1.2 TLSv1.3
268 TLSCertificateChainFile ${config.security.acme.certs.ftp.directory}/fullchain.pem
269 TLSECCertificateFile ${config.security.acme.certs.ftp.directory}/cert.pem
270 TLSECCertificateKeyFile ${config.security.acme.certs.ftp.directory}/key.pem
272 PidFile /run/proftpd/proftpd.pid
274 ScoreboardFile /run/proftpd/proftpd.scoreboard
276 PassivePorts 40000 50000
278 Include ${config.secrets.fullPaths."proftpd-ldap.conf"}
280 RequireValidShell off
282 # Bar use of SITE CHMOD by default
287 <VirtualHost 0.0.0.0>
296 SFTPHostKey /etc/ssh/ssh_host_ed25519_key
297 SFTPHostKey /etc/ssh/ssh_host_rsa_key
298 Include ${config.secrets.fullPaths."proftpd-ldap.conf"}
299 RequireValidShell off
300 SFTPAuthorizedUserKeys file:/var/lib/proftpd/authorized_keys/%u
301 SFTPAuthMethods password publickey
303 SFTPOptions IgnoreSFTPSetOwners
304 AllowChrootSymlinks off
307 in lib.mkIf proftpd-enabled {
308 description = "ProFTPD server";
309 wantedBy = [ "multi-user.target" ];
310 after = [ "network.target" ];
312 serviceConfig.ExecStart = "${pkgs.proftpd}/bin/proftpd -c ${configFile}";
313 serviceConfig.Type = "forking";
314 serviceConfig.PIDFile = "/run/proftpd/proftpd.pid";
315 serviceConfig.RuntimeDirectory = "proftpd";
318 services.cron.systemCronJobs = lib.mkIf proftpd-enabled [
319 "*/2 * * * * nobody ${./ftp_sync.sh}"
322 myServices.monitoring.fromMasterActivatedPlugins = [ "ftp" ];
323 myServices.monitoring.fromMasterObjects.service = [
325 service_description = "ftp has access to database for authentication";
326 host_name = config.hostEnv.fqdn;
327 use = "external-service";
328 check_command = "check_ftp_database";
330 servicegroups = "webstatus-remote-services";
331 _webstatus_name = "FTP";
332 _webstatus_url = "ftp.immae.eu";