zoekt: 3.7.2-2-unstable-2024-10-24 -> 3.7.2-2-unstable-2024-12-09 (#363818)
[NixPkgs.git] / nixos / modules / services / web-servers / tomcat.nix
blob2fffc7cd47db5affe8def066c3b878be6d35b403
2   config,
3   lib,
4   pkgs,
5   ...
6 }:
8 let
10   cfg = config.services.tomcat;
11   tomcat = cfg.package;
15   meta = {
16     maintainers = with lib.maintainers; [
17       danbst
18       anthonyroussel
19     ];
20   };
22   ###### interface
24   options = {
25     services.tomcat = {
26       enable = lib.mkEnableOption "Apache Tomcat";
28       package = lib.mkPackageOption pkgs "tomcat9" {
29         example = "tomcat10";
30       };
32       port = lib.mkOption {
33         type = lib.types.port;
34         default = 8080;
35         description = ''
36           The TCP port Tomcat should listen on.
37         '';
38       };
40       purifyOnStart = lib.mkOption {
41         type = lib.types.bool;
42         default = false;
43         description = ''
44           On startup, the `baseDir` directory is populated with various files,
45           subdirectories and symlinks. If this option is enabled, these items
46           (except for the `logs` and `work` subdirectories) are first removed.
47           This prevents interference from remainders of an old configuration
48           (libraries, webapps, etc.), so it's recommended to enable this option.
49         '';
50       };
52       baseDir = lib.mkOption {
53         type = lib.types.path;
54         default = "/var/tomcat";
55         description = ''
56           Location where Tomcat stores configuration files, web applications
57           and logfiles. Note that it is partially cleared on each service startup
58           if `purifyOnStart` is enabled.
59         '';
60       };
62       logDirs = lib.mkOption {
63         default = [ ];
64         type = lib.types.listOf lib.types.path;
65         description = "Directories to create in baseDir/logs/";
66       };
68       extraConfigFiles = lib.mkOption {
69         default = [ ];
70         type = lib.types.listOf lib.types.path;
71         description = "Extra configuration files to pull into the tomcat conf directory";
72       };
74       extraEnvironment = lib.mkOption {
75         type = lib.types.listOf lib.types.str;
76         default = [ ];
77         example = [ "ENVIRONMENT=production" ];
78         description = "Environment Variables to pass to the tomcat service";
79       };
81       extraGroups = lib.mkOption {
82         default = [ ];
83         type = lib.types.listOf lib.types.str;
84         example = [ "users" ];
85         description = "Defines extra groups to which the tomcat user belongs.";
86       };
88       user = lib.mkOption {
89         type = lib.types.str;
90         default = "tomcat";
91         description = "User account under which Apache Tomcat runs.";
92       };
94       group = lib.mkOption {
95         type = lib.types.str;
96         default = "tomcat";
97         description = "Group account under which Apache Tomcat runs.";
98       };
100       javaOpts = lib.mkOption {
101         type = lib.types.either (lib.types.listOf lib.types.str) lib.types.str;
102         default = "";
103         description = "Parameters to pass to the Java Virtual Machine which spawns Apache Tomcat";
104       };
106       catalinaOpts = lib.mkOption {
107         type = lib.types.either (lib.types.listOf lib.types.str) lib.types.str;
108         default = "";
109         description = "Parameters to pass to the Java Virtual Machine which spawns the Catalina servlet container";
110       };
112       sharedLibs = lib.mkOption {
113         type = lib.types.listOf lib.types.str;
114         default = [ ];
115         description = "List containing JAR files or directories with JAR files which are libraries shared by the web applications";
116       };
118       serverXml = lib.mkOption {
119         type = lib.types.lines;
120         default = "";
121         description = ''
122           Verbatim server.xml configuration.
123           This is mutually exclusive with the virtualHosts options.
124         '';
125       };
127       commonLibs = lib.mkOption {
128         type = lib.types.listOf lib.types.str;
129         default = [ ];
130         description = "List containing JAR files or directories with JAR files which are libraries shared by the web applications and the servlet container";
131       };
133       webapps = lib.mkOption {
134         type = lib.types.listOf lib.types.path;
135         default = [ tomcat.webapps ];
136         defaultText = lib.literalExpression "[ config.services.tomcat.package.webapps ]";
137         description = "List containing WAR files or directories with WAR files which are web applications to be deployed on Tomcat";
138       };
140       virtualHosts = lib.mkOption {
141         type = lib.types.listOf (
142           lib.types.submodule {
143             options = {
144               name = lib.mkOption {
145                 type = lib.types.str;
146                 description = "name of the virtualhost";
147               };
148               aliases = lib.mkOption {
149                 type = lib.types.listOf lib.types.str;
150                 description = "aliases of the virtualhost";
151                 default = [ ];
152               };
153               webapps = lib.mkOption {
154                 type = lib.types.listOf lib.types.path;
155                 description = ''
156                   List containing web application WAR files and/or directories containing
157                   web applications and configuration files for the virtual host.
158                 '';
159                 default = [ ];
160               };
161             };
162           }
163         );
164         default = [ ];
165         description = "List consisting of a virtual host name and a list of web applications to deploy on each virtual host";
166       };
168       logPerVirtualHost = lib.mkOption {
169         type = lib.types.bool;
170         default = false;
171         description = "Whether to enable logging per virtual host.";
172       };
174       jdk = lib.mkPackageOption pkgs "jdk" { };
176       axis2 = {
177         enable = lib.mkEnableOption "Apache Axis2 container";
179         services = lib.mkOption {
180           default = [ ];
181           type = lib.types.listOf lib.types.str;
182           description = "List containing AAR files or directories with AAR files which are web services to be deployed on Axis2";
183         };
184       };
185     };
186   };
188   ###### implementation
190   config = lib.mkIf config.services.tomcat.enable {
192     users.groups.tomcat.gid = config.ids.gids.tomcat;
194     users.users.tomcat = {
195       uid = config.ids.uids.tomcat;
196       description = "Tomcat user";
197       home = "/homeless-shelter";
198       group = "tomcat";
199       extraGroups = cfg.extraGroups;
200     };
202     systemd.services.tomcat = {
203       description = "Apache Tomcat server";
204       wantedBy = [ "multi-user.target" ];
205       after = [ "network.target" ];
207       preStart = ''
208         ${lib.optionalString cfg.purifyOnStart ''
209           # Delete most directories/symlinks we create from the existing base directory,
210           # to get rid of remainders of an old configuration.
211           # The list of directories to delete is taken from the "mkdir" command below,
212           # excluding "logs" (because logs are valuable) and "work" (because normally
213           # session files are there), and additionally including "bin".
214           rm -rf ${cfg.baseDir}/{conf,virtualhosts,temp,lib,shared/lib,webapps,bin}
215         ''}
217         # Create the base directory
218         mkdir -p \
219           ${cfg.baseDir}/{conf,virtualhosts,logs,temp,lib,shared/lib,webapps,work}
220         chown ${cfg.user}:${cfg.group} \
221           ${cfg.baseDir}/{conf,virtualhosts,logs,temp,lib,shared/lib,webapps,work}
223         # Create a symlink to the bin directory of the tomcat component
224         ln -sfn ${tomcat}/bin ${cfg.baseDir}/bin
226         # Symlink the config files in the conf/ directory (except for catalina.properties and server.xml)
227         for i in $(ls ${tomcat}/conf | grep -v catalina.properties | grep -v server.xml); do
228           ln -sfn ${tomcat}/conf/$i ${cfg.baseDir}/conf/`basename $i`
229         done
231         ${lib.optionalString (cfg.extraConfigFiles != [ ]) ''
232           for i in ${toString cfg.extraConfigFiles}; do
233             ln -sfn $i ${cfg.baseDir}/conf/`basename $i`
234           done
235         ''}
237         # Create a modified catalina.properties file
238         # Change all references from CATALINA_HOME to CATALINA_BASE and add support for shared libraries
239         sed -e 's|''${catalina.home}|''${catalina.base}|g' \
240           -e 's|shared.loader=|shared.loader=''${catalina.base}/shared/lib/*.jar|' \
241           ${tomcat}/conf/catalina.properties > ${cfg.baseDir}/conf/catalina.properties
243         ${
244           if cfg.serverXml != "" then
245             ''
246               cp -f ${pkgs.writeTextDir "server.xml" cfg.serverXml}/* ${cfg.baseDir}/conf/
247             ''
248           else
249             let
250               hostElementForVirtualHost =
251                 virtualHost:
252                 ''
253                   <Host name="${virtualHost.name}" appBase="virtualhosts/${virtualHost.name}/webapps"
254                         unpackWARs="true" autoDeploy="true" xmlValidation="false" xmlNamespaceAware="false">
255                 ''
256                 + lib.concatStrings (innerElementsForVirtualHost virtualHost)
257                 + ''
258                   </Host>
259                 '';
260               innerElementsForVirtualHost =
261                 virtualHost:
262                 (map (alias: ''
263                   <Alias>${alias}</Alias>
264                 '') virtualHost.aliases)
265                 ++ (lib.optional cfg.logPerVirtualHost ''
266                   <Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs/${virtualHost.name}"
267                          prefix="${virtualHost.name}_access_log." pattern="combined" resolveHosts="false"/>
268                 '');
269               hostElementsString = lib.concatMapStringsSep "\n" hostElementForVirtualHost cfg.virtualHosts;
270               hostElementsSedString = lib.replaceStrings [ "\n" ] [ "\\\n" ] hostElementsString;
271             in
272             ''
273               # Create a modified server.xml which listens on the given port,
274               # and also includes all virtual hosts.
275               # The host modification must be last here,
276               # else if hostElementsSedString is empty sed gets confused as to what to append
277               sed -e 's/<Connector port="8080"/<Connector port="${toString cfg.port}"/' \
278                   -e "/<Engine name=\"Catalina\" defaultHost=\"localhost\">/a\\"${lib.escapeShellArg hostElementsSedString} \
279                     ${tomcat}/conf/server.xml > ${cfg.baseDir}/conf/server.xml
280             ''
281         }
282         ${lib.optionalString (cfg.logDirs != [ ]) ''
283           for i in ${toString cfg.logDirs}; do
284             mkdir -p ${cfg.baseDir}/logs/$i
285             chown ${cfg.user}:${cfg.group} ${cfg.baseDir}/logs/$i
286           done
287         ''}
288         ${lib.optionalString cfg.logPerVirtualHost (
289           toString (
290             map (h: ''
291               mkdir -p ${cfg.baseDir}/logs/${h.name}
292               chown ${cfg.user}:${cfg.group} ${cfg.baseDir}/logs/${h.name}
293             '') cfg.virtualHosts
294           )
295         )}
297         # Symlink all the given common libs files or paths into the lib/ directory
298         for i in ${tomcat} ${toString cfg.commonLibs}; do
299           if [ -f $i ]; then
300             # If the given web application is a file, symlink it into the common/lib/ directory
301             ln -sfn $i ${cfg.baseDir}/lib/`basename $i`
302           elif [ -d $i ]; then
303             # If the given web application is a directory, then iterate over the files
304             # in the special purpose directories and symlink them into the tomcat tree
306             for j in $i/lib/*; do
307               ln -sfn $j ${cfg.baseDir}/lib/`basename $j`
308             done
309           fi
310         done
312         # Symlink all the given shared libs files or paths into the shared/lib/ directory
313         for i in ${toString cfg.sharedLibs}; do
314           if [ -f $i ]; then
315             # If the given web application is a file, symlink it into the common/lib/ directory
316             ln -sfn $i ${cfg.baseDir}/shared/lib/`basename $i`
317           elif [ -d $i ]; then
318             # If the given web application is a directory, then iterate over the files
319             # in the special purpose directories and symlink them into the tomcat tree
321             for j in $i/shared/lib/*; do
322               ln -sfn $j ${cfg.baseDir}/shared/lib/`basename $j`
323             done
324           fi
325         done
327         # Symlink all the given web applications files or paths into the webapps/ directory
328         for i in ${toString cfg.webapps}; do
329           if [ -f $i ]; then
330             # If the given web application is a file, symlink it into the webapps/ directory
331             ln -sfn $i ${cfg.baseDir}/webapps/`basename $i`
332           elif [ -d $i ]; then
333             # If the given web application is a directory, then iterate over the files
334             # in the special purpose directories and symlink them into the tomcat tree
336             for j in $i/webapps/*; do
337               ln -sfn $j ${cfg.baseDir}/webapps/`basename $j`
338             done
340             # Also symlink the configuration files if they are included
341             if [ -d $i/conf/Catalina ]; then
342               for j in $i/conf/Catalina/*; do
343                 mkdir -p ${cfg.baseDir}/conf/Catalina/localhost
344                 ln -sfn $j ${cfg.baseDir}/conf/Catalina/localhost/`basename $j`
345               done
346             fi
347           fi
348         done
350         ${toString (
351           map (virtualHost: ''
352             # Create webapps directory for the virtual host
353             mkdir -p ${cfg.baseDir}/virtualhosts/${virtualHost.name}/webapps
355             # Modify ownership
356             chown ${cfg.user}:${cfg.group} ${cfg.baseDir}/virtualhosts/${virtualHost.name}/webapps
358             # Symlink all the given web applications files or paths into the webapps/ directory
359             # of this virtual host
360             for i in "${lib.optionalString (virtualHost ? webapps) (toString virtualHost.webapps)}"; do
361               if [ -f $i ]; then
362                 # If the given web application is a file, symlink it into the webapps/ directory
363                 ln -sfn $i ${cfg.baseDir}/virtualhosts/${virtualHost.name}/webapps/`basename $i`
364               elif [ -d $i ]; then
365                 # If the given web application is a directory, then iterate over the files
366                 # in the special purpose directories and symlink them into the tomcat tree
368                 for j in $i/webapps/*; do
369                   ln -sfn $j ${cfg.baseDir}/virtualhosts/${virtualHost.name}/webapps/`basename $j`
370                 done
372                 # Also symlink the configuration files if they are included
373                 if [ -d $i/conf/Catalina ]; then
374                   for j in $i/conf/Catalina/*; do
375                     mkdir -p ${cfg.baseDir}/conf/Catalina/${virtualHost.name}
376                     ln -sfn $j ${cfg.baseDir}/conf/Catalina/${virtualHost.name}/`basename $j`
377                   done
378                 fi
379               fi
380             done
381           '') cfg.virtualHosts
382         )}
384         ${lib.optionalString cfg.axis2.enable ''
385           # Copy the Axis2 web application
386           cp -av ${pkgs.axis2}/webapps/axis2 ${cfg.baseDir}/webapps
388           # Turn off addressing, which causes many errors
389           sed -i -e 's%<module ref="addressing"/>%<!-- <module ref="addressing"/> -->%' ${cfg.baseDir}/webapps/axis2/WEB-INF/conf/axis2.xml
391           # Modify permissions on the Axis2 application
392           chown -R ${cfg.user}:${cfg.group} ${cfg.baseDir}/webapps/axis2
394           # Symlink all the given web service files or paths into the webapps/axis2/WEB-INF/services directory
395           for i in ${toString cfg.axis2.services}; do
396             if [ -f $i ]; then
397               # If the given web service is a file, symlink it into the webapps/axis2/WEB-INF/services
398               ln -sfn $i ${cfg.baseDir}/webapps/axis2/WEB-INF/services/`basename $i`
399             elif [ -d $i ]; then
400               # If the given web application is a directory, then iterate over the files
401               # in the special purpose directories and symlink them into the tomcat tree
403               for j in $i/webapps/axis2/WEB-INF/services/*; do
404                 ln -sfn $j ${cfg.baseDir}/webapps/axis2/WEB-INF/services/`basename $j`
405               done
407               # Also symlink the configuration files if they are included
408               if [ -d $i/conf/Catalina ]; then
409                 for j in $i/conf/Catalina/*; do
410                   ln -sfn $j ${cfg.baseDir}/conf/Catalina/localhost/`basename $j`
411                 done
412               fi
413             fi
414           done
415         ''}
416       '';
418       serviceConfig = {
419         Type = "forking";
420         PermissionsStartOnly = true;
421         PIDFile = "/run/tomcat/tomcat.pid";
422         RuntimeDirectory = "tomcat";
423         User = cfg.user;
424         Environment = [
425           "CATALINA_BASE=${cfg.baseDir}"
426           "CATALINA_PID=/run/tomcat/tomcat.pid"
427           "JAVA_HOME='${cfg.jdk}'"
428           "JAVA_OPTS='${builtins.toString cfg.javaOpts}'"
429           "CATALINA_OPTS='${builtins.toString cfg.catalinaOpts}'"
430         ] ++ cfg.extraEnvironment;
431         ExecStart = "${tomcat}/bin/startup.sh";
432         ExecStop = "${tomcat}/bin/shutdown.sh";
433       };
434     };
435   };