summary refs log tree commit diff
diff options
context:
space:
mode:
authorEmery Hemingway <ehmry@posteo.net>2020-05-03 16:00:31 +0530
committerEmery Hemingway <ehmry@posteo.net>2020-05-21 12:11:13 +0530
commitac97b19a2ab6901af26cee6878ffa73de68d1fcb (patch)
treeeba5a614169c64bf12c7c41053a552c75a1afe95
parent067ad3cc946a7a8435bec88a653be1db93b442db (diff)
downloadnixpkgs-ac97b19a2ab6901af26cee6878ffa73de68d1fcb.tar
nixpkgs-ac97b19a2ab6901af26cee6878ffa73de68d1fcb.tar.gz
nixpkgs-ac97b19a2ab6901af26cee6878ffa73de68d1fcb.tar.bz2
nixpkgs-ac97b19a2ab6901af26cee6878ffa73de68d1fcb.tar.lz
nixpkgs-ac97b19a2ab6901af26cee6878ffa73de68d1fcb.tar.xz
nixpkgs-ac97b19a2ab6901af26cee6878ffa73de68d1fcb.tar.zst
nixpkgs-ac97b19a2ab6901af26cee6878ffa73de68d1fcb.zip
nixos/yggdrasil: change config priority, persistentKeys
Favor the configuration in "configFile" over "config" to allow
"configFile" to override "config" without a system rebuild.

Add a "persistentKeys" option to generate keys and addresses that
persist across service restarts. This is useful for self-configuring
boot media.
-rw-r--r--nixos/modules/services/networking/yggdrasil.nix139
-rw-r--r--nixos/tests/yggdrasil.nix1
2 files changed, 72 insertions, 68 deletions
diff --git a/nixos/modules/services/networking/yggdrasil.nix b/nixos/modules/services/networking/yggdrasil.nix
index ecd1406b483..0fe9a200a1b 100644
--- a/nixos/modules/services/networking/yggdrasil.nix
+++ b/nixos/modules/services/networking/yggdrasil.nix
@@ -1,55 +1,17 @@
 { config, lib, pkgs, ... }:
 with lib;
 let
+  keysPath = "/var/lib/yggdrasil/keys.json";
+
   cfg = config.services.yggdrasil;
-  configProvided = (cfg.config != {});
-  configAsFile = (if configProvided then
-                   toString (pkgs.writeTextFile {
-                     name = "yggdrasil-conf";
-                     text = builtins.toJSON cfg.config;
-                   })
-                   else null);
-  configFileProvided = (cfg.configFile != null);
-  generateConfig = (
-    if configProvided && configFileProvided then
-      "${pkgs.jq}/bin/jq -s add ${configAsFile} ${cfg.configFile}"
-    else if configProvided then
-      "cat ${configAsFile}"
-    else if configFileProvided then
-      "cat ${cfg.configFile}"
-    else
-      "${cfg.package}/bin/yggdrasil -genconf"
-  );
+  configProvided = cfg.config != { };
+  configFileProvided = cfg.configFile != null;
 
 in {
   options = with types; {
     services.yggdrasil = {
       enable = mkEnableOption "the yggdrasil system service";
 
-      configFile = mkOption {
-        type =  nullOr str;
-        default = null;
-        example = "/run/keys/yggdrasil.conf";
-        description = ''
-          A file which contains JSON configuration for yggdrasil.
-
-          You do not have to supply a complete configuration, as
-          yggdrasil will use default values for anything which is
-          omitted.  If the encryption and signing keys are omitted,
-          yggdrasil will generate new ones each time the service is
-          started, resulting in a random IPv6 address on the yggdrasil
-          network each time.
-
-          If both this option and <option>config</option> are
-          supplied, they will be combined, with values from
-          <option>config</option> taking precedence.
-
-          You can use the command <code>nix-shell -p yggdrasil --run
-          "yggdrasil -genconf -json"</code> to generate a default
-          JSON configuration.
-        '';
-      };
-
       config = mkOption {
         type = attrs;
         default = {};
@@ -66,16 +28,21 @@ in {
           Configuration for yggdrasil, as a Nix attribute set.
 
           Warning: this is stored in the WORLD-READABLE Nix store!
-          Therefore, it is not appropriate for private keys.  If you
-          do not specify the keys, yggdrasil will generate a new set
-          each time the service is started, creating a random IPv6
-          address on the yggdrasil network each time.
+          Therefore, it is not appropriate for private keys. If you
+          wish to specify the keys, use <option>configFile</option>.
+
+          If the <option>persistentKeys</option> is enabled then the
+          keys that are generated during activation will override
+          those in <option>config</option> or
+          <option>configFile</option>.
+
+          If no keys are specified then ephemeral keys are generated
+          and the Yggdrasil interface will have a random IPv6 address
+          each time the service is started, this is the default.
 
-          If you wish to specify the keys, use
-          <option>configFile</option>.  If both
-          <option>configFile</option> and <option>config</option> are
-          supplied, they will be combined, with values from
-          <option>config</option> taking precedence.
+          If both <option>configFile</option> and <option>config</option>
+          are supplied, they will be combined, with values from
+          <option>configFile</option> taking precedence.
 
           You can use the command <code>nix-shell -p yggdrasil --run
           "yggdrasil -genconf"</code> to generate default
@@ -83,12 +50,21 @@ in {
         '';
       };
 
+      configFile = mkOption {
+        type = nullOr path;
+        default = null;
+        example = "/run/keys/yggdrasil.conf";
+        description = ''
+          A file which contains JSON configuration for yggdrasil.
+          See the <option>config</option> option for more information.
+        '';
+      };
+
       group = mkOption {
         type = types.str;
         default = "root";
         example = "wheel";
-        description =
-          "Group to grant acces to the Yggdrasil control socket.";
+        description = "Group to grant acces to the Yggdrasil control socket.";
       };
 
       openMulticastPort = mkOption {
@@ -126,37 +102,64 @@ in {
         defaultText = "pkgs.yggdrasil";
         description = "Yggdrasil package to use.";
       };
+
+      persistentKeys = mkEnableOption ''
+        If enabled then keys will be generated once and Yggdrasil
+        will retain the same IPv6 address when the service is
+        restarted. Keys are stored at ${keysPath}.
+      '';
+
     };
   };
 
-  config = mkIf cfg.enable {
-    assertions = [
-      { assertion = config.networking.enableIPv6;
-        message = "networking.enableIPv6 must be true for yggdrasil to work";
-      }
-    ];
+  config = mkIf cfg.enable (let binYggdrasil = cfg.package + "/bin/yggdrasil";
+  in {
+    assertions = [{
+      assertion = config.networking.enableIPv6;
+      message = "networking.enableIPv6 must be true for yggdrasil to work";
+    }];
+
+    system.activationScripts.yggdrasil = mkIf cfg.persistentKeys ''
+      if [ ! -e ${keysPath} ]
+      then
+        mkdir -p ${builtins.dirOf keysPath}
+        ${binYggdrasil} -genconf -json \
+          | ${pkgs.jq}/bin/jq \
+              'to_entries|map(select(.key|endswith("Key")))|from_entries' \
+          > ${keysPath}
+        chmod 600 ${keysPath}
+      fi
+    '';
 
     systemd.services.yggdrasil = {
       description = "Yggdrasil Network Service";
-      path = [ cfg.package ] ++ optional (configProvided && configFileProvided) pkgs.jq;
       bindsTo = [ "network-online.target" ];
       after = [ "network-online.target" ];
       wantedBy = [ "multi-user.target" ];
 
-      preStart = ''
-        ${generateConfig} | yggdrasil -normaliseconf -useconf > /run/yggdrasil/yggdrasil.conf
-      '';
+      preStart =
+        (if configProvided || configFileProvided || cfg.persistentKeys then
+          "echo "
+
+          + (lib.optionalString configProvided
+            "'${builtins.toJSON cfg.config}'")
+          + (lib.optionalString configFileProvided "$(cat ${cfg.configFile})")
+          + (lib.optionalString cfg.persistentKeys "$(cat ${keysPath})")
+          + " | ${pkgs.jq}/bin/jq -s add | ${binYggdrasil} -normaliseconf -useconf"
+        else
+          "${binYggdrasil} -genconf") + " > /run/yggdrasil/yggdrasil.conf";
 
       serviceConfig = {
-        ExecStart = "${cfg.package}/bin/yggdrasil -useconffile /run/yggdrasil/yggdrasil.conf";
+        ExecStart =
+          "${binYggdrasil} -useconffile /run/yggdrasil/yggdrasil.conf";
         ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
         Restart = "always";
 
         Group = cfg.group;
         RuntimeDirectory = "yggdrasil";
         RuntimeDirectoryMode = "0750";
-        BindReadOnlyPaths = mkIf configFileProvided
-          [ "${cfg.configFile}" ];
+        BindReadOnlyPaths = lib.optional configFileProvided cfg.configFile
+          ++ lib.optional cfg.persistentKeys keysPath;
 
         # TODO: as of yggdrasil 0.3.8 and systemd 243, yggdrasil fails
         # to set up the network adapter when DynamicUser is set.  See
@@ -191,6 +194,6 @@ in {
 
     # Make yggdrasilctl available on the command line.
     environment.systemPackages = [ cfg.package ];
-  };
-  meta.maintainers = with lib.maintainers; [ gazally ];
+  });
+  meta.maintainers = with lib.maintainers; [ gazally ehmry ];
 }
diff --git a/nixos/tests/yggdrasil.nix b/nixos/tests/yggdrasil.nix
index 468fcf67127..9ceb7974733 100644
--- a/nixos/tests/yggdrasil.nix
+++ b/nixos/tests/yggdrasil.nix
@@ -85,6 +85,7 @@ in import ./make-test-python.nix ({ pkgs, ...} : {
             MulticastInterfaces = [ "eth1" ];
             LinkLocalTCPPort = 43210;
           };
+          persistentKeys = true;
         };
       };
     };