nixos/part-db: use writable attachment storage

Move mutable public media and uploads out of the Nix store and add nginx hardening for served media files.

Assisted-by: pi coding agent / Mika (OpenAI GPT-5.5)
This commit is contained in:
Gerhard Schwanzer
2026-07-08 10:11:26 +02:00
parent b8171f37b9
commit b7dcd54f64
3 changed files with 64 additions and 7 deletions
+46 -5
View File
@@ -176,18 +176,34 @@ in
root = "${pkg}/public";
locations = {
"/" = {
tryFiles = "$uri $uri/ /index.php?$query_string";
tryFiles = "$uri $uri/ /index.php$is_args$args";
index = "index.php";
extraConfig = ''
add_header Content-Security-Policy "default-src 'self'; script-src 'none'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; sandbox;" always;
add_header X-Content-Type-Options "nosniff" always;
sendfile off;
'';
};
"~ \\.php$" = {
"= /index.php" = {
extraConfig = ''
include ${config.services.nginx.package}/conf/fastcgi_params ;
fastcgi_param SCRIPT_FILENAME $request_filename;
fastcgi_param modHeadersAvailable true; #Avoid sending the security headers twice
include ${config.services.nginx.package}/conf/fastcgi_params;
fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
fastcgi_param DOCUMENT_ROOT $realpath_root;
fastcgi_param modHeadersAvailable true; # Avoid sending the security headers twice
fastcgi_pass unix:${config.services.phpfpm.pools.part-db.socket};
internal;
'';
};
"~ \\.php$" = {
return = "404";
};
"~* ^/media/.*\\.(php[3-8]?|phar|phtml|pht|phps)$" = {
return = "403";
};
"~* \\.svg$" = {
extraConfig = ''
add_header Content-Security-Policy "default-src 'self'; script-src 'none'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; frame-ancestors 'none'; sandbox;" always;
add_header X-Content-Type-Options "nosniff" always;
'';
};
};
@@ -240,6 +256,31 @@ in
lib.concatStringsSep "\n" (lib.mapAttrsToList (key: value: "${key}=\"${value}\"") cfg.settings)
)}";
};
"/var/lib/part-db/".d = {
mode = "0755";
user = "part-db";
group = "part-db";
};
"/var/lib/part-db/public/".d = {
mode = "0755";
user = "part-db";
group = "part-db";
};
"/var/lib/part-db/public/media/".d = {
mode = "0755";
user = "part-db";
group = "part-db";
};
"/var/lib/part-db/uploads/".d = {
mode = "0750";
user = "part-db";
group = "part-db";
};
"/var/lib/part-db/share/".d = {
mode = "0750";
user = "part-db";
group = "part-db";
};
"/var/log/part-db/".d = {
mode = "0750";
user = "part-db";
+13 -1
View File
@@ -1,4 +1,4 @@
{ lib, ... }:
{ lib, pkgs, ... }:
{
name = "part-db";
meta.maintainers = with lib.maintainers; [ oddlama ];
@@ -15,8 +15,20 @@
machine.wait_for_unit("part-db-migrate.service")
machine.wait_for_unit("phpfpm-part-db.service")
machine.wait_for_unit("nginx.service")
machine.succeed("test -d /var/lib/part-db/public/media")
machine.succeed("test -d /var/lib/part-db/uploads")
machine.succeed("test -d /var/lib/part-db/share")
machine.succeed("test $(readlink ${pkgs.part-db}/public/media) = /var/lib/part-db/public/media/")
machine.succeed("test $(readlink ${pkgs.part-db}/uploads) = /var/lib/part-db/uploads/")
machine.wait_for_open_port(80)
machine.succeed("curl -L --fail http://localhost | grep 'Part-DB'", timeout=10)
machine.succeed("echo static > /var/lib/part-db/public/media/static.txt")
machine.succeed("curl -I --fail http://localhost/media/static.txt | grep 'Content-Security-Policy'")
machine.succeed("curl -I --fail http://localhost/media/static.txt | grep 'X-Content-Type-Options: nosniff'")
machine.succeed("echo '<?php echo 1; ?>' > /var/lib/part-db/public/media/shell.phar")
machine.succeed("curl -I http://localhost/media/shell.phar | grep 'HTTP/1.1 403 Forbidden'")
'';
}
+5 -1
View File
@@ -11,6 +11,8 @@
envLocalPath ? "/var/lib/part-db/env.local",
cachePath ? "/var/cache/part-db/",
logPath ? "/var/log/part-db/",
mediaPath ? "/var/lib/part-db/public/media/",
uploadsPath ? "/var/lib/part-db/uploads/",
}:
let
pname = "part-db";
@@ -79,10 +81,12 @@ stdenv.mkDerivation (finalAttrs: {
mkdir $out
mv * .* $out/
rm -rf $out/var/{cache,log}
rm -rf $out/var/{cache,log} $out/public/media $out/uploads
ln -s ${envLocalPath} $out/.env.local
ln -s ${logPath} $out/var/log
ln -s ${cachePath} $out/var/cache
ln -s ${mediaPath} $out/public/media
ln -s ${uploadsPath} $out/uploads
'';
passthru.tests = { inherit (nixosTests) part-db; };