at master 4.9 kB view raw
1{ 2 config, 3 lib, 4 pkgs, 5 ... 6}: 7let 8 cfg = config.services.traccar; 9 stateDirectory = "/var/lib/traccar"; 10 configFilePath = "${stateDirectory}/config.xml"; 11 12 # Map leafs to XML <entry> elements as expected by traccar, using 13 # dot-separated keys for nested attribute paths. 14 mapLeafs = lib.mapAttrsRecursive ( 15 path: value: "<entry key='${lib.concatStringsSep "." path}'>${value}</entry>" 16 ); 17 18 mkConfigEntry = config: lib.collect builtins.isString (mapLeafs config); 19 20 mkConfig = 21 configurationOptions: 22 pkgs.writeText "traccar.xml" '' 23 <?xml version='1.0' encoding='UTF-8'?> 24 <!DOCTYPE properties SYSTEM 'http://java.sun.com/dtd/properties.dtd'> 25 <properties> 26 ${builtins.concatStringsSep "\n" (mkConfigEntry configurationOptions)} 27 </properties> 28 ''; 29 30 defaultConfig = { 31 database = { 32 driver = "org.h2.Driver"; 33 password = ""; 34 url = "jdbc:h2:${stateDirectory}/traccar"; 35 user = "sa"; 36 }; 37 logger.console = "true"; 38 media.path = "${stateDirectory}/media"; 39 templates.root = "${stateDirectory}/templates"; 40 }; 41 42in 43{ 44 options.services.traccar = { 45 enable = lib.mkEnableOption "Traccar, an open source GPS tracking system"; 46 settingsFile = lib.mkOption { 47 type = with lib.types; nullOr path; 48 default = null; 49 description = '' 50 File used as configuration for traccar. When specified, {option}`settings` is ignored. 51 ''; 52 }; 53 settings = lib.mkOption { 54 apply = lib.recursiveUpdate defaultConfig; 55 default = defaultConfig; 56 description = '' 57 {file}`config.xml` configuration as a Nix attribute set. 58 This option is ignored if `settingsFile` is set. 59 60 Nested attributes get translated to a properties entry in the traccar configuration. 61 For instance: `mail.smtp.port = "25"` results in the following entry: 62 `<entry key='mail.smtp.port'>25</entry>` 63 64 Secrets should be specified using {option}`environmentFile` 65 instead of this world-readable attribute set. 66 [Traccar - Configuration File](https://www.traccar.org/configuration-file/). 67 ''; 68 }; 69 environmentFile = lib.mkOption { 70 type = lib.types.nullOr lib.types.path; 71 default = null; 72 description = '' 73 File containing environment variables to substitute in the configuration before starting Traccar. 74 75 Can be used for storing the secrets without making them available in the world-readable Nix store. 76 77 For example, you can set {option}`services.traccar.settings.database.password = "$TRACCAR_DB_PASSWORD"` 78 and then specify `TRACCAR_DB_PASSWORD="<secret>"` in the environment file. 79 This value will get substituted in the configuration file. 80 ''; 81 }; 82 }; 83 84 config = 85 let 86 configuration = if cfg.settingsFile != null then cfg.settingsFile else mkConfig cfg.settings; 87 in 88 lib.mkIf cfg.enable { 89 systemd.services.traccar = { 90 enable = true; 91 description = "Traccar"; 92 93 after = [ "network-online.target" ]; 94 wantedBy = [ "multi-user.target" ]; 95 wants = [ "network-online.target" ]; 96 97 preStart = '' 98 # Copy new templates into our state directory. 99 cp -a --update=none ${pkgs.traccar}/templates ${stateDirectory} 100 test -f '${configFilePath}' && rm -f '${configFilePath}' 101 102 # Substitute the configFile from Envvars read from EnvironmentFile 103 old_umask=$(umask) 104 umask 0177 105 ${lib.getExe pkgs.envsubst} \ 106 -i ${configuration} \ 107 -o ${configFilePath} 108 umask $old_umask 109 ''; 110 111 serviceConfig = { 112 DynamicUser = true; 113 EnvironmentFile = lib.mkIf (cfg.environmentFile != null) cfg.environmentFile; 114 ExecStart = "${lib.getExe pkgs.traccar} ${configFilePath}"; 115 LockPersonality = true; 116 NoNewPrivileges = true; 117 PrivateDevices = true; 118 PrivateTmp = true; 119 PrivateUsers = true; 120 ProtectClock = true; 121 ProtectControlGroups = true; 122 ProtectHome = true; 123 ProtectHostname = true; 124 ProtectKernelLogs = true; 125 ProtectKernelModules = true; 126 ProtectKernelTunables = true; 127 ProtectSystem = "strict"; 128 Restart = "on-failure"; 129 RestartSec = 10; 130 RestrictRealtime = true; 131 RestrictSUIDSGID = true; 132 StateDirectory = "traccar"; 133 SuccessExitStatus = 143; 134 Type = "simple"; 135 # Set the working directory to traccar's package. 136 # Traccar only searches for the DB migrations relative to it's WorkingDirectory and nothing worked to 137 # work around this. To avoid copying the migrations over to the state directory, we use the package as 138 # WorkingDirectory. 139 WorkingDirectory = "${pkgs.traccar}"; 140 }; 141 }; 142 }; 143}