1{ 2 config, 3 lib, 4 pkgs, 5 ... 6}: 7 8with lib; 9 10let 11 cfg = config.services.trafficserver; 12 user = config.users.users.trafficserver.name; 13 group = config.users.groups.trafficserver.name; 14 15 getManualUrl = 16 name: "https://docs.trafficserver.apache.org/en/latest/admin-guide/files/${name}.en.html"; 17 18 yaml = pkgs.formats.yaml { }; 19 20 mkYamlConf = 21 name: cfg: 22 if cfg != null then 23 { 24 "trafficserver/${name}.yaml".source = yaml.generate "${name}.yaml" cfg; 25 } 26 else 27 { 28 "trafficserver/${name}.yaml".text = ""; 29 }; 30 31 mkRecordLines = 32 path: value: 33 if isAttrs value then 34 lib.mapAttrsToList (n: v: mkRecordLines (path ++ [ n ]) v) value 35 else if isInt value then 36 "CONFIG ${concatStringsSep "." path} INT ${toString value}" 37 else if isFloat value then 38 "CONFIG ${concatStringsSep "." path} FLOAT ${toString value}" 39 else 40 "CONFIG ${concatStringsSep "." path} STRING ${toString value}"; 41 42 mkRecordsConfig = cfg: concatStringsSep "\n" (flatten (mkRecordLines [ ] cfg)); 43 mkPluginConfig = cfg: concatStringsSep "\n" (map (p: "${p.path} ${p.arg}") cfg); 44in 45{ 46 options.services.trafficserver = { 47 enable = mkEnableOption "Apache Traffic Server"; 48 49 cache = mkOption { 50 type = types.lines; 51 default = ""; 52 example = "dest_domain=example.com suffix=js action=never-cache"; 53 description = '' 54 Caching rules that overrule the origin's caching policy. 55 56 Consult the [upstream 57 documentation](${getManualUrl "cache.config"}) for more details. 58 ''; 59 }; 60 61 hosting = mkOption { 62 type = types.lines; 63 default = ""; 64 example = "domain=example.com volume=1"; 65 description = '' 66 Partition the cache according to origin server or domain 67 68 Consult the [ 69 upstream documentation](${getManualUrl "hosting.config"}) for more details. 70 ''; 71 }; 72 73 ipAllow = mkOption { 74 type = types.nullOr yaml.type; 75 default = lib.importJSON ./ip_allow.json; 76 defaultText = literalMD "upstream defaults"; 77 example = literalExpression '' 78 { 79 ip_allow = [{ 80 apply = "in"; 81 ip_addrs = "127.0.0.1"; 82 action = "allow"; 83 methods = "ALL"; 84 }]; 85 } 86 ''; 87 description = '' 88 Control client access to Traffic Server and Traffic Server connections 89 to upstream servers. 90 91 Consult the [upstream 92 documentation](${getManualUrl "ip_allow.yaml"}) for more details. 93 ''; 94 }; 95 96 logging = mkOption { 97 type = types.nullOr yaml.type; 98 default = lib.importJSON ./logging.json; 99 defaultText = literalMD "upstream defaults"; 100 example = { }; 101 description = '' 102 Configure logs. 103 104 Consult the [upstream 105 documentation](${getManualUrl "logging.yaml"}) for more details. 106 ''; 107 }; 108 109 parent = mkOption { 110 type = types.lines; 111 default = ""; 112 example = '' 113 dest_domain=. method=get parent="p1.example:8080; p2.example:8080" round_robin=true 114 ''; 115 description = '' 116 Identify the parent proxies used in an cache hierarchy. 117 118 Consult the [upstream 119 documentation](${getManualUrl "parent.config"}) for more details. 120 ''; 121 }; 122 123 plugins = mkOption { 124 default = [ ]; 125 126 description = '' 127 Controls run-time loadable plugins available to Traffic Server, as 128 well as their configuration. 129 130 Consult the [upstream 131 documentation](${getManualUrl "plugin.config"}) for more details. 132 ''; 133 134 type = 135 with types; 136 listOf (submodule { 137 options.path = mkOption { 138 type = str; 139 example = "xdebug.so"; 140 description = '' 141 Path to plugin. The path can either be absolute, or relative to 142 the plugin directory. 143 ''; 144 }; 145 options.arg = mkOption { 146 type = str; 147 default = ""; 148 example = "--header=ATS-My-Debug"; 149 description = "arguments to pass to the plugin"; 150 }; 151 }); 152 }; 153 154 records = mkOption { 155 type = 156 with types; 157 let 158 valueType = 159 (attrsOf (oneOf [ 160 int 161 float 162 str 163 valueType 164 ])) 165 // { 166 description = "Traffic Server records value"; 167 }; 168 in 169 valueType; 170 default = { }; 171 example = { 172 proxy.config.proxy_name = "my_server"; 173 }; 174 description = '' 175 List of configurable variables used by Traffic Server. 176 177 Consult the [ 178 upstream documentation](${getManualUrl "records.config"}) for more details. 179 ''; 180 }; 181 182 remap = mkOption { 183 type = types.lines; 184 default = ""; 185 example = "map http://from.example http://origin.example"; 186 description = '' 187 URL remapping rules used by Traffic Server. 188 189 Consult the [ 190 upstream documentation](${getManualUrl "remap.config"}) for more details. 191 ''; 192 }; 193 194 splitDns = mkOption { 195 type = types.lines; 196 default = ""; 197 example = '' 198 dest_domain=internal.corp.example named="255.255.255.255:212 255.255.255.254" def_domain=corp.example search_list="corp.example corp1.example" 199 dest_domain=!internal.corp.example named=255.255.255.253 200 ''; 201 description = '' 202 Specify the DNS server that Traffic Server should use under specific 203 conditions. 204 205 Consult the [ 206 upstream documentation](${getManualUrl "splitdns.config"}) for more details. 207 ''; 208 }; 209 210 sslMulticert = mkOption { 211 type = types.lines; 212 default = ""; 213 example = "dest_ip=* ssl_cert_name=default.pem"; 214 description = '' 215 Configure SSL server certificates to terminate the SSL sessions. 216 217 Consult the [ 218 upstream documentation](${getManualUrl "ssl_multicert.config"}) for more details. 219 ''; 220 }; 221 222 sni = mkOption { 223 type = types.nullOr yaml.type; 224 default = null; 225 example = literalExpression '' 226 { 227 sni = [{ 228 fqdn = "no-http2.example.com"; 229 https = "off"; 230 }]; 231 } 232 ''; 233 description = '' 234 Configure aspects of TLS connection handling for both inbound and 235 outbound connections. 236 237 Consult the [upstream 238 documentation](${getManualUrl "sni.yaml"}) for more details. 239 ''; 240 }; 241 242 storage = mkOption { 243 type = types.lines; 244 default = "/var/cache/trafficserver 256M"; 245 example = "/dev/disk/by-id/XXXXX volume=1"; 246 description = '' 247 List all the storage that make up the Traffic Server cache. 248 249 Consult the [ 250 upstream documentation](${getManualUrl "storage.config"}) for more details. 251 ''; 252 }; 253 254 strategies = mkOption { 255 type = types.nullOr yaml.type; 256 default = null; 257 description = '' 258 Specify the next hop proxies used in an cache hierarchy and the 259 algorithms used to select the next proxy. 260 261 Consult the [ 262 upstream documentation](${getManualUrl "strategies.yaml"}) for more details. 263 ''; 264 }; 265 266 volume = mkOption { 267 type = types.nullOr yaml.type; 268 default = ""; 269 example = "volume=1 scheme=http size=20%"; 270 description = '' 271 Manage cache space more efficiently and restrict disk usage by 272 creating cache volumes of different sizes. 273 274 Consult the [ 275 upstream documentation](${getManualUrl "volume.config"}) for more details. 276 ''; 277 }; 278 }; 279 280 config = mkIf cfg.enable { 281 environment.etc = 282 { 283 "trafficserver/cache.config".text = cfg.cache; 284 "trafficserver/hosting.config".text = cfg.hosting; 285 "trafficserver/parent.config".text = cfg.parent; 286 "trafficserver/plugin.config".text = mkPluginConfig cfg.plugins; 287 "trafficserver/records.config".text = mkRecordsConfig cfg.records; 288 "trafficserver/remap.config".text = cfg.remap; 289 "trafficserver/splitdns.config".text = cfg.splitDns; 290 "trafficserver/ssl_multicert.config".text = cfg.sslMulticert; 291 "trafficserver/storage.config".text = cfg.storage; 292 "trafficserver/volume.config".text = cfg.volume; 293 } 294 // (mkYamlConf "ip_allow" cfg.ipAllow) 295 // (mkYamlConf "logging" cfg.logging) 296 // (mkYamlConf "sni" cfg.sni) 297 // (mkYamlConf "strategies" cfg.strategies); 298 299 environment.systemPackages = [ pkgs.trafficserver ]; 300 systemd.packages = [ pkgs.trafficserver ]; 301 302 # Traffic Server does privilege handling independently of systemd, and 303 # therefore should be started as root 304 systemd.services.trafficserver = { 305 enable = true; 306 wantedBy = [ "multi-user.target" ]; 307 }; 308 309 # These directories can't be created by systemd because: 310 # 311 # 1. Traffic Servers starts as root and switches to an unprivileged user 312 # afterwards. The runtime directories defined below are assumed to be 313 # owned by that user. 314 # 2. The bin/trafficserver script assumes these directories exist. 315 systemd.tmpfiles.rules = [ 316 "d '/run/trafficserver' - ${user} ${group} - -" 317 "d '/var/cache/trafficserver' - ${user} ${group} - -" 318 "d '/var/lib/trafficserver' - ${user} ${group} - -" 319 "d '/var/log/trafficserver' - ${user} ${group} - -" 320 ]; 321 322 services.trafficserver = { 323 records.proxy.config.admin.user_id = user; 324 records.proxy.config.body_factory.template_sets_dir = 325 "${pkgs.trafficserver}/etc/trafficserver/body_factory"; 326 }; 327 328 users.users.trafficserver = { 329 description = "Apache Traffic Server"; 330 isSystemUser = true; 331 inherit group; 332 }; 333 users.groups.trafficserver = { }; 334 }; 335}