diff --git a/doc/build-helpers/trivial-build-helpers.chapter.md b/doc/build-helpers/trivial-build-helpers.chapter.md index 8af68845202f..31b42bd77426 100644 --- a/doc/build-helpers/trivial-build-helpers.chapter.md +++ b/doc/build-helpers/trivial-build-helpers.chapter.md @@ -533,7 +533,6 @@ writeScript "my-file" Contents of File '' ``` -::: This is equivalent to: @@ -546,6 +545,7 @@ writeTextFile { executable = true; } ``` +::: ### `writeScriptBin` {#trivial-builder-writeScriptBin} diff --git a/maintainers/maintainer-list.nix b/maintainers/maintainer-list.nix index b424321e6436..40b7cedf55bc 100644 --- a/maintainers/maintainer-list.nix +++ b/maintainers/maintainer-list.nix @@ -13173,6 +13173,13 @@ githubId = 191622; name = "Denys Pavlov"; }; + meator = { + email = "meator.dev@gmail.com"; + github = "meator"; + githubId = 67633081; + name = "meator"; + keys = [ { fingerprint = "7B0F 58A5 E0F1 A2EA 1157 8A73 1A14 CB34 64CB E5BF"; } ]; + }; meditans = { email = "meditans@gmail.com"; github = "meditans"; diff --git a/nixos/doc/manual/release-notes/rl-2411.section.md b/nixos/doc/manual/release-notes/rl-2411.section.md index 72afefaa963b..4bc221f10d75 100644 --- a/nixos/doc/manual/release-notes/rl-2411.section.md +++ b/nixos/doc/manual/release-notes/rl-2411.section.md @@ -43,6 +43,8 @@ - [Flood](https://flood.js.org/), a beautiful WebUI for various torrent clients. Available as [services.flood](options.html#opt-services.flood). +- [Firefly-iii Data Importer](https://github.com/firefly-iii/data-importer), a data importer for Firefly-III. Available as [services.firefly-iii-data-importer](options.html#opt-services.firefly-iii-data-importer) + - [QGroundControl], a ground station support and configuration manager for the PX4 and APM Flight Stacks. Available as [programs.qgroundcontrol](options.html#opt-programs.qgroundcontrol.enable). - [Eintopf](https://eintopf.info), community event and calendar web application. Available as [services.eintopf](options.html#opt-services.eintopf). diff --git a/nixos/lib/make-ext4-fs.nix b/nixos/lib/make-ext4-fs.nix index b8e1b8d24c48..8afc5555ced4 100644 --- a/nixos/lib/make-ext4-fs.nix +++ b/nixos/lib/make-ext4-fs.nix @@ -58,10 +58,18 @@ pkgs.stdenv.mkDerivation { # Make a crude approximation of the size of the target image. # If the script starts failing, increase the fudge factors here. numInodes=$(find ./rootImage | wc -l) - numDataBlocks=$(du -s -c -B 4096 --apparent-size ./rootImage | tail -1 | awk '{ print int($1 * 1.10) }') + numDataBlocks=$(du -s -c -B 4096 --apparent-size ./rootImage | tail -1 | awk '{ print int($1 * 1.20) }') bytes=$((2 * 4096 * $numInodes + 4096 * $numDataBlocks)) echo "Creating an EXT4 image of $bytes bytes (numInodes=$numInodes, numDataBlocks=$numDataBlocks)" + mebibyte=$(( 1024 * 1024 )) + # Round up to the nearest mebibyte. + # This ensures whole 512 bytes sector sizes in the disk image + # and helps towards aligning partitions optimally. + if (( bytes % mebibyte )); then + bytes=$(( ( bytes / mebibyte + 1) * mebibyte )) + fi + truncate -s $bytes $img faketime -f "1970-01-01 00:00:01" fakeroot mkfs.ext4 -L ${volumeLabel} -U ${uuid} -d ./rootImage $img diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix index b429d8709b7a..d66a7d6b9737 100644 --- a/nixos/modules/module-list.nix +++ b/nixos/modules/module-list.nix @@ -286,6 +286,7 @@ ./programs/ssh.nix ./programs/starship.nix ./programs/steam.nix + ./programs/streamcontroller.nix ./programs/streamdeck-ui.nix ./programs/sysdig.nix ./programs/system-config-printer.nix @@ -1401,6 +1402,7 @@ ./services/web-apps/ethercalc.nix ./services/web-apps/filesender.nix ./services/web-apps/firefly-iii.nix + ./services/web-apps/firefly-iii-data-importer.nix ./services/web-apps/flarum.nix ./services/web-apps/fluidd.nix ./services/web-apps/freshrss.nix diff --git a/nixos/modules/programs/streamcontroller.nix b/nixos/modules/programs/streamcontroller.nix new file mode 100644 index 000000000000..2f85d37625c8 --- /dev/null +++ b/nixos/modules/programs/streamcontroller.nix @@ -0,0 +1,22 @@ +{ + config, + lib, + pkgs, + ... +}: +let + cfg = config.programs.streamcontroller; +in +{ + options.programs.streamcontroller = { + enable = lib.mkEnableOption "StreamController"; + package = lib.mkPackageOption pkgs "streamcontroller" { default = [ "streamcontroller" ]; }; + }; + + config = lib.mkIf cfg.enable { + environment.systemPackages = [ cfg.package ]; + services.udev.packages = [ cfg.package ]; + }; + + meta.maintainers = with lib.maintainers; [ sifmelcara ]; +} diff --git a/nixos/modules/services/networking/wstunnel.nix b/nixos/modules/services/networking/wstunnel.nix index 439e1f8ea9b5..941e3e7366de 100644 --- a/nixos/modules/services/networking/wstunnel.nix +++ b/nixos/modules/services/networking/wstunnel.nix @@ -1,7 +1,8 @@ -{ config -, lib -, pkgs -, ... +{ + config, + lib, + pkgs, + ... }: let @@ -29,10 +30,9 @@ let package = lib.mkPackageOption pkgs "wstunnel" { }; - autoStart = - lib.mkEnableOption "starting this wstunnel instance automatically" // { - default = true; - }; + autoStart = lib.mkEnableOption "starting this wstunnel instance automatically" // { + default = true; + }; extraArgs = lib.mkOption { description = '' @@ -75,192 +75,198 @@ let }; }; - serverSubmodule = { config, ... }: { - options = commonOptions // { - listen = lib.mkOption { - description = '' - Address and port to listen on. - Setting the port to a value below 1024 will also give the process - the required `CAP_NET_BIND_SERVICE` capability. - ''; - type = lib.types.submodule hostPortSubmodule; - default = { - host = "0.0.0.0"; - port = if config.enableHTTPS then 443 else 80; - }; - defaultText = lib.literalExpression '' - { + serverSubmodule = + { config, ... }: + { + options = commonOptions // { + listen = lib.mkOption { + description = '' + Address and port to listen on. + Setting the port to a value below 1024 will also give the process + the required `CAP_NET_BIND_SERVICE` capability. + ''; + type = lib.types.submodule hostPortSubmodule; + default = { host = "0.0.0.0"; - port = if enableHTTPS then 443 else 80; - } - ''; - }; + port = if config.enableHTTPS then 443 else 80; + }; + defaultText = lib.literalExpression '' + { + host = "0.0.0.0"; + port = if enableHTTPS then 443 else 80; + } + ''; + }; - restrictTo = lib.mkOption { - description = '' - Accepted traffic will be forwarded only to this service. - ''; - type = lib.types.listOf (lib.types.submodule hostPortSubmodule); - default = [ ]; - example = [{ - host = "127.0.0.1"; - port = 51820; - }]; - }; + restrictTo = lib.mkOption { + description = '' + Accepted traffic will be forwarded only to this service. + ''; + type = lib.types.listOf (lib.types.submodule hostPortSubmodule); + default = [ ]; + example = [ + { + host = "127.0.0.1"; + port = 51820; + } + ]; + }; - enableHTTPS = lib.mkOption { - description = "Use HTTPS for the tunnel server."; - type = lib.types.bool; - default = true; - }; + enableHTTPS = lib.mkOption { + description = "Use HTTPS for the tunnel server."; + type = lib.types.bool; + default = true; + }; - tlsCertificate = lib.mkOption { - description = '' - TLS certificate to use instead of the hardcoded one in case of HTTPS connections. - Use together with `tlsKey`. - ''; - type = lib.types.nullOr lib.types.path; - default = null; - example = "/var/lib/secrets/cert.pem"; - }; + tlsCertificate = lib.mkOption { + description = '' + TLS certificate to use instead of the hardcoded one in case of HTTPS connections. + Use together with `tlsKey`. + ''; + type = lib.types.nullOr lib.types.path; + default = null; + example = "/var/lib/secrets/cert.pem"; + }; - tlsKey = lib.mkOption { - description = '' - TLS key to use instead of the hardcoded on in case of HTTPS connections. - Use together with `tlsCertificate`. - ''; - type = lib.types.nullOr lib.types.path; - default = null; - example = "/var/lib/secrets/key.pem"; - }; + tlsKey = lib.mkOption { + description = '' + TLS key to use instead of the hardcoded on in case of HTTPS connections. + Use together with `tlsCertificate`. + ''; + type = lib.types.nullOr lib.types.path; + default = null; + example = "/var/lib/secrets/key.pem"; + }; - useACMEHost = lib.mkOption { - description = '' - Use a certificate generated by the NixOS ACME module for the given host. - Note that this will not generate a new certificate - you will need to do so with `security.acme.certs`. - ''; - type = lib.types.nullOr lib.types.str; - default = null; - example = "example.com"; - }; - }; - }; - - clientSubmodule = { config, ... }: { - options = commonOptions // { - connectTo = lib.mkOption { - description = "Server address and port to connect to."; - type = lib.types.str; - example = "https://wstunnel.server.com:8443"; - }; - - localToRemote = lib.mkOption { - description = ''Listen on local and forwards traffic from remote.''; - type = lib.types.listOf (lib.types.str); - default = [ ]; - example = [ - "tcp://1212:google.com:443" - "unix:///tmp/wstunnel.sock:g.com:443" - ]; - }; - - remoteToLocal = lib.mkOption { - description = "Listen on remote and forwards traffic from local. Only tcp is supported"; - type = lib.types.listOf lib.types.str; - default = [ ]; - example = [ - "tcp://1212:google.com:443" - "unix://wstunnel.sock:g.com:443" - ]; - }; - - addNetBind = lib.mkEnableOption "Whether add CAP_NET_BIND_SERVICE to the tunnel service, this should be enabled if you want to bind port < 1024"; - - httpProxy = lib.mkOption { - description = '' - Proxy to use to connect to the wstunnel server (`USER:PASS@HOST:PORT`). - - ::: {.warning} - Passwords specified here will be world-readable in the Nix store! - To pass a password to the service, point the `environmentFile` option - to a file containing `PROXY_PASSWORD=` and set - this option to `:$PROXY_PASSWORD@:`. - Note however that this will also locally leak the passwords at - runtime via e.g. /proc//cmdline. - ::: - ''; - type = lib.types.nullOr lib.types.str; - default = null; - }; - - soMark = lib.mkOption { - description = '' - Mark network packets with the SO_MARK sockoption with the specified value. - Setting this option will also enable the required `CAP_NET_ADMIN` capability - for the systemd service. - ''; - type = lib.types.nullOr lib.types.ints.unsigned; - default = null; - }; - - upgradePathPrefix = lib.mkOption { - description = '' - Use a specific HTTP path prefix that will show up in the upgrade - request to the `wstunnel` server. - Useful when running `wstunnel` behind a reverse proxy. - ''; - type = lib.types.nullOr lib.types.str; - default = null; - example = "wstunnel"; - }; - - tlsSNI = lib.mkOption { - description = "Use this as the SNI while connecting via TLS. Useful for circumventing hostname-based firewalls."; - type = lib.types.nullOr lib.types.str; - default = null; - }; - - tlsVerifyCertificate = lib.mkOption { - description = "Whether to verify the TLS certificate of the server. It might be useful to set this to `false` when working with the `tlsSNI` option."; - type = lib.types.bool; - default = true; - }; - - # The original argument name `websocketPingFrequency` is a misnomer, as the frequency is the inverse of the interval. - websocketPingInterval = lib.mkOption { - description = "Frequency at which the client will send websocket ping to the server."; - type = lib.types.nullOr lib.types.ints.unsigned; - default = null; - }; - - upgradeCredentials = lib.mkOption { - description = '' - Use these credentials to authenticate during the HTTP upgrade request - (Basic authorization type, `USER:[PASS]`). - - ::: {.warning} - Passwords specified here will be world-readable in the Nix store! - To pass a password to the service, point the `environmentFile` option - to a file containing `HTTP_PASSWORD=` and set this - option to `:$HTTP_PASSWORD`. - Note however that this will also locally leak the passwords at runtime - via e.g. /proc//cmdline. - ::: - ''; - type = lib.types.nullOr lib.types.str; - default = null; - }; - - customHeaders = lib.mkOption { - description = "Custom HTTP headers to send during the upgrade request."; - type = lib.types.attrsOf lib.types.str; - default = { }; - example = { - "X-Some-Header" = "some-value"; + useACMEHost = lib.mkOption { + description = '' + Use a certificate generated by the NixOS ACME module for the given host. + Note that this will not generate a new certificate - you will need to do so with `security.acme.certs`. + ''; + type = lib.types.nullOr lib.types.str; + default = null; + example = "example.com"; + }; + }; + }; + + clientSubmodule = + { config, ... }: + { + options = commonOptions // { + connectTo = lib.mkOption { + description = "Server address and port to connect to."; + type = lib.types.str; + example = "https://wstunnel.server.com:8443"; + }; + + localToRemote = lib.mkOption { + description = ''Listen on local and forwards traffic from remote.''; + type = lib.types.listOf (lib.types.str); + default = [ ]; + example = [ + "tcp://1212:google.com:443" + "unix:///tmp/wstunnel.sock:g.com:443" + ]; + }; + + remoteToLocal = lib.mkOption { + description = "Listen on remote and forwards traffic from local. Only tcp is supported"; + type = lib.types.listOf lib.types.str; + default = [ ]; + example = [ + "tcp://1212:google.com:443" + "unix://wstunnel.sock:g.com:443" + ]; + }; + + addNetBind = lib.mkEnableOption "Whether add CAP_NET_BIND_SERVICE to the tunnel service, this should be enabled if you want to bind port < 1024"; + + httpProxy = lib.mkOption { + description = '' + Proxy to use to connect to the wstunnel server (`USER:PASS@HOST:PORT`). + + ::: {.warning} + Passwords specified here will be world-readable in the Nix store! + To pass a password to the service, point the `environmentFile` option + to a file containing `PROXY_PASSWORD=` and set + this option to `:$PROXY_PASSWORD@:`. + Note however that this will also locally leak the passwords at + runtime via e.g. /proc//cmdline. + ::: + ''; + type = lib.types.nullOr lib.types.str; + default = null; + }; + + soMark = lib.mkOption { + description = '' + Mark network packets with the SO_MARK sockoption with the specified value. + Setting this option will also enable the required `CAP_NET_ADMIN` capability + for the systemd service. + ''; + type = lib.types.nullOr lib.types.ints.unsigned; + default = null; + }; + + upgradePathPrefix = lib.mkOption { + description = '' + Use a specific HTTP path prefix that will show up in the upgrade + request to the `wstunnel` server. + Useful when running `wstunnel` behind a reverse proxy. + ''; + type = lib.types.nullOr lib.types.str; + default = null; + example = "wstunnel"; + }; + + tlsSNI = lib.mkOption { + description = "Use this as the SNI while connecting via TLS. Useful for circumventing hostname-based firewalls."; + type = lib.types.nullOr lib.types.str; + default = null; + }; + + tlsVerifyCertificate = lib.mkOption { + description = "Whether to verify the TLS certificate of the server. It might be useful to set this to `false` when working with the `tlsSNI` option."; + type = lib.types.bool; + default = true; + }; + + # The original argument name `websocketPingFrequency` is a misnomer, as the frequency is the inverse of the interval. + websocketPingInterval = lib.mkOption { + description = "Frequency at which the client will send websocket ping to the server."; + type = lib.types.nullOr lib.types.ints.unsigned; + default = null; + }; + + upgradeCredentials = lib.mkOption { + description = '' + Use these credentials to authenticate during the HTTP upgrade request + (Basic authorization type, `USER:[PASS]`). + + ::: {.warning} + Passwords specified here will be world-readable in the Nix store! + To pass a password to the service, point the `environmentFile` option + to a file containing `HTTP_PASSWORD=` and set this + option to `:$HTTP_PASSWORD`. + Note however that this will also locally leak the passwords at runtime + via e.g. /proc//cmdline. + ::: + ''; + type = lib.types.nullOr lib.types.str; + default = null; + }; + + customHeaders = lib.mkOption { + description = "Custom HTTP headers to send during the upgrade request."; + type = lib.types.attrsOf lib.types.str; + default = { }; + example = { + "X-Some-Header" = "some-value"; + }; }; }; }; - }; generateServerUnit = name: serverCfg: { name = "wstunnel-server-${name}"; @@ -270,22 +276,25 @@ let in { description = "wstunnel server - ${name}"; - requires = [ "network.target" "network-online.target" ]; - after = [ "network.target" "network-online.target" ]; + requires = [ + "network.target" + "network-online.target" + ]; + after = [ + "network.target" + "network-online.target" + ]; wantedBy = lib.optional serverCfg.autoStart "multi-user.target"; environment.RUST_LOG = serverCfg.loggingLevel; serviceConfig = { Type = "exec"; - EnvironmentFile = - lib.optional (serverCfg.environmentFile != null) serverCfg.environmentFile; + EnvironmentFile = lib.optional (serverCfg.environmentFile != null) serverCfg.environmentFile; DynamicUser = true; - SupplementaryGroups = - lib.optional (serverCfg.useACMEHost != null) certConfig.group; + SupplementaryGroups = lib.optional (serverCfg.useACMEHost != null) certConfig.group; PrivateTmp = true; - AmbientCapabilities = - lib.optionals (serverCfg.listen.port < 1024) [ "CAP_NET_BIND_SERVICE" ]; + AmbientCapabilities = lib.optionals (serverCfg.listen.port < 1024) [ "CAP_NET_BIND_SERVICE" ]; NoNewPrivileges = true; RestrictNamespaces = "uts ipc pid user cgroup"; ProtectSystem = "strict"; @@ -305,19 +314,16 @@ let script = with serverCfg; '' ${lib.getExe package} \ server \ - ${lib.cli.toGNUCommandLineShell { } ( - lib.recursiveUpdate - { - restrict-to = map hostPortToString restrictTo; - tls-certificate = if useACMEHost != null - then "${certConfig.directory}/fullchain.pem" - else "${tlsCertificate}"; - tls-private-key = if useACMEHost != null - then "${certConfig.directory}/key.pem" - else "${tlsKey}"; - } - extraArgs - )} \ + ${ + lib.cli.toGNUCommandLineShell { } ( + lib.recursiveUpdate { + restrict-to = map hostPortToString restrictTo; + tls-certificate = + if useACMEHost != null then "${certConfig.directory}/fullchain.pem" else "${tlsCertificate}"; + tls-private-key = if useACMEHost != null then "${certConfig.directory}/key.pem" else "${tlsKey}"; + } extraArgs + ) + } \ ${lib.escapeShellArg "${if enableHTTPS then "wss" else "ws"}://${hostPortToString listen}"} ''; }; @@ -327,21 +333,26 @@ let name = "wstunnel-client-${name}"; value = { description = "wstunnel client - ${name}"; - requires = [ "network.target" "network-online.target" ]; - after = [ "network.target" "network-online.target" ]; + requires = [ + "network.target" + "network-online.target" + ]; + after = [ + "network.target" + "network-online.target" + ]; wantedBy = lib.optional clientCfg.autoStart "multi-user.target"; environment.RUST_LOG = clientCfg.loggingLevel; serviceConfig = { Type = "exec"; - EnvironmentFile = - lib.optional (clientCfg.environmentFile != null) clientCfg.environmentFile; + EnvironmentFile = lib.optional (clientCfg.environmentFile != null) clientCfg.environmentFile; DynamicUser = true; PrivateTmp = true; AmbientCapabilities = - (lib.optionals clientCfg.addNetBind [ "CAP_NET_BIND_SERVICE" ]) ++ - (lib.optionals (clientCfg.soMark != null) [ "CAP_NET_ADMIN" ]); + (lib.optionals clientCfg.addNetBind [ "CAP_NET_BIND_SERVICE" ]) + ++ (lib.optionals (clientCfg.soMark != null) [ "CAP_NET_ADMIN" ]); NoNewPrivileges = true; RestrictNamespaces = "uts ipc pid user cgroup"; ProtectSystem = "strict"; @@ -361,22 +372,22 @@ let script = with clientCfg; '' ${lib.getExe package} \ client \ - ${lib.cli.toGNUCommandLineShell { } ( - lib.recursiveUpdate - { - local-to-remote = localToRemote; - remote-to-local = remoteToLocal; - http-headers = lib.mapAttrsToList (n: v: "${n}:${v}") customHeaders; - http-proxy = httpProxy; - socket-so-mark = soMark; - http-upgrade-path-prefix = upgradePathPrefix; - tls-sni-override = tlsSNI; - tls-verify-certificate = tlsVerifyCertificate; - websocket-ping-frequency-sec = websocketPingInterval; - http-upgrade-credentials = upgradeCredentials; - } - extraArgs - )} \ + ${ + lib.cli.toGNUCommandLineShell { } ( + lib.recursiveUpdate { + local-to-remote = localToRemote; + remote-to-local = remoteToLocal; + http-headers = lib.mapAttrsToList (n: v: "${n}:${v}") customHeaders; + http-proxy = httpProxy; + socket-so-mark = soMark; + http-upgrade-path-prefix = upgradePathPrefix; + tls-sni-override = tlsSNI; + tls-verify-certificate = tlsVerifyCertificate; + websocket-ping-frequency-sec = websocketPingInterval; + http-upgrade-credentials = upgradeCredentials; + } extraArgs + ) + } \ ${lib.escapeShellArg connectTo} ''; }; @@ -399,10 +410,12 @@ in enableHTTPS = true; tlsCertificate = "/var/lib/secrets/fullchain.pem"; tlsKey = "/var/lib/secrets/key.pem"; - restrictTo = [{ - host = "127.0.0.1"; - port = 51820; - }]; + restrictTo = [ + { + host = "127.0.0.1"; + port = 51820; + } + ]; }; }; }; @@ -429,40 +442,39 @@ in config = lib.mkIf cfg.enable { systemd.services = - (lib.mapAttrs' generateServerUnit (lib.filterAttrs (n: v: v.enable) cfg.servers)) // - (lib.mapAttrs' generateClientUnit (lib.filterAttrs (n: v: v.enable) cfg.clients)); + (lib.mapAttrs' generateServerUnit (lib.filterAttrs (n: v: v.enable) cfg.servers)) + // (lib.mapAttrs' generateClientUnit (lib.filterAttrs (n: v: v.enable) cfg.clients)); assertions = - (lib.mapAttrsToList - (name: serverCfg: { - assertion = - !(serverCfg.useACMEHost != null && serverCfg.tlsCertificate != null); - message = '' - Options services.wstunnel.servers."${name}".useACMEHost and services.wstunnel.servers."${name}".{tlsCertificate, tlsKey} are mutually exclusive. - ''; - }) - cfg.servers) ++ + (lib.mapAttrsToList (name: serverCfg: { + assertion = !(serverCfg.useACMEHost != null && serverCfg.tlsCertificate != null); + message = '' + Options services.wstunnel.servers."${name}".useACMEHost and services.wstunnel.servers."${name}".{tlsCertificate, tlsKey} are mutually exclusive. + ''; + }) cfg.servers) + ++ - (lib.mapAttrsToList - (name: serverCfg: { + (lib.mapAttrsToList (name: serverCfg: { assertion = - (serverCfg.tlsCertificate == null && serverCfg.tlsKey == null) || - (serverCfg.tlsCertificate != null && serverCfg.tlsKey != null); + (serverCfg.tlsCertificate == null && serverCfg.tlsKey == null) + || (serverCfg.tlsCertificate != null && serverCfg.tlsKey != null); message = '' services.wstunnel.servers."${name}".tlsCertificate and services.wstunnel.servers."${name}".tlsKey need to be set together. ''; - }) - cfg.servers) ++ + }) cfg.servers) + ++ - (lib.mapAttrsToList - (name: clientCfg: { + (lib.mapAttrsToList (name: clientCfg: { assertion = !(clientCfg.localToRemote == [ ] && clientCfg.remoteToLocal == [ ]); message = '' Either one of services.wstunnel.clients."${name}".localToRemote or services.wstunnel.clients."${name}".remoteToLocal must be set. ''; - }) - cfg.clients); + }) cfg.clients); }; - meta.maintainers = with lib.maintainers; [ alyaeanyx rvdp neverbehave ]; + meta.maintainers = with lib.maintainers; [ + alyaeanyx + rvdp + neverbehave + ]; } diff --git a/nixos/modules/services/web-apps/firefly-iii-data-importer.nix b/nixos/modules/services/web-apps/firefly-iii-data-importer.nix new file mode 100644 index 000000000000..5d1712a506d8 --- /dev/null +++ b/nixos/modules/services/web-apps/firefly-iii-data-importer.nix @@ -0,0 +1,301 @@ +{ + pkgs, + config, + lib, + ... +}: + +let + cfg = config.services.firefly-iii-data-importer; + + user = cfg.user; + group = cfg.group; + + defaultUser = "firefly-iii-data-importer"; + defaultGroup = "firefly-iii-data-importer"; + + artisan = "${cfg.package}/artisan"; + + env-file-values = lib.attrsets.mapAttrs' ( + n: v: lib.attrsets.nameValuePair (lib.strings.removeSuffix "_FILE" n) v + ) (lib.attrsets.filterAttrs (n: v: lib.strings.hasSuffix "_FILE" n) cfg.settings); + env-nonfile-values = lib.attrsets.filterAttrs (n: v: !lib.strings.hasSuffix "_FILE" n) cfg.settings; + + data-importer-maintenance = pkgs.writeShellScript "data-importer-maintenance.sh" '' + set -a + ${lib.strings.toShellVars env-nonfile-values} + ${lib.strings.concatLines ( + lib.attrsets.mapAttrsToList (n: v: "${n}=\"$(< ${v})\"") env-file-values + )} + set +a + ${artisan} package:discover + ${artisan} cache:clear + ${artisan} config:cache + ''; + + commonServiceConfig = { + Type = "oneshot"; + User = user; + Group = group; + StateDirectory = "firefly-iii-data-importer"; + ReadWritePaths = [ cfg.dataDir ]; + WorkingDirectory = cfg.package; + PrivateTmp = true; + PrivateDevices = true; + CapabilityBoundingSet = ""; + AmbientCapabilities = ""; + ProtectSystem = "strict"; + ProtectKernelTunables = true; + ProtectKernelModules = true; + ProtectControlGroups = true; + ProtectClock = true; + ProtectHostname = true; + ProtectHome = "tmpfs"; + ProtectKernelLogs = true; + ProtectProc = "invisible"; + ProcSubset = "pid"; + PrivateNetwork = false; + RestrictAddressFamilies = "AF_INET AF_INET6 AF_UNIX"; + SystemCallArchitectures = "native"; + SystemCallFilter = [ + "@system-service @resources" + "~@obsolete @privileged" + ]; + RestrictSUIDSGID = true; + RemoveIPC = true; + NoNewPrivileges = true; + RestrictRealtime = true; + RestrictNamespaces = true; + LockPersonality = true; + PrivateUsers = true; + }; + +in +{ + + options.services.firefly-iii-data-importer = { + enable = lib.mkEnableOption "Firefly III Data Importer"; + + user = lib.mkOption { + type = lib.types.str; + default = defaultUser; + description = "User account under which firefly-iii-data-importer runs."; + }; + + group = lib.mkOption { + type = lib.types.str; + default = if cfg.enableNginx then "nginx" else defaultGroup; + defaultText = "If `services.firefly-iii-data-importer.enableNginx` is true then `nginx` else ${defaultGroup}"; + description = '' + Group under which firefly-iii-data-importer runs. It is best to set this to the group + of whatever webserver is being used as the frontend. + ''; + }; + + dataDir = lib.mkOption { + type = lib.types.path; + default = "/var/lib/firefly-iii-data-importer"; + description = '' + The place where firefly-iii data importer stores its state. + ''; + }; + + package = lib.mkOption { + type = lib.types.package; + default = pkgs.firefly-iii-data-importer; + defaultText = lib.literalExpression "pkgs.firefly-iii-data-importer"; + description = '' + The firefly-iii-data-importer package served by php-fpm and the webserver of choice. + This option can be used to point the webserver to the correct root. It + may also be used to set the package to a different version, say a + development version. + ''; + apply = + firefly-iii-data-importer: + firefly-iii-data-importer.override (prev: { + dataDir = cfg.dataDir; + }); + }; + + enableNginx = lib.mkOption { + type = lib.types.bool; + default = false; + description = '' + Whether to enable nginx or not. If enabled, an nginx virtual host will + be created for access to firefly-iii data importer. If not enabled, then you may use + `''${config.services.firefly-iii-data-importer.package}` as your document root in + whichever webserver you wish to setup. + ''; + }; + + virtualHost = lib.mkOption { + type = lib.types.str; + default = "localhost"; + description = '' + The hostname at which you wish firefly-iii-data-importer to be served. If you have + enabled nginx using `services.firefly-iii-data-importer.enableNginx` then this will + be used. + ''; + }; + + poolConfig = lib.mkOption { + type = lib.types.attrsOf ( + lib.types.oneOf [ + lib.types.str + lib.types.int + lib.types.bool + ] + ); + default = { }; + defaultText = lib.literalExpression '' + { + "pm" = "dynamic"; + "pm.max_children" = 32; + "pm.start_servers" = 2; + "pm.min_spare_servers" = 2; + "pm.max_spare_servers" = 4; + "pm.max_requests" = 500; + } + ''; + description = '' + Options for the Firefly III Data Importer PHP pool. See the documentation on php-fpm.conf + for details on configuration directives. + ''; + }; + + settings = lib.mkOption { + default = { }; + description = '' + Options for firefly-iii data importer configuration. Refer to + for + details on supported values. All