commit 4fb8253e575f9a8d2f2504d971ccea22f860d4d4
parent ff3a1905a53aa488b991f127c9d0240bae2b257d
Author: Daniel Barlow <dan@telent.net>
Date: Mon, 12 Aug 2024 22:57:21 +0100
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
Diffstat:
4 files changed, 103 insertions(+), 10 deletions(-)
diff --git a/examples/router-with-l2tp.nix b/examples/router-with-l2tp.nix
@@ -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";
+ };
+ };
};
};
diff --git a/modules/hostapd/service.nix b/modules/hostapd/service.nix
@@ -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
+ '';
}
diff --git a/modules/secrets/default.nix b/modules/secrets/default.nix
@@ -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";
+ };
+ };
+ };
+}
diff --git a/modules/secrets/outboard.nix b/modules/secrets/outboard.nix
@@ -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)}
+ )
+ '';
+}