]> git.immae.eu Git - perso/Immae/Config/Nix.git/blobdiff - systems/eldiron/ftp.nix
Squash changes containing private information
[perso/Immae/Config/Nix.git] / systems / eldiron / ftp.nix
diff --git a/systems/eldiron/ftp.nix b/systems/eldiron/ftp.nix
new file mode 100644 (file)
index 0000000..6aa1afc
--- /dev/null
@@ -0,0 +1,339 @@
+{ 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";
+      }
+
+    ];
+
+  };
+
+}