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