nix-ota/nix/modules/agent.nix

88 lines
3.2 KiB
Nix
Raw Normal View History

self: { config, lib, pkgs, ... }:
let
cfg = config.services.nix-ota-agent;
inherit (lib) mkEnableOption mkOption mkIf types;
in {
options.services.nix-ota-agent = {
enable = mkEnableOption "nix-ota device agent";
package = mkOption {
type = types.package;
default = self.packages.${pkgs.system}.nix-ota-agent;
};
server = mkOption { type = types.str; example = "https://ota.example.com"; };
channel = mkOption { type = types.str; default = "prod"; };
deviceId = mkOption { type = types.str; example = "device-001"; };
publicKey = mkOption {
type = types.nullOr types.str;
default = null;
description = "Base64-encoded ed25519 verifying key. The agent will reject manifests not signed by the matching private key. Mutually exclusive with publicKeyFile.";
};
publicKeyFile = mkOption {
type = types.nullOr types.path;
default = null;
description = "Path to a file containing the base64-encoded verifying key. Use this if you need to write the key at runtime (e.g. from an orchestration system).";
};
interval = mkOption { type = types.int; default = 60; };
healthCmd = mkOption { type = types.nullOr types.str; default = null; };
cacheUrl = mkOption {
type = types.str;
description = "Substituter URL added to nix.settings.substituters so `nix copy` can fetch from it.";
};
cachePublicKey = mkOption {
type = types.str;
description = "Trusted public key of the binary cache (the one that signs store paths).";
};
};
config = mkIf cfg.enable {
assertions = [{
assertion = (cfg.publicKey != null) != (cfg.publicKeyFile != null);
message = "services.nix-ota-agent: set exactly one of publicKey or publicKeyFile.";
}];
nix.settings = {
substituters = [ cfg.cacheUrl ];
trusted-public-keys = [ cfg.cachePublicKey ];
experimental-features = [ "nix-command" "flakes" ];
};
environment.etc."nix-ota/public.key" = lib.mkIf (cfg.publicKey != null) {
text = cfg.publicKey;
};
systemd.services.nix-ota-agent = {
description = "nix-ota device agent (oneshot)";
after = [ "network-online.target" ];
wants = [ "network-online.target" ];
serviceConfig = {
Type = "oneshot";
StateDirectory = "nix-ota";
};
environment = {
NIX_OTA_SERVER = cfg.server;
NIX_OTA_CHANNEL = cfg.channel;
NIX_OTA_DEVICE_ID = cfg.deviceId;
NIX_OTA_PUBLIC_KEY_FILE = if cfg.publicKeyFile != null
then toString cfg.publicKeyFile
else "/etc/nix-ota/public.key";
NIX_OTA_STATE_DIR = "/var/lib/nix-ota";
} // lib.optionalAttrs (cfg.healthCmd != null) {
NIX_OTA_HEALTH_CMD = cfg.healthCmd;
};
script = ''
export PATH=${lib.makeBinPath [ pkgs.nix pkgs.systemd pkgs.coreutils pkgs.bash ]}:$PATH
exec ${cfg.package}/bin/nix-ota-agent --once
'';
};
systemd.timers.nix-ota-agent = {
wantedBy = [ "timers.target" ];
timerConfig = {
OnBootSec = "1min";
OnUnitActiveSec = "${toString cfg.interval}s";
Unit = "nix-ota-agent.service";
};
};
};
}