difftreelog
feat secret management
in: trunk
14 files changed
.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
flake.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"
}
}
},
flake.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
+ ];
+ };
+ });
}
lib/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 {
lib/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 (
modules/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) ];
+ })
+ ];
+ });
+}
modules/modules.nixdiffbeforeafterbothmodules/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;
- };
- }
- ];
- }
- );
- };
-}
modules/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
modules/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 { };
- };
-}
modules/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);
+ }
+ ];
+ });
+ };
+}
modules/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}
+ '';
+ };
+}
pkgs/default.nixdiffbeforeafterboth--- /dev/null
+++ b/pkgs/default.nix
@@ -0,0 +1,5 @@
+pkgs: super:
+with pkgs;
+{
+ fleet-install-secrets = callPackage ./fleet-install-secrets.nix { };
+}
pkgs/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;
+ };
+}