at 21.11-pre 5.0 kB view raw
1{ config, lib, pkgs, ... }: 2 3with lib; 4 5let 6 cfg = config.services.caddy; 7 configFile = pkgs.writeText "Caddyfile" cfg.config; 8 9 tlsConfig = { 10 apps.tls.automation.policies = [{ 11 issuer = { 12 inherit (cfg) ca email; 13 module = "acme"; 14 }; 15 }]; 16 }; 17 18 adaptedConfig = pkgs.runCommand "caddy-config-adapted.json" { } '' 19 ${cfg.package}/bin/caddy adapt \ 20 --config ${configFile} --adapter ${cfg.adapter} > $out 21 ''; 22 tlsJSON = pkgs.writeText "tls.json" (builtins.toJSON tlsConfig); 23 24 # merge the TLS config options we expose with the ones originating in the Caddyfile 25 configJSON = 26 let tlsConfigMerge = '' 27 {"apps": 28 {"tls": 29 {"automation": 30 {"policies": 31 (if .[0].apps.tls.automation.policies == .[1]?.apps.tls.automation.policies 32 then .[0].apps.tls.automation.policies 33 else (.[0].apps.tls.automation.policies + .[1]?.apps.tls.automation.policies) 34 end) 35 } 36 } 37 } 38 }''; 39 in pkgs.runCommand "caddy-config.json" { } '' 40 ${pkgs.jq}/bin/jq -s '.[0] * ${tlsConfigMerge}' ${adaptedConfig} ${tlsJSON} > $out 41 ''; 42in { 43 imports = [ 44 (mkRemovedOptionModule [ "services" "caddy" "agree" ] "this option is no longer necessary for Caddy 2") 45 ]; 46 47 options.services.caddy = { 48 enable = mkEnableOption "Caddy web server"; 49 50 config = mkOption { 51 default = ""; 52 example = '' 53 example.com { 54 encode gzip 55 log 56 root /srv/http 57 } 58 ''; 59 type = types.lines; 60 description = '' 61 Verbatim Caddyfile to use. 62 Caddy v2 supports multiple config formats via adapters (see <option>services.caddy.adapter</option>). 63 ''; 64 }; 65 66 user = mkOption { 67 default = "caddy"; 68 type = types.str; 69 description = "User account under which caddy runs."; 70 }; 71 72 group = mkOption { 73 default = "caddy"; 74 type = types.str; 75 description = "Group account under which caddy runs."; 76 }; 77 78 adapter = mkOption { 79 default = "caddyfile"; 80 example = "nginx"; 81 type = types.str; 82 description = '' 83 Name of the config adapter to use. 84 See https://caddyserver.com/docs/config-adapters for the full list. 85 ''; 86 }; 87 88 ca = mkOption { 89 default = "https://acme-v02.api.letsencrypt.org/directory"; 90 example = "https://acme-staging-v02.api.letsencrypt.org/directory"; 91 type = types.str; 92 description = "Certificate authority ACME server. The default (Let's Encrypt production server) should be fine for most people."; 93 }; 94 95 email = mkOption { 96 default = ""; 97 type = types.str; 98 description = "Email address (for Let's Encrypt certificate)"; 99 }; 100 101 dataDir = mkOption { 102 default = "/var/lib/caddy"; 103 type = types.path; 104 description = '' 105 The data directory, for storing certificates. Before 17.09, this 106 would create a .caddy directory. With 17.09 the contents of the 107 .caddy directory are in the specified data directory instead. 108 109 Caddy v2 replaced CADDYPATH with XDG directories. 110 See https://caddyserver.com/docs/conventions#file-locations. 111 ''; 112 }; 113 114 package = mkOption { 115 default = pkgs.caddy; 116 defaultText = "pkgs.caddy"; 117 example = "pkgs.caddy"; 118 type = types.package; 119 description = '' 120 Caddy package to use. 121 ''; 122 }; 123 }; 124 125 config = mkIf cfg.enable { 126 systemd.services.caddy = { 127 description = "Caddy web server"; 128 # upstream unit: https://github.com/caddyserver/dist/blob/master/init/caddy.service 129 after = [ "network-online.target" ]; 130 wants = [ "network-online.target" ]; # systemd-networkd-wait-online.service 131 wantedBy = [ "multi-user.target" ]; 132 startLimitIntervalSec = 14400; 133 startLimitBurst = 10; 134 serviceConfig = { 135 ExecStart = "${cfg.package}/bin/caddy run --config ${configJSON}"; 136 ExecReload = "${cfg.package}/bin/caddy reload --config ${configJSON}"; 137 Type = "simple"; 138 User = cfg.user; 139 Group = cfg.group; 140 Restart = "on-abnormal"; 141 AmbientCapabilities = "cap_net_bind_service"; 142 CapabilityBoundingSet = "cap_net_bind_service"; 143 NoNewPrivileges = true; 144 LimitNPROC = 512; 145 LimitNOFILE = 1048576; 146 PrivateTmp = true; 147 PrivateDevices = true; 148 ProtectHome = true; 149 ProtectSystem = "full"; 150 ReadWriteDirectories = cfg.dataDir; 151 KillMode = "mixed"; 152 KillSignal = "SIGQUIT"; 153 TimeoutStopSec = "5s"; 154 }; 155 }; 156 157 users.users = optionalAttrs (cfg.user == "caddy") { 158 caddy = { 159 group = cfg.group; 160 uid = config.ids.uids.caddy; 161 home = cfg.dataDir; 162 createHome = true; 163 }; 164 }; 165 166 users.groups = optionalAttrs (cfg.group == "caddy") { 167 caddy.gid = config.ids.gids.caddy; 168 }; 169 170 }; 171}