at master 8.3 kB view raw
1{ 2 config, 3 lib, 4 pkgs, 5 ... 6}: 7 8let 9 inherit (lib) 10 types 11 mkOption 12 hasPrefix 13 concatMapStringsSep 14 optionalString 15 concatMap 16 ; 17 inherit (builtins) isNull; 18 19 cfg = config.services.varnish; 20 21 # Varnish has very strong opinions and very complicated code around handling 22 # the stateDir. After a lot of back and forth, we decided that we a) 23 # do not want a configurable option here, as most of the handling depends 24 # on the version and the compile time options. Putting everything into 25 # /var/run (RAM backed) is absolutely recommended by Varnish anyways. 26 # We do need to pay attention to the version-dependend variations, though! 27 stateDir = 28 if 29 (lib.versionOlder cfg.package.version "7") 30 # Remove after Varnish 6.0 is gone. In 6.0 varnishadm always appends the 31 # hostname (by default) and can't be nudged to not use any name. This has 32 # long changed by 7.5 and can be used without the host name. 33 then 34 "/var/run/varnish/${config.networking.hostName}" 35 # Newer varnish uses this: 36 else 37 "/var/run/varnishd"; 38 39 # from --help: 40 # -a [<name>=]address[:port][,proto] # HTTP listen address and port 41 # [,user=<u>][,group=<g>] # Can be specified multiple times. 42 # [,mode=<m>] # default: ":80,HTTP" 43 # # Proto can be "PROXY" or "HTTP" (default) 44 # # user, group and mode set permissions for 45 # # a Unix domain socket. 46 commandLineAddresses = 47 (concatMapStringsSep " " ( 48 a: 49 "-a " 50 + optionalString (!isNull a.name) "${a.name}=" 51 + a.address 52 + optionalString (!isNull a.port) ":${toString a.port}" 53 + optionalString (!isNull a.proto) ",${a.proto}" 54 + optionalString (!isNull a.user) ",user=${a.user}" 55 + optionalString (!isNull a.group) ",group=${a.group}" 56 + optionalString (!isNull a.mode) ",mode=${a.mode}" 57 ) cfg.listen) 58 + lib.optionalString (!isNull cfg.http_address) " -a ${cfg.http_address}"; 59 addressSubmodule = types.submodule { 60 options = { 61 name = mkOption { 62 description = "Name is referenced in logs. If name is not specified, 'a0', 'a1', etc. is used."; 63 default = null; 64 type = with types; nullOr str; 65 }; 66 address = mkOption { 67 description = '' 68 If given an IP address, it can be a host name ("localhost"), an IPv4 dotted-quad 69 ("127.0.0.1") or an IPv6 address enclosed in square brackets ("[::1]"). 70 71 (VCL4.1 and higher) If given an absolute Path ("/path/to/listen.sock") or "@" 72 followed by the name of an abstract socket ("@myvarnishd") accept connections 73 on a Unix domain socket. 74 75 The user, group and mode sub-arguments may be used to specify the permissions 76 of the socket file. These sub-arguments do not apply to abstract sockets. 77 ''; 78 type = types.str; 79 }; 80 port = mkOption { 81 description = "The port to use for IP sockets. If port is not specified, port 80 (http) is used."; 82 default = null; 83 type = with types; nullOr port; 84 }; 85 proto = mkOption { 86 description = "PROTO can be 'HTTP' (the default) or 'PROXY'. Both version 1 and 2 of the proxy protocol can be used."; 87 type = types.enum [ 88 "HTTP" 89 "PROXY" 90 ]; 91 default = "HTTP"; 92 }; 93 user = mkOption { 94 description = "User name who owns the socket file."; 95 default = null; 96 type = with lib.types; nullOr str; 97 }; 98 group = mkOption { 99 description = "Group name who owns the socket file."; 100 default = null; 101 type = with lib.types; nullOr str; 102 }; 103 mode = mkOption { 104 description = "Permission of the socket file (3-digit octal value)."; 105 default = null; 106 type = with types; nullOr str; 107 }; 108 }; 109 }; 110 checkedAddressModule = types.addCheck addressSubmodule ( 111 m: 112 ( 113 if ((hasPrefix "@" m.address) || (hasPrefix "/" m.address)) then 114 # this is a unix socket 115 (m.port != null) 116 else 117 # this is not a path-based unix socket 118 if !(hasPrefix "/" m.address) && (m.group != null) || (m.user != null) || (m.mode != null) then 119 false 120 else 121 true 122 ) 123 ); 124 commandLine = 125 "-f ${pkgs.writeText "default.vcl" cfg.config}" 126 + 127 lib.optionalString (cfg.extraModules != [ ]) 128 " -p vmod_path='${ 129 lib.makeSearchPathOutput "lib" "lib/varnish/vmods" ([ cfg.package ] ++ cfg.extraModules) 130 }' -r vmod_path"; 131in 132{ 133 imports = [ 134 (lib.mkRemovedOptionModule [ 135 "services" 136 "varnish" 137 "stateDir" 138 ] "The `stateDir` option never was functional or useful. varnish uses compile-time settings.") 139 ]; 140 141 options = { 142 services.varnish = { 143 enable = lib.mkEnableOption "Varnish Server"; 144 145 enableConfigCheck = lib.mkEnableOption "checking the config during build time" // { 146 default = true; 147 }; 148 149 package = lib.mkPackageOption pkgs "varnish" { }; 150 151 http_address = lib.mkOption { 152 type = with lib.types; nullOr str; 153 default = null; 154 description = '' 155 HTTP listen address and port. 156 ''; 157 }; 158 159 listen = lib.mkOption { 160 description = "Accept for client requests on the specified listen addresses."; 161 type = lib.types.listOf checkedAddressModule; 162 defaultText = lib.literalExpression ''[ { address="*"; port=6081; } ]''; 163 default = lib.optional (isNull cfg.http_address) { 164 address = "*"; 165 port = 6081; 166 }; 167 }; 168 169 config = lib.mkOption { 170 type = lib.types.lines; 171 description = '' 172 Verbatim default.vcl configuration. 173 ''; 174 }; 175 176 extraModules = lib.mkOption { 177 type = lib.types.listOf lib.types.package; 178 default = [ ]; 179 example = lib.literalExpression "[ pkgs.varnishPackages.geoip ]"; 180 description = '' 181 Varnish modules (except 'std'). 182 ''; 183 }; 184 185 extraCommandLine = lib.mkOption { 186 type = lib.types.str; 187 default = ""; 188 example = "-s malloc,256M"; 189 description = '' 190 Command line switches for varnishd (run 'varnishd -?' to get list of options) 191 ''; 192 }; 193 }; 194 195 }; 196 197 config = lib.mkIf cfg.enable { 198 systemd.services.varnish = { 199 description = "Varnish"; 200 wantedBy = [ "multi-user.target" ]; 201 after = [ "network.target" ]; 202 serviceConfig = { 203 Type = "simple"; 204 PermissionsStartOnly = true; 205 ExecStart = "${cfg.package}/sbin/varnishd ${commandLineAddresses} -n ${stateDir} -F ${cfg.extraCommandLine} ${commandLine}"; 206 Restart = "always"; 207 RestartSec = "5s"; 208 User = "varnish"; 209 Group = "varnish"; 210 RuntimeDirectory = lib.removePrefix "/var/run/" stateDir; 211 AmbientCapabilities = "cap_net_bind_service"; 212 NoNewPrivileges = true; 213 LimitNOFILE = 131072; 214 }; 215 }; 216 217 environment.systemPackages = [ cfg.package ]; 218 219 # check .vcl syntax at compile time (e.g. before nixops deployment) 220 system.checks = lib.mkIf cfg.enableConfigCheck [ 221 (pkgs.runCommand "check-varnish-syntax" { } '' 222 ${cfg.package}/bin/varnishd -C ${commandLine} 2> $out || (cat $out; exit 1) 223 '') 224 ]; 225 226 assertions = concatMap (m: [ 227 { 228 assertion = (hasPrefix "/" m.address) || (hasPrefix "@" m.address) -> m.port == null; 229 message = "Listen ports must not be specified with UNIX sockets: ${builtins.toJSON m}"; 230 } 231 { 232 assertion = !(hasPrefix "/" m.address) -> m.user == null && m.group == null && m.mode == null; 233 message = "Abstract UNIX sockets or IP sockets can not be used with user, group, and mode settings: ${builtins.toJSON m}"; 234 } 235 ]) cfg.listen; 236 237 warnings = 238 lib.optional (!isNull cfg.http_address) 239 "The option `services.varnish.http_address` is deprecated. Use `services.varnish.listen` instead."; 240 241 users.users.varnish = { 242 group = "varnish"; 243 uid = config.ids.uids.varnish; 244 }; 245 246 users.groups.varnish.gid = config.ids.uids.varnish; 247 }; 248}