first pass at outboard secrets

- a module to fetch them with http(s)
- a service using templating to consume them
- update an example to use it

needs service restarts
needs other services to use the template mechanism
needs tidying up
This commit is contained in:
Daniel Barlow 2024-08-12 22:57:21 +01:00
parent ff3a1905a5
commit 4fb8253e57
4 changed files with 103 additions and 10 deletions

View File

@ -54,10 +54,23 @@ in rec {
../modules/ppp
../modules/round-robin
../modules/health-check
../modules/secrets
../modules/profiles/gateway.nix
];
hostname = "thing";
services.wan-address-for-secrets = svc.network.address.build {
interface = config.hardware.networkInterfaces.wan;
family = "inet"; address ="10.0.0.10"; prefixLength = 24;
};
services.secrets = svc.secrets.outboard.build {
name = "secret-service";
url = "http://10.0.0.1/liminix/examples/secrets.json";
interval = 5;
dependencies = [ services.wan-address-for-secrets ];
};
services.wwan = svc.wwan.huawei-e3372.build {
apn = "data.uk";
username = "user";
@ -139,7 +152,13 @@ in rec {
hw_mode = "g";
channel = "6";
ieee80211n = 1;
} // wirelessConfig;
} // wirelessConfig //{
wpa_passphrase = {
service = config.services.secrets;
path = "wpa_passphrase";
};
};
"${rsecrets.ssid}5" = rec {
interface = config.hardware.networkInterfaces.wlan5;
hw_mode = "a";
@ -149,7 +168,12 @@ in rec {
vht_oper_centr_freq_seg0_idx = channel + 6;
ieee80211n = 1;
ieee80211ac = 1;
} // wirelessConfig;
} // wirelessConfig // {
wpa_passphrase = {
service = config.services.secrets;
path = "wpa_passphrase";
};
};
};
};

View File

@ -1,6 +1,7 @@
{
liminix
, hostapd
, output-template
, writeText
, lib
}:
@ -23,15 +24,28 @@ let
ctrl_interface = "/run/hostapd";
ctrl_interface_group = 0;
};
conf = writeText "hostapd.conf"
(concatStringsSep
"\n"
(mapAttrsToList
(name: value: "${name}=${toString value}")
(defaults // params)));
attrs = defaults // params ;
literal_or_output = o:
let typ = builtins.typeOf o;
in if typ == "string"
then builtins.toJSON o
else if typ == "int"
then builtins.toJSON o
else "output(${builtins.toJSON o.service}, ${builtins.toJSON o.path})";
format_value = n : v:
"${n}={{ ${literal_or_output v} }}";
conf =
(writeText "hostapd.conf.in"
((concatStringsSep
"\n"
(mapAttrsToList
format_value
attrs)) + "\n"));
in longrun {
inherit name;
dependencies = [ interface ];
run = "${hostapd}/bin/hostapd -i $(output ${interface} ifname) -P /run/${name}.pid -S ${conf}";
run = ''
${output-template}/bin/output-template '{{' '}}' < ${conf} > /run/${name}.conf
exec ${hostapd}/bin/hostapd -i $(output ${interface} ifname) -P /run/${name}.pid -S /run/${name}.conf
'';
}

View File

@ -0,0 +1,36 @@
## Secrets
## various ways to manage secrets without writing them to the
## nix store
{ lib, pkgs, config, ...}:
let
inherit (lib) mkOption types;
inherit (pkgs) liminix;
inherit (pkgs.liminix.services) longrun;
in {
options.system.service.secrets = {
outboard = mkOption {
description = "fetch secrets from external vault with https";
type = liminix.lib.types.serviceDefn;
};
};
config.system.service.secrets = {
outboard = config.system.callService ./outboard.nix {
url = mkOption {
description = "source url";
type = types.strMatching "https?://.*";
};
name = mkOption {
description = "service name";
type = types.str;
};
interval = mkOption {
type = types.int;
default = 30;
description = "how often to check the source, in minutes";
};
};
};
}

View File

@ -0,0 +1,19 @@
{
liminix, lib, http-fstree, serviceFns
}:
{ name, url, interval } :
let
inherit (liminix.services) oneshot longrun;
in longrun {
inherit name;
buildInputs = [ http-fstree ];
# this script runs once and expects the service superviser
# to restart it
run = ''
. ${serviceFns}
( in_outputs ${name}
${http-fstree}/bin/http-fstree ${url} .
sleep ${builtins.toString (interval * 60)}
)
'';
}