From d9a5f6b7c2e6a3ecc318484de1d316c0934f4ed3 Mon Sep 17 00:00:00 2001 From: Tim Van Baak Date: Tue, 13 Dec 2022 20:03:28 -0800 Subject: [PATCH] Add nixos module --- flake.nix | 1 + module.nix | 154 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 155 insertions(+) create mode 100644 module.nix diff --git a/flake.nix b/flake.nix index 22ca729..9caf0d9 100644 --- a/flake.nix +++ b/flake.nix @@ -46,5 +46,6 @@ projectDir = ./.; }; }; + nixosModules.default = import ./module.nix self; }; } diff --git a/module.nix b/module.nix new file mode 100644 index 0000000..e9043bc --- /dev/null +++ b/module.nix @@ -0,0 +1,154 @@ +flake: { config, lib, pkgs, ... }: + +let + inherit (lib) mkIf mkOption types; + + cfg = config.services.inquisitor; +in +{ + options = { + services.inquisitor = { + enable = mkOption { + type = types.bool; + default = true; + description = "Enable the Inquisitor aggregator."; + }; + + listen.addr = mkOption { + type = types.str; + default = "0.0.0.0"; + description = "Listen address passed to nginx."; + }; + + listen.port = mkOption { + type = types.port; + default = 80; + description = "Listen port passed to nginx."; + }; + }; + }; + + config = + let + # Get the inquisitor package from the flake. + inquisitor = flake.packages.${pkgs.stdenv.hostPlatform.system}.default; + + # Define the inquisitor state directory. + stateDir = "/var/lib/inquisitor"; + + # Define an scp helper for item callbacks to use. + scp-helper = pkgs.writeShellScriptBin "scp-helper" '' + ${pkgs.openssh}/bin/scp -i ${stateDir}/.ssh/inquisitor.key -oStrictHostKeyChecking=no "$@" + ''; + + # Define the inquisitor service user. + svcUser = { + name = "inquisitor"; + group = "inquisitor"; + description = "Inquisitor service user"; + isSystemUser = true; + shell = pkgs.bashInteractive; + packages = [ inquisitor pkgs.cron ]; + }; + + # Create a config file pointing to the state directory. + inqConfig = pkgs.writeTextFile { + name = "inquisitor.conf"; + text = '' + DataPath = ${stateDir}/data/ + SourcePath = ${stateDir}/sources/ + CachePath = ${stateDir}/cache/ + Verbose = false + LogFile = ${stateDir}/inquisitor.log + ''; + }; + + # Create a setup script to ensure the service directory state. + inqSetup = pkgs.writeShellScript "inquisitor-setup.sh" '' + # Ensure the required directories exist. + ${pkgs.coreutils}/bin/mkdir -p ${stateDir}/data/inquisitor/ + ${pkgs.coreutils}/bin/mkdir -p ${stateDir}/sources/ + ${pkgs.coreutils}/bin/mkdir -p ${stateDir}/cache/ + if [ ! -f ${stateDir}/data/inquisitor/state ]; then + ${pkgs.coreutils}/bin/echo "{}" > ${stateDir}/data/inquisitor/state + fi + + # Ensure the service owns the folders. + ${pkgs.coreutils}/bin/chown -R ${svcUser.name} ${stateDir} + + # Ensure the scp helper is present + if [ -f ${stateDir}/scp-helper ]; then + ${pkgs.coreutils}/bin/rm ${stateDir}/scp-helper + fi + ln -s -t ${stateDir}/scp-helper ${scp-helper}/bin/scp-helper + ''; + + # Create a run script for the service. + inqRun = pkgs.writeShellScript "inquisitor-run.sh" '' + cd ${stateDir} + ${inquisitor}/bin/gunicorn \ + --bind=localhost:24133 \ + --workers=4 \ + --timeout 120 \ + --log-level debug \ + "inquisitor.app:wsgi()" + ''; + + # Create a wrapper to execute the cli as the service user. + # (needed to avoid creating files in the state dir the service can't read) + inqWrapper = pkgs.writeShellScriptBin "inq" '' + sudo --user=${svcUser.name} ${inquisitor}/bin/inquisitor "$@" + ''; + in mkIf cfg.enable + { + users.users.inquisitor = svcUser; + users.groups.inquisitor = {}; + + # Link the config in /etc to avoid envvar shenanigans + environment.etc."inquisitor.conf".source = inqConfig; + + # Give all users the wrapper program. + environment.systemPackages = [ inqWrapper ]; + # Allow the sudo in the cli wrapper without password. + security.sudo.extraRules = [{ + commands = [{ + command = "${inquisitor}/bin/inquisitor"; + options = [ "NOPASSWD" ]; + }]; + runAs = svcUser.name; + groups = [ "users" ]; + }]; + + # Run the setup script on activation. + system.activationScripts.inquisitorSetup = "${inqSetup}"; + + # Set up the inquisitor service. + systemd.services.inquisitor = { + description = "Inquisitor server"; + script = "${inqRun}"; + serviceConfig = { + User = svcUser.name; + Type = "simple"; + }; + wantedBy = [ "multi-user.target" ]; + after = [ "network.target" ]; + enable = true; + }; + + # Set up the nginx reverse proxy to the server. + services.nginx.enable = true; + services.nginx.virtualHosts.inquisitorHost = { + listen = [ cfg.listen ]; + locations."/".extraConfig = '' + access_log /var/log/nginx/access.inquisitor.log; + proxy_buffering off; + proxy_pass http://localhost:24133/; + ''; + }; + networking.firewall.allowedTCPPorts = [ cfg.listen.port ]; + + # Enable cron so the service can use it to schedule fetches. + services.cron.enable = true; + }; +} +