summaryrefslogtreecommitdiff
path: root/sys/web
diff options
context:
space:
mode:
Diffstat (limited to 'sys/web')
-rw-r--r--sys/web/default.nix6
-rw-r--r--sys/web/nginx.nix45
-rw-r--r--sys/web/php-fpm.nix152
3 files changed, 203 insertions, 0 deletions
diff --git a/sys/web/default.nix b/sys/web/default.nix
new file mode 100644
index 0000000..6c73506
--- /dev/null
+++ b/sys/web/default.nix
@@ -0,0 +1,6 @@
+{
+ imports = [
+ ./nginx.nix
+ ./php-fpm.nix
+ ];
+}
diff --git a/sys/web/nginx.nix b/sys/web/nginx.nix
new file mode 100644
index 0000000..db2d27d
--- /dev/null
+++ b/sys/web/nginx.nix
@@ -0,0 +1,45 @@
+{ config, lib, ... }:
+with lib; let
+ cfg = config.local.web;
+ inherit (config.local) domains;
+in
+{
+ options.local.web = {
+ enable = mkEnableOption "web server";
+ };
+
+ config = mkIf cfg.enable {
+ services.nginx = {
+ enable = true;
+
+ recommendedGzipSettings = true;
+ recommendedOptimisation = true;
+ recommendedProxySettings = true;
+ recommendedTlsSettings = true;
+
+ sslDhparam = config.security.dhparams.params.nginx.path;
+
+ clientMaxBodySize = "42M";
+
+ virtualHosts = {
+ ${domains.host.www} = {
+ serverAliases = [ domains.host.main ];
+ useACMEHost = domains.host.main;
+ forceSSL = true;
+ };
+ };
+ };
+
+ security = {
+ acme.certs.${domains.host.main} = {
+ inherit (config.services.nginx) group;
+ };
+
+ dhparams.params.nginx = { };
+ };
+
+ networking.firewall.allowedTCPPorts = [ 80 443 ];
+
+ local.certs.host.enable = true;
+ };
+}
diff --git a/sys/web/php-fpm.nix b/sys/web/php-fpm.nix
new file mode 100644
index 0000000..65276ba
--- /dev/null
+++ b/sys/web/php-fpm.nix
@@ -0,0 +1,152 @@
+# Based on <https://gist.github.com/aanderse/3344baef2c3b86c8a1e98e63bd9256ea>
+# See also:
+# - <https://albert.cx/20181125-use-separate-systemd-units-for-php-fpm-pools>
+# - <https://freedesktop.org/wiki/Software/systemd/DaemonSocketActivation/>
+
+{ config, lib, pkgs, ... }:
+with lib; let
+ cfg = config.services.php-fpm-isolated;
+
+ configFile = { pool, poolOpts, runtimeDir, sockFile, pidFile }:
+ let
+ config = {
+ global = {
+ daemonize = false;
+ error_log = "syslog";
+ pid = pidFile;
+ };
+
+ "${pool}" =
+ let
+ enforced = {
+ inherit (poolOpts) user group;
+ listen = sockFile;
+ };
+
+ defaults = {
+ "pm" = "dynamic";
+ "pm.max_children" = 16;
+ "pm.min_spare_servers" = 1;
+ "pm.max_spare_servers" = 4;
+ "pm.start_servers" = 1;
+ "catch_workers_output" = true;
+ "php_admin_flag[log_errors]" = true;
+ "env[PATH]" = makeBinPath [ pkgs.php ];
+ };
+
+ env = mapAttrs'
+ (name: value: {
+ name = "env[${name}]";
+ value = "\"${escape [ "\"" ] value}\"";
+ })
+ poolOpts.env;
+ in
+ defaults // poolOpts.config // env // enforced;
+ };
+ in
+ (pkgs.formats.ini { }).generate "php-fpm-pool-${pool}.conf" config;
+in
+{
+ options.services.php-fpm-isolated.pools = mkOption {
+ default = { };
+
+ type = with types; attrsOf (submodule {
+ options = {
+ enable = mkEnableOption "PHP-FPM pool";
+
+ user = mkOption {
+ type = str;
+ };
+
+ group = mkOption {
+ type = str;
+ };
+
+ unveil = mkOption {
+ type = listOf (either package str);
+ };
+
+ env = mkOption {
+ type = attrsOf str;
+ default = { };
+ };
+
+ config = mkOption {
+ type = attrsOf (oneOf [ int str bool ]);
+ default = { };
+ };
+ };
+ });
+ };
+
+ config.systemd =
+ let
+ php-fpm = "${pkgs.php}/bin/php-fpm";
+
+ unitsFor = pool: poolOpts:
+ let
+ runtimeBase = "php-fpm-isolated/${pool}";
+ runtimeDir = "/run/${runtimeBase}";
+ pidFile = "${runtimeDir}/${pool}.pid";
+ sockFile = "${runtimeDir}/${pool}.sock";
+ in
+ {
+ name = "php-fpm-pool-${pool}";
+
+ value.service = {
+ description = "PHP-FPM process manager for pool '${pool}'";
+ after = [ "network.target" ];
+
+ confinement.enable = true;
+
+ serviceConfig = {
+ Type = "notify";
+ ExecReload = "${pkgs.coreutils}/bin/kill -USR2 $MAINPID";
+ PIDFile = pidFile;
+
+ Environment = "FPM_SOCKETS=${sockFile}=3";
+
+ ExecStart =
+ let
+ fpmConfig = configFile {
+ inherit pool poolOpts runtimeDir sockFile pidFile;
+ };
+ in
+ "${php-fpm} --nodaemonize --fpm-config ${fpmConfig} --pid ${pidFile}";
+
+ PrivateTmp = true;
+ PrivateNetwork = true;
+ PrivateDevices = true;
+ # XXX: We need AF_NETLINK to make the sendmail SUID binary from postfix work
+ RestrictAddressFamilies = "AF_UNIX AF_INET AF_INET6 AF_NETLINK";
+
+ User = poolOpts.user;
+ Group = poolOpts.group;
+ RuntimeDirectory = runtimeBase;
+
+ BindReadOnlyPaths =
+ let
+ unveiled = map builtins.toString poolOpts.unveil;
+ in
+ [ "/run/systemd/journal/socket" ] ++ unveiled;
+ };
+ };
+
+ value.socket = {
+ description = "PHP-FPM socket for pool '${pool}'";
+ listenStreams = [ sockFile ];
+
+ socketConfig = {
+ User = poolOpts.user;
+ Group = poolOpts.group;
+ };
+ };
+ };
+
+ units = mapAttrs' unitsFor (filterAttrs (_: pool: pool.enable) cfg.pools);
+ in
+ {
+ sockets = mapAttrs (_: unit: unit.socket) units;
+ services = mapAttrs (_: unit: unit.service) units;
+ };
+}