git.delta.rocks / jrsonnet / refs/commits / b47cfbf24126

difftreelog

feat secret management

Yaroslav Bolyukin2021-09-19parent: #2a625ab.patch.diff
in: trunk

14 files changed

added.envrcdiffbeforeafterboth
--- /dev/null
+++ b/.envrc
@@ -0,0 +1,5 @@
+if ! has nix_direnv_version || ! nix_direnv_version 1.4.0; then
+  source_url "https://raw.githubusercontent.com/nix-community/nix-direnv/1.4.0/direnvrc" "sha256-4XfVDjv75eHMWN4G725VW7BoOV4Vl3vAabK4YXIfPyE="
+fi
+
+use flake
modifiedflake.lockdiffbeforeafterboth
--- a/flake.lock
+++ b/flake.lock
@@ -1,23 +1,57 @@
 {
   "nodes": {
+    "flake-utils": {
+      "locked": {
+        "lastModified": 1631561581,
+        "narHash": "sha256-3VQMV5zvxaVLvqqUrNz3iJelLw30mIVSfZmAaauM3dA=",
+        "owner": "numtide",
+        "repo": "flake-utils",
+        "rev": "7e5bf3925f6fbdfaf50a2a7ca0be2879c4261d19",
+        "type": "github"
+      },
+      "original": {
+        "owner": "numtide",
+        "repo": "flake-utils",
+        "type": "github"
+      }
+    },
     "nixpkgs": {
       "locked": {
-        "lastModified": 1605344435,
-        "narHash": "sha256-Xx66M/eTwLc97sge6y210qMBZe2qwrpSqWagfEAOF0M=",
+        "lastModified": 1632011270,
+        "narHash": "sha256-UyEYSWTKB3boKu5JX/TrQtnAgaYvfSWT61VU8ZT1juk=",
         "owner": "nixos",
         "repo": "nixpkgs",
-        "rev": "d67b00e8f0b378b1700e12f5b8e68c0706839c9a",
+        "rev": "7f59b4b5295b58659064a91d0bcc8e8a11d0b351",
         "type": "github"
       },
       "original": {
         "owner": "nixos",
+        "ref": "staging-next",
         "repo": "nixpkgs",
         "type": "github"
       }
     },
     "root": {
       "inputs": {
-        "nixpkgs": "nixpkgs"
+        "flake-utils": "flake-utils",
+        "nixpkgs": "nixpkgs",
+        "rust-overlay": "rust-overlay"
+      }
+    },
+    "rust-overlay": {
+      "flake": false,
+      "locked": {
+        "lastModified": 1631758650,
+        "narHash": "sha256-7OAtO2V8omtPaoFBASTfPA5m8MzN5LX8agk0k5p8dH0=",
+        "owner": "oxalica",
+        "repo": "rust-overlay",
+        "rev": "4e79ebf67452cca4ae938180728f9f513e828d5b",
+        "type": "github"
+      },
+      "original": {
+        "owner": "oxalica",
+        "repo": "rust-overlay",
+        "type": "github"
       }
     }
   },
modifiedflake.nixdiffbeforeafterboth
--- a/flake.nix
+++ b/flake.nix
@@ -2,9 +2,32 @@
   description = "NixOS configuration management";
 
   inputs = {
-    nixpkgs.url = "github:nixos/nixpkgs";
+    nixpkgs.url = "github:nixos/nixpkgs/staging-next";
+    rust-overlay = { url = "github:oxalica/rust-overlay"; flake = false; };
+    flake-utils.url = "github:numtide/flake-utils";
   };
-  outputs = { self, nixpkgs }: with nixpkgs.lib; rec {
+  outputs = { self, rust-overlay, flake-utils, nixpkgs }: with nixpkgs.lib; rec {
     lib = import ./lib;
-  };
+  } // flake-utils.lib.eachDefaultSystem (system:
+    let
+      pkgs = import nixpkgs
+        {
+          inherit system; overlays = [ (import rust-overlay) ];
+        };
+      llvmPkgs = pkgs.buildPackages.llvmPackages_11;
+      rust = (pkgs.rustChannelOf { date = "2021-08-16"; channel = "nightly"; }).default.override { extensions = [ "rust-src" ]; };
+      rustPlatform = pkgs.makeRustPlatform { cargo = rust; rustc = rust; };
+    in
+    {
+      devShell = (pkgs.mkShell.override { stdenv = llvmPkgs.stdenv; }) {
+        nativeBuildInputs = with pkgs; [
+          rust
+          cargo-edit
+          cargo-udeps
+
+          pkgconfig
+          openssl
+        ];
+      };
+    });
 }
modifiedlib/default.nixdiffbeforeafterboth
--- a/lib/default.nix
+++ b/lib/default.nix
@@ -1,29 +1,11 @@
 {
-  fleetConfiguration = { nixpkgs, hosts, ... }@allConfig:
+  fleetConfiguration = { data, nixpkgs, hosts, ... }@allConfig:
     let
-      config = builtins.removeAttrs allConfig [ "nixpkgs" ];
+      config = builtins.removeAttrs allConfig [ "nixpkgs" "data" ];
     in
     rec {
       root = nixpkgs.lib.evalModules {
-        modules =
-          (import ../modules/modules.nix) ++ [
-            config
-            (
-              { ... }: {
-                options = { };
-                config = {
-                  # Secret data is available only via fleet build-systems
-                  secrets =
-                    if builtins?getEnv then
-                      let
-                        stringData = builtins.getEnv "SECRET_DATA";
-                      in
-                      if stringData != "" then (builtins.fromJSON stringData) else { }
-                    else { };
-                };
-              }
-            )
-          ];
+        modules = (import ../modules/modules.nix { inherit data; }) ++ [ config ];
         specialArgs = {
           inherit nixpkgs;
           fleet = import ./fleetLib.nix {
modifiedlib/fleetLib.nixdiffbeforeafterboth
--- a/lib/fleetLib.nix
+++ b/lib/fleetLib.nix
@@ -1,34 +1,5 @@
 # Shared functions for fleet configuration, available as `fleet` module argument
 { nixpkgs, hosts }: with nixpkgs.lib; rec {
-  mkSecret =
-    let
-      system = builtins.currentSystem;
-      pkgs = import nixpkgs { inherit system; };
-      keys = builtins.getEnv "RAGE_KEYS";
-      encryptCmd = "rage ${keys} -a";
-      impuritySource = builtins.getEnv "IMPURITY_SOURCE";
-    in
-    f:
-    let
-      data = f { inherit pkgs encryptCmd; };
-    in
-    builtins.derivation {
-      inherit system;
-      name = "secret";
-
-      builder = "${pkgs.bash}/bin/bash";
-      args = [
-        (
-          pkgs.writeTextFile {
-            name = "./build-${impuritySource}.sh";
-            text = data.script;
-            executable = true;
-          }
-        )
-      ];
-
-      PATH = "${pkgs.coreutils}/bin:${pkgs.rage}/bin${builtins.concatStringsSep "" (builtins.map (n: ":${n}/bin") data.utils)}";
-    };
   # Modules can't register hosts because of infinite recursion
   hostNames = attrNames hosts;
   hostsToAttrs = f: listToAttrs (
addedmodules/hosts.nixdiffbeforeafterboth
--- /dev/null
+++ b/modules/hosts.nix
@@ -0,0 +1,47 @@
+{ lib, fleet, ... }: with lib;
+let
+  host = with types; {
+    options = {
+      modules = mkOption {
+        type = listOf anything;
+        description = "List of nixos modules";
+        default = [ ];
+      };
+      network = mkOption {
+        type = submodule {
+          options = {
+            fleetIp = {
+              type = str;
+              description = "Ip which is available to all hosts in fleet";
+            };
+          };
+        };
+        description = "Network definition of host";
+      };
+      system = mkOption {
+        type = str;
+        description = "Type of system";
+      };
+      encryptionKey = mkOption {
+        type = str;
+        description = "Encryption key";
+      };
+    };
+  };
+in
+{
+  options = with types; {
+    hosts = mkOption {
+      type = attrsOf (submodule host);
+      default = { };
+      description = "Configurations of individual hosts";
+    };
+  };
+  config.hosts = fleet.hostsToAttrs (host: {
+    modules = [
+      ({ ... }: {
+        nixpkgs.overlays = [ (import ../pkgs) ];
+      })
+    ];
+  });
+}
modifiedmodules/modules.nixdiffbeforeafterboth
1[1{ data }: [
2 ./networking/wireguard2 ./hosts.nix
3 ./root.nix3 ./secrets
4 data
4]5]
56
deletedmodules/networking/wireguard/default.nixdiffbeforeafterboth
--- a/modules/networking/wireguard/default.nix
+++ /dev/null
@@ -1,106 +0,0 @@
-{ config, lib, fleet, ... }: with lib; with fleet; let
-  cfg = config.networking.wireguard;
-  genWgKey = { owners }: {
-    inherit owners;
-    generator = mkSecret (
-      { pkgs, encryptCmd }: {
-        utils = [ pkgs.wireguard-tools ];
-        script = ''
-          key=$(wg genkey)
-          pub=$(echo $key | wg pubkey)
-
-          mkdir -p $out
-          echo $key | ${encryptCmd} >$out/key
-          echo $pub >$out/pub_key
-        '';
-      }
-    );
-  };
-  genWgPsk = { owners }: {
-    inherit owners;
-    generator = mkSecret (
-      { pkgs, encryptCmd }: {
-        utils = [ pkgs.wireguard-tools ];
-        script = ''
-          key=$(wg genpsk)
-
-          mkdir -p $out
-          echo $key | ${encryptCmd} >$out/key
-        '';
-      }
-    );
-  };
-
-  hostKeys = listToAttrs (
-    map
-      (
-        hostName: {
-          name = "wg-key-${hostName}";
-          value = genWgKey {
-            owners = [ hostName ];
-          };
-        }
-      )
-      hostNames
-  );
-  psks = listToAttrs (
-    map
-      (
-        { a, b }: {
-          name = "wg-psk-${a}-${b}";
-          value = genWgPsk {
-            owners = [ a b ];
-          };
-        }
-      )
-      hostsCartesian
-  );
-in
-{
-  options.networking.wireguard = with types; {
-    enable = mkEnableOption "wireguard";
-    interface = mkOption {
-      type = str;
-      description = "Interface name for wireguard network";
-      default = "fleet";
-    };
-    port = mkOption {
-      type = int;
-      description = "Port, on which wireguard interface should listen";
-      default = 51871;
-    };
-    allowedIPs = mkOption {
-      type = attrsOf (listOf str);
-      description = "Per host allowed ips";
-    };
-  };
-  config = mkIf cfg.enable {
-    secrets =
-      (hostKeys // psks);
-    hosts = hostsToAttrs (
-      hostName: {
-        modules = [
-          {
-            networking.wireguard.enable = true;
-            networking.wireguard.interfaces.fleetwg = {
-              privateKeyFile = "/run/secrets/wg-key-${hostName}";
-              peers = map
-                (
-                  peer:
-                  let
-                    pair = hostsPair hostName peer;
-                  in
-                  {
-                    publicKey = config.secrets."wg-key-${peer}".data.key;
-                    presharedKey = "/run/secrets/wg-psk-${pair.a}-${pair.b}";
-                    allowedIPs = cfg.allowedIPs.${peer};
-                  }
-                )
-                hostNames;
-            };
-          }
-        ];
-      }
-    );
-  };
-}
deletedmodules/networking/wireguard/wgbuilder.shdiffbeforeafterboth
--- a/modules/networking/wireguard/wgbuilder.sh
+++ /dev/null
@@ -1,7 +0,0 @@
-#!/bin/sh
-key=$($WG genkey)
-pub=$(echo $key | $WG pubkey)
-
-$COREUTILS/bin/mkdir -p $out
-echo $key | $RAGE $recipients >$out/key
-echo $pub >$out/pub_key
deletedmodules/root.nixdiffbeforeafterboth
--- a/modules/root.nix
+++ /dev/null
@@ -1,76 +0,0 @@
-{ lib, ... }: with lib;
-let
-  secret = with types; {
-    options = {
-      owners = mkOption {
-        type = listOf str;
-        description = ''
-          List of hosts to encrypt secret for
-
-          Secrets would be decrypted and stored to /run/secrets/$\{name} on owners
-        '';
-      };
-      generator = mkOption {
-        type = types.package;
-        description = "Derivation to execute for secret generation";
-      };
-      expireIn = mkOption {
-        type = nullOr int;
-        description = "Time in hours, in which this secret should be regenerated";
-        default = null;
-      };
-      data = mkOption {
-        type = attrsOf anything;
-        description = "Generated secret data, do not set it yourself";
-        default = { };
-      };
-    };
-  };
-  host = with types; {
-    options = {
-      modules = mkOption {
-        type = listOf anything;
-        description = "List of nixos modules";
-        default = [ ];
-      };
-      network = mkOption {
-        type = submodule {
-          options = {
-            fleetIp = {
-              type = str;
-              description = "Ip which is available to all hosts in fleet";
-            };
-          };
-        };
-        description = "Network definition of host";
-      };
-      system = mkOption {
-        type = str;
-        description = "Type of system";
-      };
-    };
-  };
-in
-{
-  options = with types; {
-    hosts = mkOption {
-      type = attrsOf (submodule host);
-      default = { };
-      description = "Configurations of individual hosts";
-    };
-    secrets = mkOption {
-      type = attrsOf (submodule secret);
-      default = { };
-      description = "Secrets";
-    };
-  };
-  config = {
-    secrets =
-      if builtins?getEnv then
-        let
-          stringData = builtins.getEnv "SECRET_DATA";
-        in
-        if stringData != "" then (builtins.fromJSON stringData) else { }
-      else { };
-  };
-}
addedmodules/secrets/default.nixdiffbeforeafterboth
--- /dev/null
+++ b/modules/secrets/default.nix
@@ -0,0 +1,56 @@
+{ lib, fleet, config, ... }: with lib;
+let
+  secret = with types; {
+    options = {
+      owners = mkOption {
+        type = listOf str;
+        description = ''
+          List of hosts to encrypt secret for
+
+          Secrets would be decrypted and stored to /run/secrets/$\{name} on owners
+        '';
+      };
+      generator = mkOption {
+        type = package;
+        description = "Derivation to execute for secret generation";
+      };
+      expireIn = mkOption {
+        type = nullOr int;
+        description = "Time in hours, in which this secret should be regenerated";
+        default = null;
+      };
+      public = mkOption {
+        type = nullOr str;
+        description = "Secret public data";
+        default = null;
+      };
+      secret = mkOption {
+        type = str;
+        description = "Encrypted secret data";
+      };
+    };
+  };
+in
+{
+  options = with types; {
+    secrets = mkOption {
+      type = attrsOf (submodule secret);
+      default = { };
+      description = "Secrets";
+    };
+  };
+  config = with fleet; {
+    hosts = hostsToAttrs (host: {
+      modules = [
+        ./nixosModule.nix
+        {
+          secrets = mapAttrs
+            (secretName: v: {
+              inherit (v) public secret;
+            })
+            (filterAttrs (_: v: builtins.elem host v.owners) config.secrets);
+        }
+      ];
+    });
+  };
+}
addedmodules/secrets/nixosModule.nixdiffbeforeafterboth
--- /dev/null
+++ b/modules/secrets/nixosModule.nix
@@ -0,0 +1,60 @@
+{ lib, config, pkgs, ... }: with lib;
+let
+  sysConfig = config;
+  secretType = types.submodule ({ config, ... }: {
+    config = {
+      path = mkOptionDefault "/run/secrets/${config._module.args.name}";
+    };
+    options = {
+      public = mkOption {
+        type = types.nullOr types.str;
+        description = "Secret public data";
+        default = null;
+      };
+      secret = mkOption {
+        type = types.str;
+        description = "Encrypted secret data";
+      };
+      mode = mkOption {
+        type = types.str;
+        description = "Secret mode";
+        default = "0440";
+      };
+      owner = mkOption {
+        type = types.str;
+        description = "Owner of the secret";
+        default = "root";
+      };
+      group = mkOption {
+        type = types.str;
+        description = "Group of the secret";
+        default = sysConfig.users.users.${config.owner}.group;
+      };
+
+      path = mkOption {
+        type = types.str;
+        readOnly = true;
+        description = "Path to the decrypted secret";
+      };
+    };
+  });
+  secretsFile = pkgs.writeTextFile {
+    name = "secrets.json";
+    text = builtins.toJSON config.secrets;
+  };
+in
+{
+  options = {
+    secrets = mkOption {
+      type = types.attrsOf secretType;
+      default = { };
+      description = "Host-local secrets";
+    };
+  };
+  config = {
+    system.activationScripts.decryptSecrets = ''
+      1>&2 echo "setting up secrets"
+      ${pkgs.fleet-install-secrets}/bin/fleet-install-secrets ${secretsFile}
+    '';
+  };
+}
addedpkgs/default.nixdiffbeforeafterboth
--- /dev/null
+++ b/pkgs/default.nix
@@ -0,0 +1,5 @@
+pkgs: super:
+with pkgs;
+{
+  fleet-install-secrets = callPackage ./fleet-install-secrets.nix { };
+}
addedpkgs/fleet-install-secrets.nixdiffbeforeafterboth
--- /dev/null
+++ b/pkgs/fleet-install-secrets.nix
@@ -0,0 +1,13 @@
+{ rustPlatform }:
+
+rustPlatform.buildRustPackage rec {
+  pname = "fleet-install-secrets";
+  version = "0.0.1";
+  name = "${pname}-${version}";
+
+  src = ../.;
+  cargoBuildFlags = "-p ${pname}";
+  cargoLock = {
+    lockFile = ../Cargo.lock;
+  };
+}