diff options
Diffstat (limited to 'nixos/modules')
-rw-r--r-- | nixos/modules/module-list.nix | 2 | ||||
-rw-r--r-- | nixos/modules/services/misc/ntfy-sh.nix | 100 | ||||
-rw-r--r-- | nixos/modules/services/web-apps/changedetection-io.nix | 218 |
3 files changed, 320 insertions, 0 deletions
diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix index 98ac1948e6862..1a87df9897692 100644 --- a/nixos/modules/module-list.nix +++ b/nixos/modules/module-list.nix @@ -613,6 +613,7 @@ ./services/misc/nix-optimise.nix ./services/misc/nix-ssh-serve.nix ./services/misc/novacomd.nix + ./services/misc/ntfy-sh.nix ./services/misc/nzbget.nix ./services/misc/nzbhydra2.nix ./services/misc/octoprint.nix @@ -1072,6 +1073,7 @@ ./services/web-apps/calibre-web.nix ./services/web-apps/code-server.nix ./services/web-apps/baget.nix + ./services/web-apps/changedetection-io.nix ./services/web-apps/convos.nix ./services/web-apps/dex.nix ./services/web-apps/discourse.nix diff --git a/nixos/modules/services/misc/ntfy-sh.nix b/nixos/modules/services/misc/ntfy-sh.nix new file mode 100644 index 0000000000000..9d52fcf25364f --- /dev/null +++ b/nixos/modules/services/misc/ntfy-sh.nix @@ -0,0 +1,100 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.services.ntfy-sh; + + settingsFormat = pkgs.formats.yaml { }; +in + +{ + options.services.ntfy-sh = { + enable = mkEnableOption (mdDoc "[ntfy-sh](https://ntfy.sh), a push notification service"); + + package = mkOption { + type = types.package; + default = pkgs.ntfy-sh; + defaultText = literalExpression "pkgs.ntfy-sh"; + description = mdDoc "The ntfy.sh package to use."; + }; + + user = mkOption { + default = "ntfy-sh"; + type = types.str; + description = lib.mdDoc "User the ntfy-sh server runs under."; + }; + + group = mkOption { + default = "ntfy-sh"; + type = types.str; + description = lib.mdDoc "Primary group of ntfy-sh user."; + }; + + settings = mkOption { + type = types.submodule { freeformType = settingsFormat.type; }; + + default = { }; + + example = literalExpression '' + { + listen-http = ":8080"; + } + ''; + + description = mdDoc '' + Configuration for ntfy.sh, supported values are [here](https://ntfy.sh/docs/config/#config-options). + ''; + }; + }; + + config = + let + configuration = settingsFormat.generate "server.yml" cfg.settings; + in + mkIf cfg.enable { + # to configure access control via the cli + environment = { + etc."ntfy/server.yml".source = configuration; + systemPackages = [ cfg.package ]; + }; + + systemd.services.ntfy-sh = { + description = "Push notifications server"; + + wantedBy = [ "multi-user.target" ]; + after = [ "network.target" ]; + + serviceConfig = { + ExecStart = "${cfg.package}/bin/ntfy serve -c ${configuration}"; + User = cfg.user; + + AmbientCapabilities = "CAP_NET_BIND_SERVICE"; + PrivateTmp = true; + NoNewPrivileges = true; + CapabilityBoundingSet = "CAP_NET_BIND_SERVICE"; + ProtectSystem = "full"; + ProtectKernelTunables = true; + ProtectKernelModules = true; + ProtectKernelLogs = true; + ProtectControlGroups = true; + PrivateDevices = true; + RestrictSUIDSGID = true; + RestrictNamespaces = true; + RestrictRealtime = true; + MemoryDenyWriteExecute = true; + }; + }; + + users.groups = optionalAttrs (cfg.group == "ntfy-sh") { + ntfy-sh = { }; + }; + + users.users = optionalAttrs (cfg.user == "ntfy-sh") { + ntfy-sh = { + isSystemUser = true; + group = cfg.group; + }; + }; + }; +} diff --git a/nixos/modules/services/web-apps/changedetection-io.nix b/nixos/modules/services/web-apps/changedetection-io.nix new file mode 100644 index 0000000000000..83d8b32c0c833 --- /dev/null +++ b/nixos/modules/services/web-apps/changedetection-io.nix @@ -0,0 +1,218 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.services.changedetection-io; +in +{ + options.services.changedetection-io = { + enable = mkEnableOption (lib.mdDoc "changedetection-io"); + + user = mkOption { + default = "changedetection-io"; + type = types.str; + description = lib.mdDoc '' + User account under which changedetection-io runs. + ''; + }; + + group = mkOption { + default = "changedetection-io"; + type = types.str; + description = lib.mdDoc '' + Group account under which changedetection-io runs. + ''; + }; + + listenAddress = mkOption { + type = types.str; + default = "localhost"; + description = lib.mdDoc "Address the server will listen on."; + }; + + port = mkOption { + type = types.port; + default = 5000; + description = lib.mdDoc "Port the server will listen on."; + }; + + datastorePath = mkOption { + type = types.str; + default = "/var/lib/changedetection-io"; + description = lib.mdDoc '' + The directory used to store all data for changedetection-io. + ''; + }; + + baseURL = mkOption { + type = types.nullOr types.str; + default = null; + example = "https://changedetection-io.example"; + description = lib.mdDoc '' + The base url used in notifications and `{base_url}` token. + ''; + }; + + behindProxy = mkOption { + type = types.bool; + default = false; + description = lib.mdDoc '' + Enable this option when changedetection-io runs behind a reverse proxy, so that it trusts X-* headers. + It is recommend to run changedetection-io behind a TLS reverse proxy. + ''; + }; + + environmentFile = mkOption { + type = types.nullOr types.path; + default = null; + example = "/run/secrets/changedetection-io.env"; + description = lib.mdDoc '' + Securely pass environment variabels to changedetection-io. + + This can be used to set for example a frontend password reproducible via `SALTED_PASS` + which convinetly also deactivates nags about the hosted version. + `SALTED_PASS` should be 64 characters long while the first 32 are the salt and the second the frontend password. + It can easily be retrieved from the settings file when first set via the frontend with the following command: + ``jq -r .settings.application.password /var/lib/changedetection-io/url-watches.json`` + ''; + }; + + webDriverSupport = mkOption { + type = types.bool; + default = false; + description = lib.mdDoc '' + Enable support for fetching web pages using WebDriver and Chromium. + This starts a headless chromium controlled by puppeteer in an oci container. + + ::: {.note} + Playwright can currently leak memory. + See https://github.com/dgtlmoon/changedetection.io/wiki/Playwright-content-fetcher#playwright-memory-leak + ::: + ''; + }; + + playwrightSupport = mkOption { + type = types.bool; + default = false; + description = lib.mdDoc '' + Enable support for fetching web pages using playwright and Chromium. + This starts a headless Chromium controlled by puppeteer in an oci container. + + ::: {.note} + Playwright can currently leak memory. + See https://github.com/dgtlmoon/changedetection.io/wiki/Playwright-content-fetcher#playwright-memory-leak + ::: + ''; + }; + + chromePort = mkOption { + type = types.port; + default = 4444; + description = lib.mdDoc '' + A free port on which webDriverSupport or playwrightSupport listen on localhost. + ''; + }; + }; + + config = mkIf cfg.enable { + assertions = [ + { + assertion = !((cfg.webDriverSupport == true) && (cfg.playwrightSupport == true)); + message = "'services.changedetection-io.webDriverSupport' and 'services.changedetion-io.playwrightSupport' cannot be used together."; + } + ]; + + systemd = let + defaultStateDir = cfg.datastorePath == "/var/lib/changedetection-io"; + in { + services.changedetection-io = { + wantedBy = [ "mutli-user.target" ]; + after = [ "network.target" ]; + preStart = '' + mkdir -p ${cfg.datastorePath} + ''; + serviceConfig = { + User = cfg.user; + Group = cfg.group; + StateDirectory = mkIf defaultStateDir "changedetion-io"; + StateDirectoryMode = mkIf defaultStateDir "0750"; + WorkingDirectory = cfg.datastorePath; + Environment = lib.optional (cfg.baseURL != null) "BASE_URL=${cfg.baseURL}" + ++ lib.optional cfg.behindProxy "USE_X_SETTINGS=1" + ++ lib.optional cfg.webDriverSupport "WEBDRIVER_URL=http://127.0.0.1:${toString cfg.chromePort}/wd/hub" + ++ lib.optional cfg.playwrightSupport "PLAYWRIGHT_DRIVER_URL=ws://127.0.0.1:${toString cfg.chromePort}/?stealth=1&--disable-web-security=true"; + EnvironmentFile = mkIf (cfg.environmentFile != null) cfg.environmentFile; + ExecStart = '' + ${pkgs.changedetection-io}/bin/changedetection.py \ + -h ${cfg.listenAddress} -p ${toString cfg.port} -d ${cfg.datastorePath} + ''; + ProtectHome = true; + ProtectSystem = true; + Restart = "on-failure"; + }; + }; + tmpfiles.rules = mkIf defaultStateDir [ + "d ${cfg.datastorePath} 0750 ${cfg.user} ${cfg.group} - -" + ]; + }; + + users = { + users = optionalAttrs (cfg.user == "changedetection-io") { + "changedetection-io" = { + isSystemUser = true; + group = "changedetection-io"; + }; + }; + + groups = optionalAttrs (cfg.group == "changedetection-io") { + "changedetection-io" = { }; + }; + }; + + virtualisation = { + oci-containers.containers = lib.mkMerge [ + (mkIf cfg.webDriverSupport { + changedetection-io-webdriver = { + image = "selenium/standalone-chrome"; + environment = { + VNC_NO_PASSWORD = "1"; + SCREEN_WIDTH = "1920"; + SCREEN_HEIGHT = "1080"; + SCREEN_DEPTH = "24"; + }; + ports = [ + "127.0.0.1:${toString cfg.chromePort}:4444" + ]; + volumes = [ + "/dev/shm:/dev/shm" + ]; + extraOptions = [ "--network=bridge" ]; + }; + }) + + (mkIf cfg.playwrightSupport { + changedetection-io-playwright = { + image = "browserless/chrome"; + environment = { + SCREEN_WIDTH = "1920"; + SCREEN_HEIGHT = "1024"; + SCREEN_DEPTH = "16"; + ENABLE_DEBUGGER = "false"; + PREBOOT_CHROME = "true"; + CONNECTION_TIMEOUT = "300000"; + MAX_CONCURRENT_SESSIONS = "10"; + CHROME_REFRESH_TIME = "600000"; + DEFAULT_BLOCK_ADS = "true"; + DEFAULT_STEALTH = "true"; + }; + ports = [ + "127.0.0.1:${toString cfg.chromePort}:3000" + ]; + extraOptions = [ "--network=bridge" ]; + }; + }) + ]; + }; + }; +} |