diff --git a/nixos/modules/services/web-apps/plantuml-server.nix b/nixos/modules/services/web-apps/plantuml-server.nix index 5ebee48c3e0b..1fa69814c6c9 100644 --- a/nixos/modules/services/web-apps/plantuml-server.nix +++ b/nixos/modules/services/web-apps/plantuml-server.nix @@ -1,123 +1,110 @@ { config, lib, pkgs, ... }: -with lib; - let + inherit (lib) + literalExpression + mdDoc + mkEnableOption + mkIf + mkOption + mkPackageOptionMD + mkRemovedOptionModule + types + ; cfg = config.services.plantuml-server; in { + imports = [ + (mkRemovedOptionModule [ "services" "plantuml-server" "allowPlantumlInclude" ] "This option has been removed from PlantUML.") + ]; + options = { services.plantuml-server = { - enable = mkEnableOption (lib.mdDoc "PlantUML server"); + enable = mkEnableOption (mdDoc "PlantUML server"); - package = mkOption { - type = types.package; - default = pkgs.plantuml-server; - defaultText = literalExpression "pkgs.plantuml-server"; - description = lib.mdDoc "PlantUML server package to use"; - }; + package = mkPackageOptionMD pkgs "plantuml-server" { }; packages = { - jdk = mkOption { - type = types.package; - default = pkgs.jdk; - defaultText = literalExpression "pkgs.jdk"; - description = lib.mdDoc "JDK package to use for the server"; - }; - jetty = mkOption { - type = types.package; - default = pkgs.jetty; - defaultText = literalExpression "pkgs.jetty"; - description = lib.mdDoc "Jetty package to use for the server"; + jdk = mkPackageOptionMD pkgs "jdk" { }; + jetty = mkPackageOptionMD pkgs "jetty" { + default = "jetty_11"; + extraDescription = '' + At the time of writing (v1.2023.12), PlantUML Server does not support + Jetty versions higher than 12.x. + + Jetty 12.x has introduced major breaking changes, see + and + + ''; }; }; user = mkOption { type = types.str; default = "plantuml"; - description = lib.mdDoc "User which runs PlantUML server."; + description = mdDoc "User which runs PlantUML server."; }; group = mkOption { type = types.str; default = "plantuml"; - description = lib.mdDoc "Group which runs PlantUML server."; + description = mdDoc "Group which runs PlantUML server."; }; home = mkOption { - type = types.str; + type = types.path; default = "/var/lib/plantuml"; - description = lib.mdDoc "Home directory of the PlantUML server instance."; + description = mdDoc "Home directory of the PlantUML server instance."; }; listenHost = mkOption { type = types.str; default = "127.0.0.1"; - description = lib.mdDoc "Host to listen on."; + description = mdDoc "Host to listen on."; }; listenPort = mkOption { type = types.int; default = 8080; - description = lib.mdDoc "Port to listen on."; + description = mdDoc "Port to listen on."; }; plantumlLimitSize = mkOption { type = types.int; default = 4096; - description = lib.mdDoc "Limits image width and height."; + description = mdDoc "Limits image width and height."; }; - graphvizPackage = mkOption { - type = types.package; - default = pkgs.graphviz; - defaultText = literalExpression "pkgs.graphviz"; - description = lib.mdDoc "Package containing the dot executable."; - }; + graphvizPackage = mkPackageOptionMD pkgs "graphviz" { }; plantumlStats = mkOption { type = types.bool; default = false; - description = lib.mdDoc "Set it to on to enable statistics report (https://plantuml.com/statistics-report)."; + description = mdDoc "Set it to on to enable statistics report (https://plantuml.com/statistics-report)."; }; httpAuthorization = mkOption { type = types.nullOr types.str; default = null; - description = lib.mdDoc "When calling the proxy endpoint, the value of HTTP_AUTHORIZATION will be used to set the HTTP Authorization header."; - }; - - allowPlantumlInclude = mkOption { - type = types.bool; - default = false; - description = lib.mdDoc "Enables !include processing which can read files from the server into diagrams. Files are read relative to the current working directory."; + description = mdDoc "When calling the proxy endpoint, the value of HTTP_AUTHORIZATION will be used to set the HTTP Authorization header."; }; }; }; config = mkIf cfg.enable { - users.users.${cfg.user} = { - isSystemUser = true; - group = cfg.group; - home = cfg.home; - createHome = true; - }; - - users.groups.${cfg.group} = {}; - systemd.services.plantuml-server = { description = "PlantUML server"; wantedBy = [ "multi-user.target" ]; path = [ cfg.home ]; + environment = { PLANTUML_LIMIT_SIZE = builtins.toString cfg.plantumlLimitSize; GRAPHVIZ_DOT = "${cfg.graphvizPackage}/bin/dot"; PLANTUML_STATS = if cfg.plantumlStats then "on" else "off"; HTTP_AUTHORIZATION = cfg.httpAuthorization; - ALLOW_PLANTUML_INCLUDE = if cfg.allowPlantumlInclude then "true" else "false"; }; script = '' ${cfg.packages.jdk}/bin/java \ @@ -128,13 +115,40 @@ in jetty.http.host=${cfg.listenHost} \ jetty.http.port=${builtins.toString cfg.listenPort} ''; + serviceConfig = { User = cfg.user; Group = cfg.group; + StateDirectory = mkIf (cfg.home == "/var/lib/plantuml") "plantuml"; + StateDirectoryMode = mkIf (cfg.home == "/var/lib/plantuml") "0750"; + + # Hardening + AmbientCapabilities = [ "" ]; + CapabilityBoundingSet = [ "" ]; + DynamicUser = true; + LockPersonality = true; + NoNewPrivileges = true; + PrivateDevices = true; + PrivateNetwork = false; PrivateTmp = true; + PrivateUsers = true; + ProtectClock = true; + ProtectControlGroups = true; + ProtectHome = true; + ProtectHostname = true; + ProtectKernelLogs = true; + ProtectKernelModules = true; + ProtectKernelTunables = true; + ProtectSystem = "strict"; + RestrictAddressFamilies = [ "AF_UNIX" "AF_INET" "AF_INET6" ]; + RestrictNamespaces = true; + RestrictRealtime = true; + RestrictSUIDSGID = true; + SystemCallArchitectures = "native"; + SystemCallFilter = [ "@system-service" ]; }; }; }; - meta.maintainers = with lib.maintainers; [ truh ]; + meta.maintainers = with lib.maintainers; [ truh anthonyroussel ]; } diff --git a/nixos/tests/all-tests.nix b/nixos/tests/all-tests.nix index f44fcfcf54ab..fdd95a9b4f94 100644 --- a/nixos/tests/all-tests.nix +++ b/nixos/tests/all-tests.nix @@ -656,6 +656,7 @@ in { phylactery = handleTest ./web-apps/phylactery.nix {}; pict-rs = handleTest ./pict-rs.nix {}; pinnwand = handleTest ./pinnwand.nix {}; + plantuml-server = handleTest ./plantuml-server.nix {}; plasma-bigscreen = handleTest ./plasma-bigscreen.nix {}; plasma5 = handleTest ./plasma5.nix {}; plasma5-systemd-start = handleTest ./plasma5-systemd-start.nix {}; diff --git a/nixos/tests/plantuml-server.nix b/nixos/tests/plantuml-server.nix new file mode 100644 index 000000000000..460c30919aec --- /dev/null +++ b/nixos/tests/plantuml-server.nix @@ -0,0 +1,20 @@ +import ./make-test-python.nix ({ pkgs, lib, ... }: { + name = "plantuml-server"; + meta.maintainers = with lib.maintainers; [ anthonyroussel ]; + + nodes.machine = { pkgs, ... }: { + environment.systemPackages = [ pkgs.curl ]; + services.plantuml-server.enable = true; + }; + + testScript = '' + start_all() + + machine.wait_for_unit("plantuml-server.service") + machine.wait_for_open_port(8080) + + with subtest("Generate chart"): + chart_id = machine.succeed("curl -sSf http://localhost:8080/plantuml/coder -d 'Alice -> Bob'") + machine.succeed("curl -sSf http://localhost:8080/plantuml/txt/{}".format(chart_id)) + ''; +}) diff --git a/pkgs/servers/http/jetty/11.x.nix b/pkgs/servers/http/jetty/11.x.nix new file mode 100644 index 000000000000..3196b24d7485 --- /dev/null +++ b/pkgs/servers/http/jetty/11.x.nix @@ -0,0 +1,4 @@ +import ./common.nix { + version = "11.0.18"; + hash = "sha256-HxtO2r6YWo6+MAYUgk7dNSPDqQZoyO9t/8NdI5pPkL4="; +} diff --git a/pkgs/servers/http/jetty/12.x.nix b/pkgs/servers/http/jetty/12.x.nix new file mode 100644 index 000000000000..4dba445b6b90 --- /dev/null +++ b/pkgs/servers/http/jetty/12.x.nix @@ -0,0 +1,4 @@ +import ./common.nix { + version = "12.0.3"; + hash = "sha256-Z/jJKKzoqTPZnoFOMwbpSd/Kd1w+rXloKH+aw6aNrKs="; +} diff --git a/pkgs/servers/http/jetty/default.nix b/pkgs/servers/http/jetty/common.nix similarity index 73% rename from pkgs/servers/http/jetty/default.nix rename to pkgs/servers/http/jetty/common.nix index 1ebd33f51d6f..83adac4ddd0d 100644 --- a/pkgs/servers/http/jetty/default.nix +++ b/pkgs/servers/http/jetty/common.nix @@ -1,11 +1,15 @@ +{ version, hash }: + { lib, stdenv, fetchurl }: stdenv.mkDerivation rec { pname = "jetty"; - version = "12.0.2"; + + inherit version; + src = fetchurl { url = "mirror://maven/org/eclipse/jetty/jetty-home/${version}/jetty-home-${version}.tar.gz"; - hash = "sha256-DtlHTXjbr31RmK6ycDdiWOL7jIpbWNh0la90OnOhzvM="; + inherit hash; }; dontBuild = true; @@ -17,10 +21,10 @@ stdenv.mkDerivation rec { meta = with lib; { description = "A Web server and javax.servlet container"; - homepage = "https://www.eclipse.org/jetty/"; + homepage = "https://eclipse.dev/jetty/"; platforms = platforms.all; sourceProvenance = with sourceTypes; [ binaryBytecode ]; license = with licenses; [ asl20 epl10 ]; - maintainers = with maintainers; [ emmanuelrosa ]; + maintainers = with maintainers; [ emmanuelrosa anthonyroussel ]; }; } diff --git a/pkgs/tools/misc/plantuml-server/default.nix b/pkgs/tools/misc/plantuml-server/default.nix index 039e9acb2e8e..dc7fe1627a1c 100644 --- a/pkgs/tools/misc/plantuml-server/default.nix +++ b/pkgs/tools/misc/plantuml-server/default.nix @@ -1,4 +1,4 @@ -{ lib, stdenv, fetchurl }: +{ lib, stdenv, fetchurl, nixosTests }: let version = "1.2023.12"; @@ -17,6 +17,10 @@ stdenv.mkDerivation rec { cp "$src" "$out/webapps/plantuml.war" ''; + passthru.tests = { + inherit (nixosTests) plantuml-server; + }; + meta = with lib; { description = "A web application to generate UML diagrams on-the-fly."; homepage = "https://plantuml.com/"; diff --git a/pkgs/top-level/all-packages.nix b/pkgs/top-level/all-packages.nix index 83db067292b6..ed3b9a8c0e10 100644 --- a/pkgs/top-level/all-packages.nix +++ b/pkgs/top-level/all-packages.nix @@ -26531,7 +26531,9 @@ with pkgs; jboss_mysql_jdbc = callPackage ../servers/http/jboss/jdbc/mysql { }; - jetty = callPackage ../servers/http/jetty { }; + jetty = jetty_12; + jetty_12 = callPackage ../servers/http/jetty/12.x.nix { }; + jetty_11 = callPackage ../servers/http/jetty/11.x.nix { }; jibri = callPackage ../servers/jibri { };