aboutsummaryrefslogblamecommitdiff
path: root/nixops/modules/websites/default.nix
blob: 555e780661ab14f99dc814c47bd28b0b341af964 (plain) (tree)
1
2
3
4
5
6
7
8
9
10
                                             

                                   

                                                    




                                                                     
                                                                       
                                  
                                                             




                                                                         
                  
                                                            


                                  
                              
                      
                               








                                      
                                                                       
                                                            
















                                                                        
                             

                                                   

                  
                                                           






                                                                              
                                    
                                                       

                                                                            
    
                              
                                                             






                                                       



















                                                                                                

                                                                                                                                            


             




                    
                   
            
                
                    
                     
                     
                          
                   
                  
                   

                     
                  

                 
                     
               


                           
                     
                        

                                                                                                                                                
                        
                                

                           

                                          

                              


                                 


                                                   




















                                                     
                                                
                                                     
 
                                            
                                  
                  

                                         




                                                                     
                                        






                                                                                                          
                                                                  
         
 




                                                      
                                                     
                                                        
                                                     
                                                          
                                                     
 




                                                                                 
                                                                         
                                                                          
                                                                          
                                                                        
                                                                       
                                                                               
                                                                         
                                                                             
                                                                        

                                                                          





                                                                                   
                                                                
                                                          
 

                           

                       
                           











                                                                                 
       
 









                                                                                                                     















                                                                                          

                                           
                        







                                      
                                          



                        




                                                   
                                                                          

                                             

                                                                                               


                               

           


                                            

                                      














































































                                                                                                                                                                                   











                                                                                                      
 




                                                                            
                                                                       

                                                                             
                                                                                 


           








                                                                        



                                                   
                           



                                        





                          
                                                                                        

                                                                     

                                                                                          

                                                                      
 
                                                                               

                                                                  









                                                                                                   

    
{ lib, pkgs, config, mylibs, myconfig, ... }:
let
  cfg = config.services.myWebsites;
  www_root = "/run/current-system/webapps/_www";
  theme_root = "/run/current-system/webapps/_theme";
  makeService = name: cfg: let
    toVhost = vhostConf: {
      enableSSL = true;
      sslServerCert = "/var/lib/acme/${vhostConf.certName}/cert.pem";
      sslServerKey = "/var/lib/acme/${vhostConf.certName}/key.pem";
      sslServerChain = "/var/lib/acme/${vhostConf.certName}/chain.pem";
      logFormat = "combinedVhost";
      listen = map (ip: { inherit ip; port = 443; }) cfg.ips;
      hostName = builtins.head vhostConf.hosts;
      serverAliases = builtins.tail vhostConf.hosts or [];
      documentRoot = vhostConf.root;
      extraConfig = builtins.concatStringsSep "\n" vhostConf.extraConfig;
    };
    nosslVhost = {
      listen = map (ip: { inherit ip; port = 80; }) cfg.ips;
      hostName = "nossl.immae.eu";
      enableSSL = false;
      logFormat = "combinedVhost";
      documentRoot = www_root;
      extraConfig = ''
        <Directory ${www_root}>
          DirectoryIndex nossl.html
          AllowOverride None
          Require all granted

          RewriteEngine on
          RewriteRule ^/(.+)   /   [L]
        </Directory>
        '';
    };
    redirectVhost = { # Should go last, catchall http -> https redirect
      listen = map (ip: { inherit ip; port = 80; }) cfg.ips;
      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
      '';
    };
    fallbackVhost = toVhost { # Should go first, default choice
      certName    = "eldiron";
      hosts       = ["eldiron.immae.eu" ];
      root        = www_root;
      extraConfig = [ "DirectoryIndex index.htm" ];
    };
  in rec {
    enable = true;
    listen = map (ip: { inherit ip; port = 443; }) cfg.ips;
    stateDir = "/run/httpd_${name}";
    logPerVirtualHost = true;
    multiProcessingModule = "worker";
    adminAddr = "httpd@immae.eu";
    logFormat = "combinedVhost";
    extraModules = pkgs.lib.lists.unique (pkgs.lib.lists.flatten cfg.modules);
    extraConfig = builtins.concatStringsSep "\n" cfg.extraConfig;
    virtualHosts = [ fallbackVhost ]
      ++ lib.optionals (name == "tools") [ nosslVhost ]
      ++ (pkgs.lib.attrsets.mapAttrsToList (n: v: toVhost v) cfg.vhostConfs)
      ++ [ redirectVhost ];
  };
  makeServiceOptions = name: {
    enable = lib.mkEnableOption "enable websites in ${name}";
    ips = lib.mkOption {
      type = lib.types.listOf lib.types.string;
      default = let
        ips = myconfig.env.servers.eldiron.ips.${name};
      in
        [ips.ip4] ++ (ips.ip6 or []);
      description = "${name} ips to listen to";
    };
    modules = lib.mkOption {
      type = lib.types.listOf (lib.types.str);
      default = [];
    };
    extraConfig = lib.mkOption {
      type = lib.types.listOf (lib.types.lines);
      default = [];
    };
    vhostConfs = lib.mkOption {
      type = lib.types.attrsOf (lib.types.submodule {
        options = {
          certName = lib.mkOption { type = lib.types.string; };
          hosts    = lib.mkOption { type = lib.types.listOf lib.types.string; };
          root     = lib.mkOption { type = lib.types.nullOr lib.types.path; };
          extraConfig = lib.mkOption { type = lib.types.listOf lib.types.lines; default = []; };
        };
      });
    };
  };
  makeModules = cfg: pkgs.lib.lists.flatten (pkgs.lib.attrsets.mapAttrsToList (n: v: v.modules or []) cfg.apacheConfig);
  makeExtraConfig = cfg: (builtins.filter (x: x != null) (pkgs.lib.attrsets.mapAttrsToList (n: v: v.extraConfig or null) cfg.apacheConfig));
in
{
  imports = [
    ./chloe
    ./ludivine
    ./aten
    ./piedsjaloux
    ./connexionswing
    ./tellesflorian
    ./emilia
    ./capitaines
    ./ftp/jerome.nix
    ./ftp/nassime.nix
    ./ftp/florian.nix
    ./ftp/denisejerome.nix
    ./ftp/leila.nix
    ./ftp/papa.nix
    ./ftp/immae.nix
    ./ftp/release.nix
    ./ftp/temp.nix
    ./tools/db.nix
    ./tools/tools
    ./tools/dav
    ./tools/cloud.nix
    ./tools/git
    ./tools/mastodon.nix
    ./tools/mediagoblin.nix
    ./tools/diaspora.nix
    ./tools/ether.nix
    ./tools/peertube.nix
    # built using:
    # sed -e "s/services\.httpd/services\.httpdProd/g" .nix-defexpr/channels/nixpkgs/nixos/modules/services/web-servers/apache-httpd/default.nix
    # Removed allGranted
    # And removed users / groups
    ./apache/httpd_prod.nix
    ./apache/httpd_inte.nix
    # except for this one for users/groups
    ./apache/httpd_tools.nix
    # Adapted from base phpfpm
    ./phpfpm
  ];

  options.services.myWebsites = {
    production = makeServiceOptions "production";
    integration = makeServiceOptions "integration";
    tools = makeServiceOptions "main";

    apacheConfig = lib.mkOption {
      type = lib.types.attrsOf (lib.types.submodule {
        options = {
          modules = lib.mkOption {
            type = lib.types.listOf (lib.types.str);
            default = [];
          };
          extraConfig = lib.mkOption {
            type = lib.types.nullOr lib.types.lines;
            default = null;
          };
        };
      });
      default = {};
      description = "Extra global config";
    };

  };

  config = {
    users.users.wwwrun.extraGroups = [ "keys" ];
    networking.firewall.allowedTCPPorts = [ 80 443 ];

    nixpkgs.overlays = [ (self: super: rec {
      #openssl = self.openssl_1_1;
      php = php72;
      php72 = (super.php72.override {
        mysql.connector-c = self.mariadb;
        config.php.mysqlnd = false;
        config.php.mysqli = false;
      }).overrideAttrs(old: rec {
        # Didn't manage to build with mysqli + mysql_config connector
        configureFlags = old.configureFlags ++ [
          "--with-mysqli=shared,mysqlnd"
          ];
        # preConfigure = (old.preConfigure or "") + ''
        #   export CPPFLAGS="$CPPFLAGS -I${pkgs.mariadb}/include/mysql/server";
        #   sed -i -e 's/#include "mysqli_priv.h"/#include "mysqli_priv.h"\n#include <mysql_version.h>/' \
        #     ext/mysqli/mysqli.c ext/mysqli/mysqli_prop.c
        #   '';
      });
      phpPackages = super.php72Packages.override { inherit php; };
    }) ];

    services.myWebsites.tools.databases.enable = true;
    services.myWebsites.tools.tools.enable = true;
    services.myWebsites.tools.dav.enable = true;
    services.myWebsites.tools.cloud.enable = true;
    services.myWebsites.tools.git.enable = true;
    services.myWebsites.tools.mastodon.enable = true;
    services.myWebsites.tools.mediagoblin.enable = true;
    services.myWebsites.tools.diaspora.enable = true;
    services.myWebsites.tools.etherpad-lite.enable = true;
    services.myWebsites.tools.peertube.enable = true;

    services.myWebsites.Chloe.production.enable = cfg.production.enable;
    services.myWebsites.Ludivine.production.enable = cfg.production.enable;
    services.myWebsites.Aten.production.enable = cfg.production.enable;
    services.myWebsites.PiedsJaloux.production.enable = cfg.production.enable;
    services.myWebsites.Connexionswing.production.enable = cfg.production.enable;
    services.myWebsites.Jerome.production.enable = cfg.production.enable;
    services.myWebsites.Nassime.production.enable = cfg.production.enable;
    services.myWebsites.Florian.production.enable = cfg.production.enable;
    services.myWebsites.Leila.production.enable = cfg.production.enable;
    services.myWebsites.Papa.production.enable = cfg.production.enable;
    services.myWebsites.DeniseJerome.production.enable = cfg.production.enable;
    services.myWebsites.Emilia.production.enable = cfg.production.enable;
    services.myWebsites.Capitaines.production.enable = cfg.production.enable;
    services.myWebsites.Immae.production.enable = cfg.production.enable;
    services.myWebsites.Release.production.enable = cfg.production.enable;
    services.myWebsites.Temp.production.enable = cfg.production.enable;

    services.myWebsites.Chloe.integration.enable = cfg.integration.enable;
    services.myWebsites.Ludivine.integration.enable = cfg.integration.enable;
    services.myWebsites.Aten.integration.enable = cfg.integration.enable;
    services.myWebsites.PiedsJaloux.integration.enable = cfg.integration.enable;
    services.myWebsites.Connexionswing.integration.enable = cfg.integration.enable;
    services.myWebsites.TellesFlorian.integration.enable = true;
    services.myWebsites.Florian.integration.enable = true;

    mySecrets.keys = [{
      dest = "apache-ldap";
      user = "wwwrun";
      group = "wwwrun";
      permissions = "0400";
      text = ''
        <Macro LDAPConnect>
          <IfModule authnz_ldap_module>
            AuthLDAPURL          ldap://ldap.immae.eu:389/dc=immae,dc=eu STARTTLS
            AuthLDAPBindDN       cn=httpd,ou=services,dc=immae,dc=eu
            AuthLDAPBindPassword "${myconfig.env.httpd.ldap.password}"
            AuthType             Basic
            AuthName             "Authentification requise (Acces LDAP)"
            AuthBasicProvider    ldap
          </IfModule>
        </Macro>
        '';
    }];

    services.myWebsites.apacheConfig = {
      gzip = {
        modules = [ "deflate" "filter" ];
        extraConfig = ''
          AddOutputFilterByType DEFLATE text/html text/plain text/xml text/css text/javascript application/javascript
        '';
      };
      macros = {
        modules = [ "macro" ];
      };
      stats = {
        extraConfig = ''
          <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>
        '';
      };
      ldap = {
        modules = [ "ldap" "authnz_ldap" ];
        extraConfig = ''
          <IfModule ldap_module>
            LDAPSharedCacheSize 500000
            LDAPCacheEntries 1024
            LDAPCacheTTL 600
            LDAPOpCacheEntries 1024
            LDAPOpCacheTTL 600
          </IfModule>

          Include /var/secrets/apache-ldap
        '';
      };
      global = {
        extraConfig = ''
          ErrorDocument 500 /maintenance_immae.html
          ErrorDocument 501 /maintenance_immae.html
          ErrorDocument 502 /maintenance_immae.html
          ErrorDocument 503 /maintenance_immae.html
          ErrorDocument 504 /maintenance_immae.html
          Alias /maintenance_immae.html ${www_root}/maintenance_immae.html
          ProxyPass /maintenance_immae.html !

          AliasMatch "(.*)/googleb6d69446ff4ca3e5.html" ${www_root}/googleb6d69446ff4ca3e5.html
          <Directory ${www_root}>
            AllowOverride None
            Require all granted
          </Directory>
        '';
      };
      apaxy = {
        extraConfig = ''
          <Macro Apaxy %{folder} %{ignored}>
            Alias /theme ${theme_root}
            <Directory ${theme_root}>
              Options -Indexes
              AllowOverride None
              Require all granted
            </Directory>

            # mod_autoindex
            <Directory %{folder}>
              Options Indexes
              AllowOverride None
              Require all granted

              # Inspired from Apaxy by @adamwhitcroft

              IndexOptions +Charset=UTF-8 +FancyIndexing +IgnoreCase +FoldersFirst +XHTML +HTMLTable +SuppressRules +SuppressDescription +NameWidth=* +IconsAreLinks +ShowForbidden

              IndexHeadInsert "<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />"

              IndexIgnoreReset ON
              IndexIgnore /theme .htaccess %{ignored}

              AddIcon /theme/icons/blank.png ^^BLANKICON^^
              AddIcon /theme/icons/folder.png ^^DIRECTORY^^
              AddIcon /theme/icons/folder-home.png ..

              AddIconByType (TXT,/theme/icons/text.png) text/*
              AddIconByType (IMG,/theme/icons/image.png) image/*
              AddIconByType (SND,/theme/icons/audio.png) audio/*
              AddIconByType (VID,/theme/icons/video.png) video/*

              AddIcon /theme/icons/archive.png .7z .bz2 .cab .gz .tar
              AddIcon /theme/icons/audio.png .aac .aif .aifc .aiff .ape .au .flac .iff .m4a .mid .mp3 .mpa .ra .wav .wma .f4a .f4b .oga .ogg .xm .it .s3m .mod
              AddIcon /theme/icons/bin.png .bin .hex
              AddIcon /theme/icons/bmp.png .bmp
              AddIcon /theme/icons/c.png .c
              AddIcon /theme/icons/calc.png .xlsx .xlsm .xltx .xltm .xlam .xlr .xls .csv
              AddIcon /theme/icons/cd.png .iso
              AddIcon /theme/icons/cpp.png .cpp
              AddIcon /theme/icons/css.png .css .sass .scss
              AddIcon /theme/icons/deb.png .deb
              AddIcon /theme/icons/doc.png .doc .docx .docm .dot .dotx .dotm .log .msg .odt .pages .rtf .tex .wpd .wps
              AddIcon /theme/icons/draw.png .svg .svgz
              AddIcon /theme/icons/eps.png .ai .eps
              AddIcon /theme/icons/exe.png .exe
              AddIcon /theme/icons/gif.png .gif
              AddIcon /theme/icons/h.png .h
              AddIcon /theme/icons/html.png .html .xhtml .shtml .htm .URL .url
              AddIcon /theme/icons/ico.png .ico
              AddIcon /theme/icons/java.png .jar
              AddIcon /theme/icons/jpg.png .jpg .jpeg .jpe
              AddIcon /theme/icons/js.png .js .json
              AddIcon /theme/icons/markdown.png .md
              AddIcon /theme/icons/package.png .pkg .dmg
              AddIcon /theme/icons/pdf.png .pdf
              AddIcon /theme/icons/php.png .php .phtml
              AddIcon /theme/icons/playlist.png .m3u .m3u8 .pls .pls8
              AddIcon /theme/icons/png.png .png
              AddIcon /theme/icons/ps.png .ps
              AddIcon /theme/icons/psd.png .psd
              AddIcon /theme/icons/py.png .py
              AddIcon /theme/icons/rar.png .rar
              AddIcon /theme/icons/rb.png .rb
              AddIcon /theme/icons/rpm.png .rpm
              AddIcon /theme/icons/rss.png .rss
              AddIcon /theme/icons/script.png .bat .cmd .sh
              AddIcon /theme/icons/sql.png .sql
              AddIcon /theme/icons/tiff.png .tiff .tif
              AddIcon /theme/icons/text.png .txt .nfo
              AddIcon /theme/icons/video.png .asf .asx .avi .flv .mkv .mov .mp4 .mpg .rm .srt .swf .vob .wmv .m4v .f4v .f4p .ogv
              AddIcon /theme/icons/xml.png .xml
              AddIcon /theme/icons/zip.png .zip
              DefaultIcon /theme/icons/default.png

              HeaderName /theme/header.html
              ReadmeName /theme/footer.html
              IndexStyleSheet /theme/style.css
            </Directory>
          </Macro>
        '';
      };
      http2 = {
        modules = [ "http2" ];
        extraConfig = ''
          Protocols h2 http/1.1
        '';
      };
      customLog = {
        extraConfig = ''
          LogFormat "%v:%p %h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" combinedVhost
        '';
      };
    };

    system.activationScripts = {
      httpd = ''
        install -d -m 0755 /var/lib/acme/acme-challenge
        install -d -m 0750 -o wwwrun -g wwwrun /var/lib/php/sessions
        install -d -m 0750 -o wwwrun -g wwwrun /var/lib/php/sessions/adminer
        install -d -m 0750 -o wwwrun -g wwwrun /var/lib/php/tmp/adminer
        install -d -m 0750 -o wwwrun -g wwwrun /var/lib/php/sessions/mantisbt
        install -d -m 0750 -o wwwrun -g wwwrun /var/lib/php/sessions/davical
        install -d -m 0750 -o wwwrun -g wwwrun /var/lib/php/sessions/phpldapadmin
        '';
    };

    system.extraSystemBuilderCmds = let
      adminer = pkgs.callPackage ./commons/adminer.nix {};
    in ''
      mkdir -p $out/webapps
      ln -s ${../../www} $out/webapps/_www
      ln -s ${./apache/theme} $out/webapps/_theme
      ln -s ${adminer.webRoot} $out/webapps/${adminer.apache.webappName}
      '';

    services.myPhpfpm = {
      phpPackage = pkgs.php;
      phpOptions = ''
        session.save_path = "/var/lib/php/sessions"
        post_max_size = 20M
        ; 15 days (seconds)
        session.gc_maxlifetime = 1296000
        ; 30 days (minutes)
        session.cache_expire = 43200
        '';
      extraConfig = ''
        log_level = notice
        '';
    };

    services.httpdProd = makeService "production" config.services.myWebsites.production;
    services.myWebsites.production.modules = makeModules cfg;
    services.myWebsites.production.extraConfig = makeExtraConfig cfg;

    services.httpdInte = makeService "integration" config.services.myWebsites.integration;
    services.myWebsites.integration.modules = makeModules cfg;
    services.myWebsites.integration.extraConfig = makeExtraConfig cfg;

    services.httpdTools = makeService "tools" config.services.myWebsites.tools;
    services.myWebsites.tools.modules = makeModules cfg;
    services.myWebsites.tools.extraConfig = makeExtraConfig cfg ++
    [ ''
        RedirectMatch ^/licen[cs]es?_et_tip(ping)?$ https://www.immae.eu/licences_et_tip.html
        RedirectMatch ^/licen[cs]es?_and_tip(ping)?$ https://www.immae.eu/licenses_and_tipping.html
        RedirectMatch ^/licen[cs]es?$ https://www.immae.eu/licenses_and_tipping.html
        RedirectMatch ^/tip(ping)?$ https://www.immae.eu/licenses_and_tipping.html
        RedirectMatch ^/(mentions|mentions_legales|legal)$ https://www.immae.eu/mentions.html
        RedirectMatch ^/CGU$ https://www.immae.eu/CGU
      ''
      ]
    ;
  };
}