aboutsummaryrefslogblamecommitdiff
path: root/virtual/eldiron.nix
blob: 96efddc21c64155cd2c34f29f0b453e62a91fe33 (plain) (tree)
1
2
3
4
5
6
7
8
9





                                    
                                  

                            
                                                  
                                                           
          

      
                                                    









                                                                          




                                                       













                                                                          









                                                                          

      


                      
                                                       






















                                                                                











                                                                   
                 
               
              
                   
         

      

                                               
                           

                                                                      



                                                 

                                                                                                     
                                        
           
                                 

                                 
                                
                                  

                                           
                                  
                                     
                                
                                        
                                  











                                                                                                     

          











                                                                                                     











                                                                                                     











                                                                                                     













                                                                                                       

      




                                                             

                                                    








                                                                














                                                                                                                                                                                                                                                                                                                                                                                                                                  
                                                              
 
                       
                                                  





                                                                       

                                                                
           



                          
                                             
                                                                   
                                                                     

                                                                     

                                                               

                                                   

                                                 
                                                 
                                               


        

                                                                       
                                                                        
                                                                       


                                                                        

                                                      

                                                    
                                                    

                                                       
           



                              
                
                                                        
           
                                     











                                                                                         



                                                         


            
                           


                                           

                                                     
                                                   
           



                                                    

                                                    













                                                                                                     

      






                                                         
                      
                        
                          
                         
                                                           

                                                                 

                                               
        








                                                                                                                       
                                                                       

















                                                                                     












                                                                                            

             








                                 
                                                                                                        

             
        
            
                    


                                       
                                  

                                            
                                          
                                                   
                                                    

                                                    

                                                 

                                           

                                          
                                     
                                        
                                         



                                                                                                   
                      
                                
                                        



                                    
          
                                

                                     
                                                        
                                           
            
          
                                


                                                        
                                           
                                        

            
                                






                                                           
                                














                                                            














                                                         





                                                        
                              







                                                        














                                                        





                                                        
                                



                                                        
                                            





                                                                             




                                                
                                      












                                                                            

                               
                                                                   
                                            
















                                                                                        
                   
                   









                                           



                                                              
                

                          
                             

      
                         
                   
                

                               
                                





















                                                                     
                                                                   




                                                                   



                          


                                                                                                                                                                                                                                                                                        

                                                                          
                                                                                              
                                                                                       
                                                                                   

          

    
{
  network = {
    description = "Immae's network";
    enableRollback = true;
  };

  eldiron = { config, pkgs, ... }:
    with import ../libs.nix;
    let
        mypkgs = pkgs.callPackage ./packages.nix {
          inherit checkEnv fetchedGitPrivate fetchedGithub;
        };
    in
  {
    nixpkgs.config.packageOverrides = oldpkgs: rec {
      gitolite = oldpkgs.gitolite.overrideAttrs(old: rec {
        name = "gitolite-${version}";
        version = "3.6.10";
        src = pkgs.fetchFromGitHub {
          owner = "sitaramc";
          repo = "gitolite";
          rev = "v${version}";
          sha256 = "0p2697mn6rwm03ndlv7q137zczai82n41aplq1g006ii7f12xy8h";
        };
      });
      gitweb = oldpkgs.gitweb.overrideAttrs(old: {
        installPhase = old.installPhase + ''
          cp -r ${./packages/gitweb} $out/gitweb-theme;
          '';
      });
      postgresql = postgresql111;
      postgresql111 = oldpkgs.postgresql100.overrideAttrs(old: rec {
        passthru = old.passthru // { psqlSchema = "11.0"; };
        name = "postgresql-11.1";
        src = pkgs.fetchurl {
          url = "mirror://postgresql/source/v11.1/${name}.tar.bz2";
          sha256 = "026v0sicsh7avzi45waf8shcbhivyxmi7qgn9fd1x0vl520mx0ch";
        };
      });
      mariadb = mariadbPAM;
      mariadbPAM = oldpkgs.mariadb.overrideAttrs(old: rec {
        cmakeFlags = old.cmakeFlags ++ [ "-DWITH_AUTHENTICATION_PAM=ON" ];
        buildInputs = old.buildInputs ++ [ pkgs.pam ];
      });
      goaccess = oldpkgs.goaccess.overrideAttrs(old: rec {
        name = "goaccess-${version}";
        version = "1.3";
        src = pkgs.fetchurl {
          url = "https://tar.goaccess.io/${name}.tar.gz";
          sha256 = "16vv3pj7pbraq173wlxa89jjsd279004j4kgzlrsk1dz4if5qxwc";
        };
        configureFlags = old.configureFlags ++ [ "--enable-tcb=btree" ];
        buildInputs = old.buildInputs ++ [ pkgs.tokyocabinet pkgs.bzip2 ];
      });
    };

    networking = {
      firewall = {
        enable = true;
        allowedTCPPorts = [ 22 80 443 3306 5432 9418 ];
      };
    };

    deployment = {
      targetEnv = "hetzner";
      hetzner = {
        #robotUser = "defined in HETZNER_ROBOT_USER";
        #robotPass = "defined in HETZNER_ROBOT_PASS";
        mainIPv4 = "176.9.151.89";
        partitions = ''
          clearpart --all --initlabel --drives=sda,sdb

          part swap1 --recommended --label=swap1 --fstype=swap --ondisk=sda
          part swap2 --recommended --label=swap2 --fstype=swap --ondisk=sdb

          part raid.1 --grow --ondisk=sda
          part raid.2 --grow --ondisk=sdb

          raid / --level=1 --device=md0 --fstype=ext4 --label=root raid.1 raid.2
        '';
      };
    };

    environment.systemPackages = let
      # FIXME: move it to nextcloud
      occ = pkgs.writeScriptBin "nextcloud-occ" ''
        #! ${pkgs.stdenv.shell}
        cd ${mypkgs.nextcloud.webRoot}
        NEXTCLOUD_CONFIG_DIR="${mypkgs.nextcloud.webRoot}/config" \
          exec \
          ${config.services.phpfpm.phpPackage}/bin/php \
          -c ${config.services.phpfpm.phpPackage}/etc/php.ini \
          occ $*
        '';
    in [
      pkgs.telnet
      pkgs.htop
      pkgs.vim
      pkgs.goaccess
      occ
    ];

    # FIXME: doesn't work with httpd?
    security.acme.preliminarySelfsigned = true;
    security.acme.certs = {
      # FIXME: /!\ To create a new certificate, create it before using
      # it in httpd
      "eldiron" = {
        webroot = "/var/lib/acme/acme-challenge";
        email = "ismael@bouya.org";
        domain = "eldiron.immae.eu";
        plugins = [ "cert.pem" "chain.pem" "fullchain.pem" "full.pem" "key.pem" "account_key.json" ];
        postRun = ''
          systemctl reload httpd.service
        '';
        allowKeysForGroup = true;
        extraDomains = {
          "db-1.immae.eu" = null;
          "git.immae.eu" = null;
          "tools.immae.eu" = null;
          "connexionswing.immae.eu" = null;
          "sandetludo.immae.eu" = null;
          "cloud.immae.eu" = null;
          "ludivine.immae.eu" = null;
          "dev.aten.pro" = null;
          "piedsjaloux.immae.eu" = null;
          "chloe.immae.eu" = null;
        };
      };
      "ludivinecassal" = {
        webroot = "/var/lib/acme/acme-challenge";
        email = "ismael@bouya.org";
        domain = "ludivinecassal.com";
        plugins = [ "cert.pem" "chain.pem" "fullchain.pem" "full.pem" "key.pem" "account_key.json" ];
        postRun = ''
          systemctl reload httpd.service
        '';
        extraDomains = {
          "www.ludivinecassal.com" = null;
        };
      };
      "aten" = {
        webroot = "/var/lib/acme/acme-challenge";
        email = "ismael@bouya.org";
        domain = "aten.pro";
        plugins = [ "cert.pem" "chain.pem" "fullchain.pem" "full.pem" "key.pem" "account_key.json" ];
        postRun = ''
          systemctl reload httpd.service
        '';
        extraDomains = {
          "www.aten.pro" = null;
        };
      };
      "piedsjaloux" = {
        webroot = "/var/lib/acme/acme-challenge";
        email = "ismael@bouya.org";
        domain = "piedsjaloux.fr";
        plugins = [ "cert.pem" "chain.pem" "fullchain.pem" "full.pem" "key.pem" "account_key.json" ];
        postRun = ''
          systemctl reload httpd.service
        '';
        extraDomains = {
          "www.piedsjaloux.fr" = null;
        };
      };
      "chloe" = {
        webroot = "/var/lib/acme/acme-challenge";
        email = "ismael@bouya.org";
        domain = "osteopathe-cc.fr";
        plugins = [ "cert.pem" "chain.pem" "fullchain.pem" "full.pem" "key.pem" "account_key.json" ];
        postRun = ''
          systemctl reload httpd.service
        '';
        extraDomains = {
          "www.osteopathe-cc.fr" = null;
        };
      };
      # "connexionswing" = {
      #   webroot = "/var/lib/acme/acme-challenge";
      #   email = "ismael@bouya.org";
      #   domain = "connexionswing.com";
      #   plugins = [ "cert.pem" "chain.pem" "fullchain.pem" "full.pem" "key.pem" "account_key.json" ];
      #   postRun = ''
      #     systemctl reload httpd.service
      #   '';
      #   extraDomains = {
      #     "www.connexionswing.com" = null;
      #     "sandetludo.com" = null;
      #     "www.sandetludo.com" = null;
      #   };
      # };
    };

    services.openssh.extraConfig = ''
      AuthorizedKeysCommand     /etc/ssh/ldap_authorized_keys
      AuthorizedKeysCommandUser nobody
      '';

    users.users.wwwrun.extraGroups = [ "gitolite" ];

    users.users.gitolite.packages = let
      python-packages = python-packages: with python-packages; [
        simplejson
        urllib3
      ];
    in
      [
        (pkgs.python3.withPackages python-packages)
      ];
    # FIXME: after initial install, need to
    # (1) copy rc file (adjust gitolite_ldap_groups.sh)
    # (2) (mark old readonly and) sync repos except gitolite-admin
    #     rsync -av --exclude=gitolite-admin.git old:/var/lib/gitolite/repositories /var/lib/gitolite/
    #     chown -R gitolite:gitolite /var/lib/gitolite
    # (3) push force the gitolite-admin to new location (from external point)
    #     Don't use an existing key, it will take precedence over
    #     gitolite-admin
    # (4) su -u gitolite gitolite setup
    services.gitolite = {
      enable = true;
      # FIXME: key from ./ssh
      adminPubkey = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDXqRbiHw7QoHADNIEuo4nUT9fSOIEBMdJZH0bkQAxXyJFyCM1IMz0pxsHV0wu9tdkkr36bPEUj2aV5bkYLBN6nxcV2Y49X8bjOSCPfx3n6Own1h+NeZVBj4ZByrFmqCbTxUJIZ2bZKcWOFncML39VmWdsVhNjg0X4NBBehqXRIKr2gt3E/ESAxTYJFm0BnU0baciw9cN0bsRGqvFgf5h2P48CIAfwhVcGmPQnnAwabnosYQzRWxR0OygH5Kd8mePh6FheIRIigfXsDO8f/jdxwut8buvNIf3m5EBr3tUbTsvM+eV3M5vKGt7sk8T64DVtepTSdOOWtp+47ktsnHOMh immae@immae.eu";
    };

    services.ympd = mypkgs.ympd.config // { enable = false; };

    services.phpfpm = {
      # FIXME: move session files to separate dirs
      # /!\ phppackage is used in nextcloud configuation
      phpOptions = ''
        ; For nextcloud
        extension=${pkgs.phpPackages.redis}/lib/php/extensions/redis.so
        ; For nextcloud
        extension=${pkgs.phpPackages.apcu}/lib/php/extensions/apcu.so
        ; For nextcloud
        zend_extension=${pkgs.php}/lib/php/extensions/opcache.so
        '';
      extraConfig = ''
        log_level = notice
        '';
      poolConfigs = {
        adminer = mypkgs.adminer.phpFpm.pool;
        connexionswing_dev = mypkgs.connexionswing_dev.phpFpm.pool;
        connexionswing_prod = mypkgs.connexionswing_prod.phpFpm.pool;
        ludivinecassal_dev = mypkgs.ludivinecassal_dev.phpFpm.pool;
        ludivinecassal_prod = mypkgs.ludivinecassal_prod.phpFpm.pool;
        piedsjaloux_dev = mypkgs.piedsjaloux_dev.phpFpm.pool;
        piedsjaloux_prod = mypkgs.piedsjaloux_prod.phpFpm.pool;
        chloe_dev = mypkgs.chloe_dev.phpFpm.pool;
        chloe_prod = mypkgs.chloe_prod.phpFpm.pool;
        aten_dev = mypkgs.aten_dev.phpFpm.pool;
        aten_prod = mypkgs.aten_prod.phpFpm.pool;
        nextcloud = mypkgs.nextcloud.phpFpm.pool;
        mantisbt = mypkgs.mantisbt.phpFpm.pool;
      };
    };

    system.activationScripts = {
      connexionswing_dev  = mypkgs.connexionswing_dev.activationScript;
      connexionswing_prod = mypkgs.connexionswing_prod.activationScript;
      ludivinecassal_dev  = mypkgs.ludivinecassal_dev.activationScript;
      ludivinecassal_prod = mypkgs.ludivinecassal_prod.activationScript;
      piedsjaloux_dev     = mypkgs.piedsjaloux_dev.activationScript;
      piedsjaloux_prod    = mypkgs.piedsjaloux_prod.activationScript;
      chloe_dev  = mypkgs.chloe_dev.activationScript;
      chloe_prod = mypkgs.chloe_prod.activationScript;
      aten_dev  = mypkgs.aten_dev.activationScript;
      aten_prod = mypkgs.aten_prod.activationScript;
      nextcloud = mypkgs.nextcloud.activationScript;
      httpd = ''
        install -d -m 0755 /var/lib/acme/acme-challenge
        '';
      redis = ''
        mkdir -p /run/redis
        chown redis /run/redis
        '';
      gitolite =
        assert checkEnv "NIXOPS_GITOLITE_LDAP_PASSWORD";
        let
        gitolite_ldap_groups = wrap {
          name = "gitolite_ldap_groups.sh";
          file = ./packages/gitolite_ldap_groups.sh;
          vars = {
            LDAP_PASS = builtins.getEnv "NIXOPS_GITOLITE_LDAP_PASSWORD";
          };
          paths = [ pkgs.openldap pkgs.stdenv.shellPackage pkgs.gnugrep pkgs.coreutils ];
        };
      in {
        deps = [ "users" ];
        text = ''
          if [ -d /var/lib/gitolite ]; then
            ln -sf ${gitolite_ldap_groups} /var/lib/gitolite/gitolite_ldap_groups.sh
            chmod g+rx /var/lib/gitolite
          fi
          if [ -f /var/lib/gitolite/projects.list ]; then
            chmod g+r /var/lib/gitolite/projects.list
          fi
        '';
      };
      # FIXME: initial sync
      goaccess = ''
        mkdir -p /var/lib/goaccess
        mkdir -p /var/lib/goaccess/aten.pro
        mkdir -p /var/lib/goaccess/ludivinecassal.com
        mkdir -p /var/lib/goaccess/piedsjaloux.fr
        mkdir -p /var/lib/goaccess/osteopathe-cc.fr
        '';
    };

    environment.etc."ssh/ldap_authorized_keys" = let
      ldap_authorized_keys =
        assert checkEnv "NIXOPS_SSHD_LDAP_PASSWORD";
        wrap {
          name = "ldap_authorized_keys";
          file = ./ldap_authorized_keys.sh;
          vars = {
            LDAP_PASS = builtins.getEnv "NIXOPS_SSHD_LDAP_PASSWORD";
            GITOLITE_SHELL = "${pkgs.gitolite}/bin/gitolite-shell";
            ECHO = "${pkgs.coreutils}/bin/echo";
          };
          paths = [ pkgs.openldap pkgs.stdenv.shellPackage pkgs.gnugrep pkgs.gnused pkgs.coreutils ];
        };
    in {
      enable = true;
      mode = "0755";
      user = "root";
      source = ldap_authorized_keys;
    };

    services.gitDaemon = {
      enable = true;
      user = "gitolite";
      group = "gitolite";
      basePath = "${mypkgs.git.web.varDir}/repositories";
    };

    # FIXME: logrotate
    services.httpd = let
      withConf = domain: {
        enableSSL = true;
        sslServerCert = "/var/lib/acme/${domain}/cert.pem";
        sslServerKey = "/var/lib/acme/${domain}/key.pem";
        sslServerChain = "/var/lib/acme/${domain}/fullchain.pem";
        logFormat = "combinedVhost";
        listen = [ { ip = "*"; port = 443; } ];
      };
      apacheConfig = {
        gzip = {
          modules = [ "deflate" "filter" ];
          extraConfig = ''
            AddOutputFilterByType DEFLATE text/html text/plain text/xml text/css text/javascript application/javascript
          '';
        };
        ldap = {
          modules = [ "ldap" "authnz_ldap" ];
          extraConfig = assert checkEnv "NIXOPS_HTTP_LDAP_PASSWORD"; ''
            <IfModule ldap_module>
              LDAPSharedCacheSize 500000
              LDAPCacheEntries 1024
              LDAPCacheTTL 600
              LDAPOpCacheEntries 1024
              LDAPOpCacheTTL 600
            </IfModule>

            <Macro LDAPConnect>
              <IfModule authnz_ldap_module>
                AuthLDAPURL          ldap://ldap.immae.eu:389/dc=immae,dc=eu
                AuthLDAPBindDN       cn=httpd,ou=services,dc=immae,dc=eu
                AuthLDAPBindPassword "${builtins.getEnv "NIXOPS_HTTP_LDAP_PASSWORD"}"
                AuthType             Basic
                AuthName             "Authentification requise (Acces LDAP)"
                AuthBasicProvider    ldap
              </IfModule>
            </Macro>

            <Macro Stats %{domain}>
              Alias /awstats /var/lib/goaccess/%{domain}
              <Directory /var/lib/goaccess/%{domain}>
                DirectoryIndex index.html
                AllowOverride None
                Require all granted
              </Directory>
              <Location /awstats>
                Use LDAPConnect
                Require ldap-group cn=%{domain},ou=stats,cn=httpd,ou=services,dc=immae,dc=eu
              </Location>
            </Macro>
          '';
        };
        http2 = {
          modules = [ "http2" ];
          extraConfig = ''
            Protocols h2 http/1.1
          '';
        };
        customLog = {
          modules = [];
          extraConfig = ''
            LogFormat "%v:%p %h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" combinedVhost
          '';
        };
      };
    in rec {
      enable = true;
      logPerVirtualHost = true;
      multiProcessingModule = "worker";
      adminAddr = "httpd@immae.eu";
      logFormat = "combinedVhost";
      extraModules = pkgs.lib.lists.unique (
        mypkgs.adminer.apache.modules ++
        mypkgs.nextcloud.apache.modules ++
        mypkgs.connexionswing_dev.apache.modules ++
        mypkgs.connexionswing_prod.apache.modules ++
        mypkgs.ludivinecassal_dev.apache.modules ++
        mypkgs.ludivinecassal_prod.apache.modules ++
        mypkgs.piedsjaloux_dev.apache.modules ++
        mypkgs.piedsjaloux_prod.apache.modules ++
        mypkgs.chloe_dev.apache.modules ++
        mypkgs.chloe_prod.apache.modules ++
        mypkgs.aten_dev.apache.modules ++
        mypkgs.aten_prod.apache.modules ++
        mypkgs.ympd.apache.modules ++
        mypkgs.git.web.apache.modules ++
        mypkgs.mantisbt.apache.modules ++
        pkgs.lib.lists.flatten (pkgs.lib.attrsets.mapAttrsToList (n: v: v.modules) apacheConfig) ++
        [ "macro" ]);
      extraConfig = builtins.concatStringsSep "\n"
        (pkgs.lib.attrsets.mapAttrsToList (n: v: v.extraConfig) apacheConfig);
      virtualHosts = [
        (withConf "eldiron" // {
          hostName = "eldiron.immae.eu";
          documentRoot = ./www;
          extraConfig = ''
            DirectoryIndex index.htm
            '';
        })
        (withConf "eldiron" // {
          hostName = "db-1.immae.eu";
          documentRoot = null;
          extraConfig = builtins.concatStringsSep "\n" [
            mypkgs.adminer.apache.vhostConf
          ];
        })
        (withConf "eldiron" // {
          hostName = "tools.immae.eu";
          documentRoot = null;
          extraConfig = builtins.concatStringsSep "\n" [
            mypkgs.adminer.apache.vhostConf
            mypkgs.ympd.apache.vhostConf
          ];
        })
        (withConf "eldiron" // {
          hostName = "connexionswing.immae.eu";
          serverAliases = [ "sandetludo.immae.eu" ];
          documentRoot = mypkgs.connexionswing_dev.webRoot;
          extraConfig = builtins.concatStringsSep "\n" [
            mypkgs.connexionswing_dev.apache.vhostConf
          ];
        })
        (withConf "eldiron" // {
          hostName = "ludivine.immae.eu";
          documentRoot = mypkgs.ludivinecassal_dev.webRoot;
          extraConfig = builtins.concatStringsSep "\n" [
            mypkgs.ludivinecassal_dev.apache.vhostConf
          ];
        })
        (withConf "ludivinecassal" // {
          hostName = "ludivinecassal.com";
          serverAliases = [ "www.ludivinecassal.com" ];
          documentRoot = mypkgs.ludivinecassal_prod.webRoot;
          extraConfig = builtins.concatStringsSep "\n" [
            mypkgs.ludivinecassal_prod.apache.vhostConf
          ];
        })
        (withConf "eldiron" // {
          hostName = "piedsjaloux.immae.eu";
          documentRoot = mypkgs.piedsjaloux_dev.webRoot;
          extraConfig = builtins.concatStringsSep "\n" [
            mypkgs.piedsjaloux_dev.apache.vhostConf
          ];
        })
        (withConf "piedsjaloux" // {
          hostName = "piedsjaloux.fr";
          serverAliases = [ "www.piedsjaloux.fr" ];
          documentRoot = mypkgs.piedsjaloux_prod.webRoot;
          extraConfig = builtins.concatStringsSep "\n" [
            mypkgs.piedsjaloux_prod.apache.vhostConf
          ];
        })
        (withConf "eldiron" // {
          hostName = "chloe.immae.eu";
          documentRoot = mypkgs.chloe_dev.webRoot;
          extraConfig = builtins.concatStringsSep "\n" [
            mypkgs.chloe_dev.apache.vhostConf
          ];
        })
        (withConf "chloe" // {
          hostName = "osteopathe-cc.fr";
          serverAliases = [ "www.osteopathe-cc.fr" ];
          documentRoot = mypkgs.chloe_prod.webRoot;
          extraConfig = builtins.concatStringsSep "\n" [
            mypkgs.chloe_prod.apache.vhostConf
          ];
        })
        (withConf "eldiron" // {
          hostName = "dev.aten.pro";
          documentRoot = mypkgs.aten_dev.webRoot;
          extraConfig = builtins.concatStringsSep "\n" [
            mypkgs.aten_dev.apache.vhostConf
          ];
        })
        (withConf "aten" // {
          hostName = "aten.pro";
          serverAliases = [ "www.aten.pro" ];
          documentRoot = mypkgs.aten_prod.webRoot;
          extraConfig = builtins.concatStringsSep "\n" [
            mypkgs.aten_prod.apache.vhostConf
          ];
        })
        (withConf "eldiron" // {
          hostName = "cloud.immae.eu";
          documentRoot = mypkgs.nextcloud.webRoot;
          extraConfig = builtins.concatStringsSep "\n" [
            mypkgs.nextcloud.apache.vhostConf
          ];
        })
        (withConf "eldiron" // {
          hostName = "git.immae.eu";
          documentRoot = mypkgs.git.web.webRoot;
          extraConfig = builtins.concatStringsSep "\n" [
            mypkgs.git.web.apache.vhostConf
            mypkgs.mantisbt.apache.vhostConf
          ] + ''
            RewriteEngine on
            RewriteCond %{REQUEST_URI}       ^/releases
            RewriteRule /releases(.*)        https://release.immae.eu$1 [P,L]
            '';
        })
        { # Should go last, default fallback
          listen = [ { ip = "*"; port = 80; } ];
          hostName = "redirectSSL";
          serverAliases = [ "*" ];
          enableSSL = false;
          logFormat = "combinedVhost";
          documentRoot = "/var/lib/acme/acme-challenge";
          extraConfig = ''
            RewriteEngine on
            RewriteCond "%{REQUEST_URI}"   "!^/\.well-known"
            RewriteRule ^(.+)              https://%{HTTP_HOST}$1  [R=301]
            # To redirect in specific "VirtualHost *:80", do
            #   RedirectMatch 301 ^/((?!\.well-known.*$).*)$ https://host/$1
            # rather than rewrite
            '';
        }
        ];
    };

    security.pam.services = let
      pam_ldap = pkgs.pam_ldap;
      pam_ldap_mysql = assert checkEnv "NIXOPS_MYSQL_PAM_PASSWORD";
              pkgs.writeText "mysql.conf" ''
        host ldap.immae.eu
        base dc=immae,dc=eu
        binddn cn=mysql,cn=pam,ou=services,dc=immae,dc=eu
        bindpw ${builtins.getEnv "NIXOPS_MYSQL_PAM_PASSWORD"}
        pam_filter memberOf=cn=users,cn=mysql,cn=pam,ou=services,dc=immae,dc=eu
        '';
    in [
      {
        name = "mysql";
        text = ''
          # https://mariadb.com/kb/en/mariadb/pam-authentication-plugin/
          auth    required ${pam_ldap}/lib/security/pam_ldap.so config=${pam_ldap_mysql}
          account required ${pam_ldap}/lib/security/pam_ldap.so config=${pam_ldap_mysql}
          '';
      }
    ];

    # FIXME: backup
    # Nextcloud: 14
    services.redis = rec {
      enable = true;
      bind = "127.0.0.1";
      unixSocket = "/run/redis/redis.sock";
      extraConfig = ''
        unixsocketperm 777
        maxclients 1024
        '';
    };

    # FIXME: initial sync
    # FIXME: backup
    # FIXME: restart after pam
    # FIXME: pam access doesn’t work (because of php module)
    # FIXME: ssl
    services.mysql = rec {
      enable = true;
      package = pkgs.mariadb;
    };

    # FIXME: initial sync
    # FIXME: backup
    # FIXME: ssl
    services.postgresql = rec {
      enable = true;
      package = pkgs.postgresql;
      enableTCPIP = true;
      extraConfig = ''
        max_connections = 100
        wal_level = logical
        shared_buffers = 128MB
        max_wal_size = 1GB
        min_wal_size = 80MB
        log_timezone = 'Europe/Paris'
        datestyle = 'iso, mdy'
        timezone = 'Europe/Paris'
        lc_messages = 'en_US.UTF-8'
        lc_monetary = 'en_US.UTF-8'
        lc_numeric = 'en_US.UTF-8'
        lc_time = 'en_US.UTF-8'
        default_text_search_config = 'pg_catalog.english'
        # ssl = on
        # ssl_cert_file = '/var/lib/acme/eldiron/fullchain.pem'
        # ssl_key_file = '/var/lib/acme/eldiron/key.pem'
        '';
      authentication = ''
        local	all	postgres				ident
        local	all	all					md5
        host	all	all		samehost		md5
        host	all	all		178.33.252.96/32	md5
        host	all	all		188.165.209.148/32	md5
        #host	all	all		all			pam
      '';
    };

    services.cron = {
      enable = true;
      systemCronJobs = let
        stats = domain: conf: let
          d = pkgs.writeScriptBin "stats-${domain}" "${pkgs.gnused}/bin/sed -n '/\\['$(LC_ALL=C ${pkgs.coreutils}/bin/date -d yesterday +'%d\\/%b\\/%Y')'/ p' /var/log/httpd/access_log-${domain} | ${pkgs.goaccess}/bin/goaccess -o /var/lib/goaccess/${domain}/index.html -p ${conf}";
          in "${d}/bin/stats-${domain}";
      in [
        "5 0 * * * root ${stats "aten.pro" ./packages/aten_goaccess.conf}"
        "5 0 * * * root ${stats "ludivinecassal.com" ./packages/ludivinecassal_goaccess.conf}"
        "5 0 * * * root ${stats "piedsjaloux.fr" ./packages/piedsjaloux_goaccess.conf}"
        "5 0 * * * root ${stats "osteopathe-cc.fr" ./packages/chloe_goaccess.conf}"
        ];
    };
  };
}