{ config, pkgs, lib, ... }: with builtins // lib; let cfg = config.services.cooklang-chef; args = escapeShellArgs ([ "--port" (toString cfg.port) "--path" cfg.recipeRoot ] ++ cfg.extraArgs); in { options.services.cooklang-chef = { enable = mkEnableOption "cooklang-chef webserver"; package = mkPackageOption pkgs "cooklang-chef" {}; recipeRoot = mkOption { type = types.path; default = "${cfg.dataDir}/cookbook"; }; user = mkOption { type = types.str; default = "cooklang-chef"; }; group = mkOption { type = types.str; default = "cooklang-chef"; }; dataDir = mkOption { type = types.path; default = "/var/lib/cooklang-chef"; description = '' Home directory for ${cfg.user}. ''; }; port = mkOption { type = types.port; default = 9080; }; extraArgs = mkOption { type = with types; listOf str; default = []; }; nginx = { enable = mkEnableOption "nginx reverse proxy"; virtualHost = mkOption { type = types.str; example = mdDoc '' cook.example.com ''; }; location = mkOption { type = types.str; default = "/"; }; }; restartOnChanges = mkEnableOption "Restart cooklang-chef service on changes to ${cfg.recipeRoot}"; }; config = mkIf cfg.enable { users = { users = mkIf (cfg.user == "cooklang-chef") { ${cfg.user} = { isSystemUser = true; group = cfg.group; home = cfg.dataDir; }; }; groups = mkIf (cfg.group == "cooklang-chef") { ${cfg.group} = {}; }; }; systemd.services.cooklang-chef = { description = "A website for serving cooklang cookbooks"; after = [ "network.target" ]; requires = [ "network.target" ]; wantedBy = [ "multi-user.target" ]; unitConfig.RequiresMountsFor = cfg.recipeRoot; serviceConfig = { StateDirectory = "cooklang-chef cooklang-chef/.config"; StateDirectoryMode = "0750"; ExecStart = "${getExe cfg.package} serve ${args}"; User = cfg.user; Group = cfg.group; IPAddressAllow = "localhost"; IPAddressDeny = "any"; LockPersonality = true; MemoryDenyWriteExecute = true; NoNewPrivileges = true; PrivateDevices = true; PrivateTmp = true; PrivateUsers = true; ProcSubset = "pid"; ProtectClock = true; ProtectControlGroups = true; ProtectHome = true; ProtectHostname = true; ProtectKernelLogs = true; ProtectKernelModules = true; ProtectKernelTunables = true; ProtectProc = "invisible"; ProtectSystem = "strict"; ReadWritePaths = cfg.dataDir; RemoveIPC = true; RestrictAddressFamilies = [ "AF_INET" "AF_INET6" ]; RestrictNamespaces = true; RestrictRealtime = true; RestrictSUIDSGID = true; SystemCallArchitectures = "native"; SystemCallFilter = [ "@system-service" "~@privileged" "~@resources" ]; UMask = "0027"; WorkingDirectory = cfg.dataDir; }; }; systemd.services.cooklang-chef-watcher = mkIf cfg.restartOnChanges { description = "Restart cooklang-chef on file change"; after = [ "network.target" ]; wantedBy = [ "multi-user.target" ]; serviceConfig = { ExecStart = "/run/current-system/sw/bin/systemctl restart cooklang-chef.service"; Type = "oneshot"; }; }; systemd.paths.cooklang-chef-watcher = mkIf cfg.restartOnChanges { description = "Restart cooklang-chef on file change"; wantedBy = [ "multi-user.target" ]; pathConfig = { Unit = "cooklang-chef-watcher.service"; PathChanged = cfg.recipeRoot; }; }; services.nginx = mkIf cfg.nginx.enable { enable = mkDefault true; virtualHosts.${cfg.nginx.virtualHost} = { locations.${cfg.nginx.location} = { proxyPass = "http://127.0.0.1:${toString cfg.port}"; }; }; }; }; }