1{
2 config,
3 lib,
4 pkgs,
5 ...
6}:
7
8let
9 inherit (lib)
10 literalExpression
11 mkEnableOption
12 mkIf
13 mkOption
14 mkPackageOption
15 mkRemovedOptionModule
16 types
17 ;
18
19 cfg = config.services.plantuml-server;
20
21in
22
23{
24 imports = [
25 (mkRemovedOptionModule [
26 "services"
27 "plantuml-server"
28 "allowPlantumlInclude"
29 ] "This option has been removed from PlantUML.")
30 ];
31
32 options = {
33 services.plantuml-server = {
34 enable = mkEnableOption "PlantUML server";
35
36 package = mkPackageOption pkgs "plantuml-server" { };
37
38 packages = {
39 jdk = mkPackageOption pkgs "jdk" { };
40 jetty = mkPackageOption pkgs "jetty" {
41 default = [ "jetty_11" ];
42 extraDescription = ''
43 At the time of writing (v1.2023.12), PlantUML Server does not support
44 Jetty versions higher than 12.x.
45
46 Jetty 12.x has introduced major breaking changes, see
47 <https://github.com/jetty/jetty.project/releases/tag/jetty-12.0.0> and
48 <https://eclipse.dev/jetty/documentation/jetty-12/programming-guide/index.html#pg-migration-11-to-12>
49 '';
50 };
51 };
52
53 user = mkOption {
54 type = types.str;
55 default = "plantuml";
56 description = "User which runs PlantUML server.";
57 };
58
59 group = mkOption {
60 type = types.str;
61 default = "plantuml";
62 description = "Group which runs PlantUML server.";
63 };
64
65 home = mkOption {
66 type = types.path;
67 default = "/var/lib/plantuml";
68 description = "Home directory of the PlantUML server instance.";
69 };
70
71 listenHost = mkOption {
72 type = types.str;
73 default = "127.0.0.1";
74 description = "Host to listen on.";
75 };
76
77 listenPort = mkOption {
78 type = types.int;
79 default = 8080;
80 description = "Port to listen on.";
81 };
82
83 plantumlLimitSize = mkOption {
84 type = types.int;
85 default = 4096;
86 description = "Limits image width and height.";
87 };
88
89 graphvizPackage = mkPackageOption pkgs "graphviz" { };
90
91 plantumlStats = mkOption {
92 type = types.bool;
93 default = false;
94 description = "Set it to on to enable statistics report (https://plantuml.com/statistics-report).";
95 };
96
97 httpAuthorization = mkOption {
98 type = types.nullOr types.str;
99 default = null;
100 description = "When calling the proxy endpoint, the value of HTTP_AUTHORIZATION will be used to set the HTTP Authorization header.";
101 };
102 };
103 };
104
105 config = mkIf cfg.enable {
106 systemd.services.plantuml-server = {
107 description = "PlantUML server";
108 wantedBy = [ "multi-user.target" ];
109
110 environment = {
111 PLANTUML_LIMIT_SIZE = builtins.toString cfg.plantumlLimitSize;
112 GRAPHVIZ_DOT = "${cfg.graphvizPackage}/bin/dot";
113 PLANTUML_STATS = if cfg.plantumlStats then "on" else "off";
114 HTTP_AUTHORIZATION = cfg.httpAuthorization;
115 };
116 script = ''
117 ${cfg.packages.jdk}/bin/java \
118 -jar ${cfg.packages.jetty}/start.jar \
119 --module=deploy,http,jsp \
120 jetty.home=${cfg.packages.jetty} \
121 jetty.base=${cfg.package} \
122 jetty.http.host=${cfg.listenHost} \
123 jetty.http.port=${builtins.toString cfg.listenPort}
124 '';
125
126 serviceConfig = {
127 User = cfg.user;
128 Group = cfg.group;
129 StateDirectory = mkIf (cfg.home == "/var/lib/plantuml") "plantuml";
130 StateDirectoryMode = mkIf (cfg.home == "/var/lib/plantuml") "0750";
131
132 # Hardening
133 AmbientCapabilities = [ "" ];
134 CapabilityBoundingSet = [ "" ];
135 DynamicUser = true;
136 LockPersonality = true;
137 NoNewPrivileges = true;
138 PrivateDevices = true;
139 PrivateNetwork = false;
140 PrivateTmp = true;
141 PrivateUsers = true;
142 ProtectClock = true;
143 ProtectControlGroups = true;
144 ProtectHome = true;
145 ProtectHostname = true;
146 ProtectKernelLogs = true;
147 ProtectKernelModules = true;
148 ProtectKernelTunables = true;
149 ProtectSystem = "strict";
150 RestrictAddressFamilies = [
151 "AF_UNIX"
152 "AF_INET"
153 "AF_INET6"
154 ];
155 RestrictNamespaces = true;
156 RestrictRealtime = true;
157 RestrictSUIDSGID = true;
158 SystemCallArchitectures = "native";
159 SystemCallFilter = [ "@system-service" ];
160 };
161 };
162 };
163
164 meta.maintainers = with lib.maintainers; [
165 truh
166 anthonyroussel
167 ];
168}