diff --git a/modules/default.nix b/modules/default.nix index ea1b3d7..2c5da97 100644 --- a/modules/default.nix +++ b/modules/default.nix @@ -3,4 +3,5 @@ drone-server = ./servers/drone.nix; drone-exec-runner = ./servers/drone-exec-runner.nix; wordpress = ./web-apps/wordpress; + lychee = ./web-apps/lychee; } diff --git a/modules/web-apps/lychee/default.nix b/modules/web-apps/lychee/default.nix new file mode 100644 index 0000000..9989a1e --- /dev/null +++ b/modules/web-apps/lychee/default.nix @@ -0,0 +1,186 @@ +{ pkgs, lib, config, ... }: +let + cfg = config.services.lychee; + src = cfg.package; + envConf = cfg.settings; +in { + options.services.lychee = { + enable = lib.mkEnableOption "Whether to enable lychee"; + website = lib.mkOption { + type = lib.types.str; + default = "localhost"; + example = "www.example.com"; + }; + package = lib.mkOption { type = lib.types.path; }; + forceSSL = lib.mkOption { + type = lib.types.bool; + default = false; + description = "Whether to force SSL for the nginx virtual host"; + }; + enableACME = lib.mkOption { + type = lib.types.bool; + default = false; + description = "Whether to enableACME for the nginx virtual host"; + }; + upload_max_filesize = lib.mkOption { + type = lib.types.ints.positive; + default = 30; + description = "Max uploaded file size"; + }; + post_max_size = lib.mkOption { + type = lib.types.ints.positive; + default = 100; + description = "Max post request size"; + }; + user = lib.mkOption { + type = lib.types.str; + default = "lychee"; + description = "The user that will operate on mutable files"; + }; + settings = lib.mkOption { + default = { }; + type = lib.types.submodule { + freeformType = with lib.types; attrsOf str; + options = { + APP_URL = lib.mkOption { + type = lib.types.str; + default = "http://${cfg.website}"; + }; + DB_CONNECTION = lib.mkOption { + type = lib.types.str; + default = "sqlite"; + }; + DB_LOG_SQL = lib.mkOption { + type = lib.types.str; + default = ''"false"''; + }; + CACHE_DRIVER = lib.mkOption { + type = lib.types.str; + default = "file"; + }; + SESSION_DRIVER = lib.mkOption { + type = lib.types.str; + default = "file"; + }; + SESSION_LIFETIME = lib.mkOption { + type = lib.types.str; + default = "120"; + }; + SECURITY_HEADER_HSTS_ENABLE = lib.mkOption { + type = lib.types.str; + default = ''"false"''; + }; + SESSION_SECURE_COOKIE = lib.mkOption { + type = lib.types.str; + default = ''"false"''; + }; + REDIS_PASSWORD = lib.mkOption { + type = lib.types.str; + default = ''"null"''; + }; + REDIS_PORT = lib.mkOption { + type = lib.types.str; + default = "6379"; + }; + MAIL_DRIVER = lib.mkOption { + type = lib.types.str; + default = "smtp"; + }; + }; + }; + }; + }; + config = lib.mkIf cfg.enable { + services.nginx = { + enable = true; + virtualHosts.${cfg.website} = { + root = "/var/lib/lychee/public/"; + forceSSL = lib.mkDefault cfg.forceSSL; + enableACME = lib.mkDefault cfg.enableACME; + locations = { + "^~ /index.php" = { + fastcgiParams = { + SCRIPT_FILENAME = "$document_root$fastcgi_script_name"; + }; + extraConfig = '' + fastcgi_split_path_info ^(.+?\.php)(/.*)$; + fastcgi_pass unix:${ + config.services.phpfpm.pools."${cfg.website}".socket + }; + fastcgi_index index.php; + client_max_body_size ${ + builtins.toString cfg.upload_max_filesize + }M; + ''; + }; + "~ [^/].php(/|$)" = { return = "403"; }; + }; + extraConfig = '' + index index.php; + if (!-e $request_filename) + { + rewrite ^/(.*)$ /index.php?/$1 last; + break; + } + ''; + }; + }; + systemd.services."lychee-install" = { + wantedBy = [ "phpfpm-${cfg.website}.service" ]; + before = [ "phpfpm-${cfg.website}.service" ]; + script = let rsync = pkgs.rsync; + in '' + ${rsync}/bin/rsync -a --ignore-existing ${src}/ $STATE_DIRECTORY + chmod u+w $STATE_DIRECTORY/ + chmod u+w $STATE_DIRECTORY/.env + chmod u+w $STATE_DIRECTORY/database/ + chmod u+w $STATE_DIRECTORY/database/database.sqlite + chmod -R u+w $STATE_DIRECTORY/storage/ + chmod -R u+w $STATE_DIRECTORY/public/ + chmod -R u+w $STATE_DIRECTORY/bootstrap/cache/ + ''; + serviceConfig = { + Type = "oneshot"; + StateDirectory = "lychee"; + User = cfg.user; + Restart = "on-failure"; + ProtectHome = true; + ProtectSystem = "strict"; + PrivateTmp = true; + PrivateDevices = true; + ProtectHostname = true; + ProtectClock = true; + ProtectKernelTunables = true; + ProtectKernelModules = true; + ProtectKernelLogs = true; + ProtectControlGroups = true; + NoNewPrivileges = true; + RestrictRealtime = true; + RestrictSUIDSGID = true; + RemoveIPC = true; + PrivateMounts = true; + }; + }; + services.phpfpm.pools.${cfg.website} = { + user = cfg.user; + phpPackage = pkgs.php81.withExtensions ({ enabled, all }: + enabled ++ [ all.imagick all.bcmath all.mbstring all.gd ]); + phpOptions = '' + upload_max_filesize = ${builtins.toString cfg.upload_max_filesize}M + post_max_size = ${builtins.toString cfg.post_max_size}M + ''; + settings = { + "listen.owner" = config.services.nginx.user; + "listen.group" = config.services.nginx.group; + }; + phpEnv = { "PATH" = lib.makeBinPath [ pkgs.ffmpeg ]; } // envConf; + }; + users.users.${cfg.user} = { + isSystemUser = lib.mkDefault true; + home = lib.mkDefault src; + group = lib.mkDefault cfg.user; + }; + users.groups.${cfg.user} = { }; + }; +} + diff --git a/modules/web-apps/lychee/test.nix b/modules/web-apps/lychee/test.nix new file mode 100644 index 0000000..65a489d --- /dev/null +++ b/modules/web-apps/lychee/test.nix @@ -0,0 +1,31 @@ +{ pkgs ? import { }, myPkgs ? import ../.. { } }: +pkgs.nixosTest ({ + # NixOS tests are run inside a virtual machine, and here we specify system of the machine. + nodes = { + server = { config, pkgs, ... }: { + imports = [ myPkgs.modules.lychee ]; + + security.acme.acceptTerms = true; + security.acme.defaults.email = "test@test.fr"; + services.lychee = { + enable = true; + package = myPkgs.lychee-gallery; + forceSSL = true; + enableACME = true; + }; + environment.systemPackages = [ pkgs.w3m ]; + + users = { + mutableUsers = false; + users = { + # For ease of debugging the VM as the `root` user + root.password = ""; + }; + }; + }; + }; + testScript = '' + start_all() + server.wait_for_unit("phpfpm-localhost.service") + ''; +}) diff --git a/pkgs/default.nix b/pkgs/default.nix index 862bbb9..fb72abd 100644 --- a/pkgs/default.nix +++ b/pkgs/default.nix @@ -7,8 +7,9 @@ let subsetArgs = builtins.listToAttrs [{ name = attrName; value = mergedSubset; }]; in callPackage f (subsetArgs // extraArgs); - self = rec { - acme-dns = callPackage ./servers/acme-dns.nix {}; - }; + self = rec { + acme-dns = callPackage ./servers/acme-dns.nix {}; + lychee-gallery = callPackage ./web-apps/lychee-gallery.nix {}; + }; in self diff --git a/pkgs/web-apps/lychee-gallery.nix b/pkgs/web-apps/lychee-gallery.nix new file mode 100644 index 0000000..212fc43 --- /dev/null +++ b/pkgs/web-apps/lychee-gallery.nix @@ -0,0 +1,15 @@ +{ stdenv, fetchzip, pkgs, env ? {} }: +stdenv.mkDerivation rec { + pname = "Lychee"; + version = "4.6.2"; + src = fetchzip { + url = "https://github.com/LycheeOrg/Lychee/releases/download/v${version}/Lychee.zip"; + sha256 = "sha256-dNujUTGaxvc6uZgyanNh9kIzRqfFA9yFhAtexu1sVc4="; + }; + installPhase = '' + shopt -s dotglob + mkdir $out + mv .env.example .env + mv * $out/ + ''; +}