caddy: add virtualHosts stub

+114 -40
+1 -1
nixos/modules/module-list.nix
··· 995 ./services/web-apps/youtrack.nix 996 ./services/web-apps/zabbix.nix 997 ./services/web-servers/apache-httpd/default.nix 998 - ./services/web-servers/caddy.nix 999 ./services/web-servers/darkhttpd.nix 1000 ./services/web-servers/fcgiwrap.nix 1001 ./services/web-servers/hitch/default.nix
··· 995 ./services/web-apps/youtrack.nix 996 ./services/web-apps/zabbix.nix 997 ./services/web-servers/apache-httpd/default.nix 998 + ./services/web-servers/caddy/default.nix 999 ./services/web-servers/darkhttpd.nix 1000 ./services/web-servers/fcgiwrap.nix 1001 ./services/web-servers/hitch/default.nix
+33 -2
nixos/modules/services/web-servers/caddy.nix nixos/modules/services/web-servers/caddy/default.nix
··· 4 5 let 6 cfg = config.services.caddy; 7 - configFile = pkgs.writeText "Caddyfile" cfg.config; 8 9 tlsConfig = { 10 apps.tls.automation.policies = [{ ··· 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 ··· 67 Caddy v2 supports multiple config formats via adapters (see <option>services.caddy.adapter</option>). 68 ''; 69 }; 70 71 user = mkOption { 72 default = "caddy";
··· 4 5 let 6 cfg = config.services.caddy; 7 + vhostToConfig = vhostName: vhostAttrs: '' 8 + ${vhostName} ${builtins.concatStringsSep " " vhostAttrs.serverAliases} { 9 + ${vhostAttrs.extraConfig} 10 + } 11 + ''; 12 + configFile = pkgs.writeText "Caddyfile" (builtins.concatStringsSep "\n" 13 + ([ cfg.config ] ++ (mapAttrsToList vhostToConfig cfg.virtualHosts))); 14 + 15 + formattedConfig = pkgs.runCommand "formattedCaddyFile" { } '' 16 + ${cfg.package}/bin/caddy fmt ${configFile} > $out 17 + ''; 18 19 tlsConfig = { 20 apps.tls.automation.policies = [{ ··· 27 28 adaptedConfig = pkgs.runCommand "caddy-config-adapted.json" { } '' 29 ${cfg.package}/bin/caddy adapt \ 30 + --config ${formattedConfig} --adapter ${cfg.adapter} > $out 31 ''; 32 tlsJSON = pkgs.writeText "tls.json" (builtins.toJSON tlsConfig); 33 ··· 77 Caddy v2 supports multiple config formats via adapters (see <option>services.caddy.adapter</option>). 78 ''; 79 }; 80 + 81 + virtualHosts = mkOption { 82 + type = types.attrsOf (types.submodule (import ./vhost-options.nix { 83 + inherit config lib; 84 + })); 85 + default = { }; 86 + example = literalExample '' 87 + { 88 + "hydra.example.com" = { 89 + serverAliases = [ "www.hydra.example.com" ]; 90 + extraConfig = '''''' 91 + encode gzip 92 + log 93 + root /srv/http 94 + ''''''; 95 + }; 96 + }; 97 + ''; 98 + description = "Declarative vhost config"; 99 + }; 100 + 101 102 user = mkOption { 103 default = "caddy";
+28
nixos/modules/services/web-servers/caddy/vhost-options.nix
···
··· 1 + # This file defines the options that can be used both for the Nginx 2 + # main server configuration, and for the virtual hosts. (The latter 3 + # has additional options that affect the web server as a whole, like 4 + # the user/group to run under.) 5 + 6 + { lib, ... }: 7 + 8 + with lib; 9 + { 10 + options = { 11 + serverAliases = mkOption { 12 + type = types.listOf types.str; 13 + default = [ ]; 14 + example = [ "www.example.org" "example.org" ]; 15 + description = '' 16 + Additional names of virtual hosts served by this virtual host configuration. 17 + ''; 18 + }; 19 + 20 + extraConfig = mkOption { 21 + type = types.lines; 22 + default = ""; 23 + description = '' 24 + These lines go into the vhost verbatim 25 + ''; 26 + }; 27 + }; 28 + }
+52 -37
nixos/tests/caddy.nix
··· 43 } 44 ''; 45 }; 46 }; 47 - }; 48 49 - testScript = { nodes, ... }: let 50 - etagSystem = "${nodes.webserver.config.system.build.toplevel}/specialisation/etag"; 51 - justReloadSystem = "${nodes.webserver.config.system.build.toplevel}/specialisation/config-reload"; 52 - in '' 53 - url = "http://localhost/example.html" 54 - webserver.wait_for_unit("caddy") 55 - webserver.wait_for_open_port("80") 56 57 58 - def check_etag(url): 59 - etag = webserver.succeed( 60 - "curl --fail -v '{}' 2>&1 | sed -n -e \"s/^< [Ee][Tt][Aa][Gg]: *//p\"".format( 61 - url 62 ) 63 - ) 64 - etag = etag.replace("\r\n", " ") 65 - http_code = webserver.succeed( 66 - "curl --fail --silent --show-error -o /dev/null -w \"%{{http_code}}\" --head -H 'If-None-Match: {}' {}".format( 67 - etag, url 68 ) 69 - ) 70 - assert int(http_code) == 304, "HTTP code is {}, expected 304".format(http_code) 71 - return etag 72 73 74 - with subtest("check ETag if serving Nix store paths"): 75 - old_etag = check_etag(url) 76 - webserver.succeed( 77 - "${etagSystem}/bin/switch-to-configuration test >&2" 78 - ) 79 - webserver.sleep(1) 80 - new_etag = check_etag(url) 81 - assert old_etag != new_etag, "Old ETag {} is the same as {}".format( 82 - old_etag, new_etag 83 - ) 84 85 - with subtest("config is reloaded on nixos-rebuild switch"): 86 - webserver.succeed( 87 - "${justReloadSystem}/bin/switch-to-configuration test >&2" 88 - ) 89 - webserver.wait_for_open_port("8080") 90 - ''; 91 - })
··· 43 } 44 ''; 45 }; 46 + specialisation.multiple-configs.configuration = { 47 + services.caddy.virtualHosts = { 48 + "http://localhost:8080" = { }; 49 + "http://localhost:8081" = { }; 50 + }; 51 + }; 52 }; 53 54 + testScript = { nodes, ... }: 55 + let 56 + etagSystem = "${nodes.webserver.config.system.build.toplevel}/specialisation/etag"; 57 + justReloadSystem = "${nodes.webserver.config.system.build.toplevel}/specialisation/config-reload"; 58 + multipleConfigs = "${nodes.webserver.config.system.build.toplevel}/specialisation/multiple-configs"; 59 + in 60 + '' 61 + url = "http://localhost/example.html" 62 + webserver.wait_for_unit("caddy") 63 + webserver.wait_for_open_port("80") 64 65 66 + def check_etag(url): 67 + etag = webserver.succeed( 68 + "curl --fail -v '{}' 2>&1 | sed -n -e \"s/^< [Ee][Tt][Aa][Gg]: *//p\"".format( 69 + url 70 + ) 71 ) 72 + etag = etag.replace("\r\n", " ") 73 + http_code = webserver.succeed( 74 + "curl --fail --silent --show-error -o /dev/null -w \"%{{http_code}}\" --head -H 'If-None-Match: {}' {}".format( 75 + etag, url 76 + ) 77 ) 78 + assert int(http_code) == 304, "HTTP code is {}, expected 304".format(http_code) 79 + return etag 80 81 82 + with subtest("check ETag if serving Nix store paths"): 83 + old_etag = check_etag(url) 84 + webserver.succeed( 85 + "${etagSystem}/bin/switch-to-configuration test >&2" 86 + ) 87 + webserver.sleep(1) 88 + new_etag = check_etag(url) 89 + assert old_etag != new_etag, "Old ETag {} is the same as {}".format( 90 + old_etag, new_etag 91 + ) 92 93 + with subtest("config is reloaded on nixos-rebuild switch"): 94 + webserver.succeed( 95 + "${justReloadSystem}/bin/switch-to-configuration test >&2" 96 + ) 97 + webserver.wait_for_open_port("8080") 98 + 99 + with subtest("multiple configs are correctly merged"): 100 + webserver.succeed( 101 + "${multipleConfigs}/bin/switch-to-configuration test >&2" 102 + ) 103 + webserver.wait_for_open_port("8080") 104 + webserver.wait_for_open_port("8081") 105 + ''; 106 + })