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