nixos/evcc: support passing secrets with envsubst

and apply some newer best practices.
This commit is contained in:
Martin Weinelt 2025-03-07 09:54:59 +01:00
parent 369eaeefa3
commit 067732921f
No known key found for this signature in database
GPG key ID: 87C1E9888F856759
2 changed files with 64 additions and 20 deletions

View file

@ -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 = [

View file

@ -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"):