diff --git a/nixos/modules/services/home-automation/evcc.nix b/nixos/modules/services/home-automation/evcc.nix index 2a82b21bb580..32d327da9a84 100644 --- a/nixos/modules/services/home-automation/evcc.nix +++ b/nixos/modules/services/home-automation/evcc.nix @@ -1,10 +1,19 @@ { + config, lib, pkgs, - config, + utils, ... }: let + inherit (lib) + getExe + mkEnableOption + mkIf + mkOption + mkPackageOption + ; + cfg = config.services.evcc; format = pkgs.formats.yaml { }; @@ -17,27 +26,40 @@ in meta.maintainers = with lib.maintainers; [ hexa ]; options.services.evcc = with lib.types; { - enable = lib.mkEnableOption "EVCC, the extensible EV Charge Controller with PV integration"; + enable = mkEnableOption "EVCC, the extensible EV Charge Controller and Home Energy Management System"; - extraArgs = lib.mkOption { + package = mkPackageOption pkgs "evcc" { }; + + extraArgs = mkOption { type = listOf str; default = [ ]; description = '' - Extra arguments to pass to the evcc executable. + Extra arguments to pass to the `evcc` executable. ''; }; - settings = lib.mkOption { + environmentFile = mkOption { + type = nullOr path; + default = null; + example = /run/keys/evcc; + description = '' + File with environment variables to pass into the runtime environment. + + Useful to pass secrets into the configuration, that get applied using `envsubst`. + ''; + }; + + settings = mkOption { type = format.type; description = '' - evcc configuration as a Nix attribute set. + evcc configuration as a Nix attribute set. Supports substitution of secrets using `envsubst` from the `environmentFile`. Check for possible options in the sample [evcc.dist.yaml](https://github.com/andig/evcc/blob/${package.version}/evcc.dist.yaml). ''; }; }; - config = lib.mkIf cfg.enable { + config = mkIf cfg.enable { systemd.services.evcc = { wants = [ "network-online.target" ]; after = [ @@ -52,7 +74,21 @@ in getent ]; serviceConfig = { - ExecStart = "${package}/bin/evcc --config ${configFile} ${lib.escapeShellArgs cfg.extraArgs}"; + EnvironmentFile = lib.optionals (cfg.environmentFile != null) [ cfg.environmentFile ]; + ExecStartPre = utils.escapeSystemdExecArgs [ + (getExe pkgs.envsubst) + "-i" + configFile + "-o" + "/run/evcc/config.yaml" + ]; + ExecStart = utils.escapeSystemdExecArgs ( + [ + (getExe cfg.package) + "--config=/run/evcc/config.yaml" + ] + ++ cfg.extraArgs + ); CapabilityBoundingSet = [ "" ]; DeviceAllow = [ "char-ttyUSB" @@ -61,14 +97,6 @@ in DynamicUser = true; LockPersonality = true; MemoryDenyWriteExecute = true; - Restart = "on-failure"; - RestrictAddressFamilies = [ - "AF_INET" - "AF_INET6" - "AF_UNIX" - ]; - RestrictNamespaces = true; - RestrictRealtime = true; PrivateTmp = true; PrivateUsers = true; ProcSubset = "pid"; @@ -80,6 +108,15 @@ in ProtectKernelModules = true; ProtectKernelTunables = true; ProtectProc = "invisible"; + Restart = "on-failure"; + RestrictAddressFamilies = [ + "AF_INET" + "AF_INET6" + "AF_UNIX" + ]; + RestrictNamespaces = true; + RestrictRealtime = true; + RuntimeDirectory = "evcc"; StateDirectory = "evcc"; SystemCallArchitectures = "native"; SystemCallFilter = [ diff --git a/nixos/tests/evcc.nix b/nixos/tests/evcc.nix index 465dca58bc30..b9896687c2b6 100644 --- a/nixos/tests/evcc.nix +++ b/nixos/tests/evcc.nix @@ -1,5 +1,8 @@ { pkgs, lib, ... }: +let + port = "1234"; +in { name = "evcc"; meta.maintainers = with lib.maintainers; [ hexa ]; @@ -8,11 +11,15 @@ machine = { services.evcc = { enable = true; + # This is NOT a safe way to deal with secrets in production + environmentFile = pkgs.writeText "evcc-secrets" '' + PORT=${toString port} + ''; settings = { network = { schema = "http"; host = "localhost"; - port = 7070; + port = "$PORT"; }; log = "info"; @@ -82,14 +89,14 @@ start_all() machine.wait_for_unit("evcc.service") - machine.wait_for_open_port(7070) + machine.wait_for_open_port(${port}) with subtest("Check package version propagates into frontend"): machine.fail( - "curl --fail http://localhost:7070 | grep '0.0.1-alpha'" + "curl --fail http://localhost:${port} | grep '0.0.1-alpha'" ) machine.succeed( - "curl --fail http://localhost:7070 | grep '${pkgs.evcc.version}'" + "curl --fail http://localhost:${port} | grep '${pkgs.evcc.version}'" ) with subtest("Check journal for errors"):