From daf64e3f7de98e4267823d14fa34891b27b5f657 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Isma=C3=ABl=20Bouya?= Date: Tue, 14 May 2019 08:47:00 +0200 Subject: Start moving websites configuration to modules --- modules/default.nix | 2 + modules/private/default.nix | 2 +- modules/private/httpd-service-builder.nix | 8 +- modules/websites/default.nix | 148 ++++++++++++++++++++++++++++++ modules/websites/nosslVhost/index.html | 11 +++ 5 files changed, 166 insertions(+), 5 deletions(-) create mode 100644 modules/websites/default.nix create mode 100644 modules/websites/nosslVhost/index.html (limited to 'modules') diff --git a/modules/default.nix b/modules/default.nix index 6c49160..acb0bb5 100644 --- a/modules/default.nix +++ b/modules/default.nix @@ -8,4 +8,6 @@ mastodon = ./webapps/mastodon.nix; mediagoblin = ./webapps/mediagoblin.nix; peertube = ./webapps/peertube.nix; + + websites = ./websites; } // (if builtins.pathExists ./private then import ./private else {}) diff --git a/modules/private/default.nix b/modules/private/default.nix index ba46374..6c71af3 100644 --- a/modules/private/default.nix +++ b/modules/private/default.nix @@ -1,6 +1,6 @@ { # adatped from nixpkgs/nixos/modules/services/web-servers/apache-httpd/default.nix - httpdProd = import ./httpd-service-builder.nix { httpdName = "Prod"; withUsers = false; }; httpdInte = import ./httpd-service-builder.nix { httpdName = "Inte"; withUsers = false; }; + httpdProd = import ./httpd-service-builder.nix { httpdName = "Prod"; withUsers = false; }; httpdTools = import ./httpd-service-builder.nix { httpdName = "Tools"; withUsers = true; }; } diff --git a/modules/private/httpd-service-builder.nix b/modules/private/httpd-service-builder.nix index 0f0fe22..d049202 100644 --- a/modules/private/httpd-service-builder.nix +++ b/modules/private/httpd-service-builder.nix @@ -7,7 +7,7 @@ with lib; let - mainCfg = config.services."httpd${httpdName}"; + mainCfg = config.services.httpd."${httpdName}"; httpd = mainCfg.package.out; @@ -438,7 +438,7 @@ in options = { - services."httpd${httpdName}" = { + services.httpd."${httpdName}" = { enable = mkOption { type = types.bool; @@ -655,7 +655,7 @@ in ###### implementation - config = mkIf config.services."httpd${httpdName}".enable { + config = mkIf config.services.httpd."${httpdName}".enable { assertions = [ { assertion = mainCfg.enableSSL == true -> mainCfg.sslServerCert != null @@ -679,7 +679,7 @@ in environment.systemPackages = [httpd] ++ concatMap (svc: svc.extraPath) allSubservices; - services."httpd${httpdName}".phpOptions = + services.httpd."${httpdName}".phpOptions = '' ; Needed for PHP's mail() function. sendmail_path = sendmail -t -i diff --git a/modules/websites/default.nix b/modules/websites/default.nix new file mode 100644 index 0000000..6a18c8a --- /dev/null +++ b/modules/websites/default.nix @@ -0,0 +1,148 @@ +{ lib, config, ... }: with lib; +let + cfg = config.services.websites; +in +{ + options.services.websites = with types; mkOption { + default = {}; + description = "Each type of website to enable will target a distinct httpd server"; + type = attrsOf (submodule { + options = { + enable = mkEnableOption "Enable websites of this type"; + adminAddr = mkOption { + type = str; + description = "Admin e-mail address of the instance"; + }; + httpdName = mkOption { + type = str; + description = "Name of the httpd instance to assign this type to"; + }; + ips = mkOption { + type = listOf string; + default = []; + description = "ips to listen to"; + }; + modules = mkOption { + type = listOf str; + default = []; + description = "Additional modules to load in Apache"; + }; + extraConfig = mkOption { + type = listOf lines; + default = []; + description = "Additional configuration to append to Apache"; + }; + nosslVhost = mkOption { + description = "A default nossl vhost for captive portals"; + default = {}; + type = submodule { + options = { + enable = mkEnableOption "Add default no-ssl vhost for this instance"; + host = mkOption { + type = string; + description = "The hostname to use for this vhost"; + }; + root = mkOption { + type = path; + default = ./nosslVhost; + description = "The root folder to serve"; + }; + indexFile = mkOption { + type = string; + default = "index.html"; + description = "The index file to show."; + }; + }; + }; + }; + fallbackVhost = mkOption { + description = "The fallback vhost that will be defined as first vhost in Apache"; + type = submodule { + options = { + certName = mkOption { type = string; }; + hosts = mkOption { type = listOf string; }; + root = mkOption { type = nullOr path; }; + extraConfig = mkOption { type = listOf lines; default = []; }; + }; + }; + }; + vhostConfs = mkOption { + default = {}; + description = "List of vhosts to define for Apache"; + type = attrsOf (submodule { + options = { + certName = mkOption { type = string; }; + hosts = mkOption { type = listOf string; }; + root = mkOption { type = nullOr path; }; + extraConfig = mkOption { type = listOf lines; default = []; }; + }; + }); + }; + }; + }); + }; + + config.services.httpd = let + redirectVhost = ips: { # Should go last, catchall http -> https redirect + listen = map (ip: { inherit ip; port = 80; }) ips; + hostName = "redirectSSL"; + serverAliases = [ "*" ]; + enableSSL = false; + logFormat = "combinedVhost"; + documentRoot = "/var/lib/acme/acme-challenge"; + extraConfig = '' + RewriteEngine on + RewriteCond "%{REQUEST_URI}" "!^/\.well-known" + RewriteRule ^(.+) https://%{HTTP_HOST}$1 [R=301] + # To redirect in specific "VirtualHost *:80", do + # RedirectMatch 301 ^/((?!\.well-known.*$).*)$ https://host/$1 + # rather than rewrite + ''; + }; + nosslVhost = ips: cfg: { + listen = map (ip: { inherit ip; port = 80; }) ips; + hostName = cfg.host; + enableSSL = false; + logFormat = "combinedVhost"; + documentRoot = cfg.root; + extraConfig = '' + + DirectoryIndex ${cfg.indexFile} + AllowOverride None + Require all granted + + RewriteEngine on + RewriteRule ^/(.+) / [L] + + ''; + }; + toVhost = ips: vhostConf: { + enableSSL = true; + sslServerCert = "/var/lib/acme/${vhostConf.certName}/cert.pem"; + sslServerKey = "/var/lib/acme/${vhostConf.certName}/key.pem"; + sslServerChain = "/var/lib/acme/${vhostConf.certName}/chain.pem"; + logFormat = "combinedVhost"; + listen = map (ip: { inherit ip; port = 443; }) ips; + hostName = builtins.head vhostConf.hosts; + serverAliases = builtins.tail vhostConf.hosts or []; + documentRoot = vhostConf.root; + extraConfig = builtins.concatStringsSep "\n" vhostConf.extraConfig; + }; + in attrsets.mapAttrs' (name: icfg: attrsets.nameValuePair + icfg.httpdName (mkIf icfg.enable { + enable = true; + listen = map (ip: { inherit ip; port = 443; }) icfg.ips; + stateDir = "/run/httpd_${name}"; + logPerVirtualHost = true; + multiProcessingModule = "worker"; + inherit (icfg) adminAddr; + logFormat = "combinedVhost"; + extraModules = lists.unique icfg.modules; + extraConfig = builtins.concatStringsSep "\n" icfg.extraConfig; + virtualHosts = [ (toVhost icfg.ips icfg.fallbackVhost) ] + ++ optionals (icfg.nosslVhost.enable) [ (nosslVhost icfg.ips icfg.nosslVhost) ] + ++ (attrsets.mapAttrsToList (n: v: toVhost icfg.ips v) icfg.vhostConfs) + ++ [ (redirectVhost icfg.ips) ]; + }) + ) cfg; +} diff --git a/modules/websites/nosslVhost/index.html b/modules/websites/nosslVhost/index.html new file mode 100644 index 0000000..4401a80 --- /dev/null +++ b/modules/websites/nosslVhost/index.html @@ -0,0 +1,11 @@ + + + + No SSL site + + +

No SSL on this site

+

Use for wifi networks with login page that doesn't work well with + https.

+ + -- cgit v1.2.3