8 files changed
--- a/README.adoc
+++ b/README.adoc
@@ -63,18 +63,14 @@
# nixosModules section of fleet config declares modules, which are used for all configured nixos hosts.
nixosModules = [
lanzaboote.nixosModules.lanzaboote
- ({
- config,
- lib,
- ...
- }: {
+ {
# Make `nix shell nixpkgs#thing` use the same nixpkgs, as used to build the system.
nix.registry.nixpkgs = {
from = { id = "nixpkgs"; type = "indirect"; };
flake = nixpkgs;
exact = false;
};
- })
+ }
];
# Those modules are used to configure all the machines in cluster at the same time, good example of global modules
@@ -97,12 +93,12 @@
./controlplane-1/hardware-configuration.nix
./controlplane-1/configuration.nix
# Configuration may also be specified inline, as in any nixos config.
- ({...}: {
+ {
services.ray = {
gpus = 4;
cpus = 128;
};
- })
+ }
];
};
};
--- a/flake.nix
+++ b/flake.nix
@@ -16,19 +16,18 @@
inputs.nixpkgs.follows = "nixpkgs";
};
};
- outputs = {
+ outputs = inputs @ {
self,
- rust-overlay,
flake-parts,
- nixpkgs,
- nixpkgs-stable-for-tests,
crane,
+ ...
}:
flake-parts.lib.mkFlake {
- # Not passing inputs through inputs for better visibility.
- inputs = {};
+ inherit inputs;
} {
- flake = {
+ flake = let
+ inherit (inputs.nixpkgs.lib) mapAttrs;
+ in {
lib = import ./lib {
fleetPkgsForPkgs = pkgs:
import ./pkgs {
@@ -45,11 +44,11 @@
'';
inventory = output: {
children =
- builtins.mapAttrs (configName: cluster: {
+ mapAttrs (configName: cluster: {
what = "fleet cluster configuration";
children =
- builtins.mapAttrs (hostName: host: {
+ mapAttrs (hostName: host: {
what = "host [${host.system}]";
})
cluster.config.hosts;
@@ -70,19 +69,20 @@
pkgs,
...
}: let
+ inherit (lib) mapAttrs' elem;
# Can also be built for darwin, through it is not usual to deploy nixos systems from macos machines.
# I have no hardware for such testing, thus only adding machines I actually have and use.
#
# It is not possible to deploy any host from armv6/armv7 hardware, and I don't think it even makes sense.
deployerSystems = ["aarch64-linux" "x86_64-linux"];
- deployerSystem = builtins.elem system deployerSystems;
+ deployerSystem = elem system deployerSystems;
lib = pkgs.lib;
rust = pkgs.rust-bin.fromRustupToolchainFile ./rust-toolchain.toml;
craneLib = (crane.mkLib pkgs).overrideToolchain rust;
in {
- _module.args.pkgs = import nixpkgs {
+ _module.args.pkgs = import inputs.nixpkgs {
inherit system;
- overlays = [(rust-overlay.overlays.default)];
+ overlays = [(inputs.rust-overlay.overlays.default)];
};
# Reference fleet package should be built with nightly rust, specified in rust-toolchain.toml.
packages = lib.mkIf deployerSystem (let
@@ -116,14 +116,14 @@
checks = let
packages = import ./pkgs {
inherit (pkgs) callPackage;
- craneLib = crane.mkLib (import nixpkgs {inherit system;});
+ craneLib = crane.mkLib pkgs;
};
packages-with-nixpkgs-stable = import ./pkgs {
inherit (pkgs) callPackage;
- craneLib = crane.mkLib (import nixpkgs-stable-for-tests {inherit system;});
+ craneLib = crane.mkLib (import inputs.nixpkgs-stable-for-tests {inherit system;});
};
prefixAttrs = prefix: attrs:
- nixpkgs.lib.attrsets.mapAttrs' (name: value: {
+ mapAttrs' (name: value: {
name = "${prefix}${name}";
value = value.overrideAttrs (prev: {
pname = "${prefix}${prev.pname}";
--- a/lib/fleetLib.nix
+++ b/lib/fleetLib.nix
@@ -2,8 +2,11 @@
{
nixpkgs,
hostNames,
-}:
-with nixpkgs.lib; rec {
+}: let
+ inherit (nixpkgs) lib;
+ inherit (lib) listToAttrs remove unique crossLists sort elemAt mkOptionType mkOverride optionalString;
+ inherit (lib.types) listOf coercedTo oneOf submodule;
+in rec {
hostsToAttrs = f:
listToAttrs (
map (name: {
@@ -34,6 +37,27 @@
then "${this}-${other}"
else "${other}-${this}";
+ types = rec {
+ anyModule = mkOptionType {
+ name = "submodule";
+ inherit (submodule {}) check;
+ merge = lib.options.mergeOneOption;
+ description = "Nixos module";
+ };
+ listOfAnyModuleStrict =
+ listOf anyModule;
+ listOfAnyModule =
+ coercedTo (oneOf [listOfAnyModuleStrict anyModule]) (
+ v:
+ if builtins.isAttrs v
+ then [v]
+ else if builtins.isFunction v
+ then [v]
+ else v
+ )
+ listOfAnyModuleStrict;
+ };
+
# mkDefault = mkOverride 1000
# For places, where fleet knows better than nixpkgs defaults.
mkFleetDefault = mkOverride 999;
--- a/modules/fleet/assertions.nix
+++ b/modules/fleet/assertions.nix
@@ -1,8 +1,10 @@
-{lib, ...}:
-with lib; {
+{lib, ...}: let
+ inherit (lib) mkOption;
+ inherit (lib.types) listOf unspecified str;
+in {
options = {
assertions = mkOption {
- type = types.listOf types.unspecified;
+ type = listOf unspecified;
internal = true;
default = [];
example = [
@@ -21,7 +23,7 @@
warnings = mkOption {
internal = true;
default = [];
- type = types.listOf types.str;
+ type = listOf str;
example = ["The `foo' service is deprecated and will go away soon!"];
description = ''
This option allows modules to show warnings to users during
--- a/modules/fleet/meta.nix
+++ b/modules/fleet/meta.nix
@@ -4,58 +4,53 @@
config,
nixpkgs,
...
-}:
-with lib;
-with fleetLib; let
- hostModule = with types;
- {...} @ hostConfig: let
- hostName = hostConfig.config._module.args.name;
- in {
- options = {
- nixosModules = mkOption {
- type = listOf (mkOptionType {
- name = "submodule";
- inherit (submodule {}) check;
- merge = lib.options.mergeOneOption;
- description = "Nixos module";
- });
- description = "List of nixos modules";
- default = [];
- };
- system = mkOption {
- type = str;
- description = "Type of system";
- };
- encryptionKey = mkOption {
- type = str;
- description = "Encryption key";
- };
- nixosSystem = mkOption {
- type = unspecified;
- description = "Nixos configuration";
- };
- nixpkgs = mkOption {
- type = unspecified;
- description = "Nixpkgs override";
- default = nixpkgs;
- };
+}: let
+ inherit (fleetLib) hostsToAttrs mkFleetGeneratorDefault;
+ inherit (fleetLib.types) listOfAnyModule;
+ inherit (lib) mkOption mkOptionType;
+ inherit (lib.types) str unspecified attrsOf listOf submodule;
+ hostModule = {...} @ hostConfig: let
+ hostName = hostConfig.config._module.args.name;
+ in {
+ options = {
+ nixosModules = mkOption {
+ # Not too strict, but nixos module system will fix everything.
+ type =
+ listOfAnyModule;
+
+ description = "List of nixos modules";
+ default = [];
+ };
+ system = mkOption {
+ type = str;
+ description = "Type of system";
+ };
+ encryptionKey = mkOption {
+ type = str;
+ description = "Encryption key";
+ };
+ nixosSystem = mkOption {
+ type = unspecified;
+ description = "Nixos configuration";
+ };
+ nixpkgs = mkOption {
+ type = unspecified;
+ description = "Nixpkgs override";
+ default = nixpkgs;
};
- config = {
- nixosSystem = hostConfig.config.nixpkgs.lib.nixosSystem {
- inherit (hostConfig.config) system;
- modules = hostConfig.config.nixosModules;
- specialArgs = {
- inherit fleetLib;
- fleet = hostsToAttrs (host: config.hosts.${host}.nixosSystem.config);
- };
+ };
+ config = {
+ nixosSystem = hostConfig.config.nixpkgs.lib.nixosSystem {
+ inherit (hostConfig.config) system;
+ modules = hostConfig.config.nixosModules;
+ specialArgs = {
+ inherit fleetLib;
+ fleet = hostsToAttrs (host: config.hosts.${host}.nixosSystem.config);
};
- nixosModules = [
- ({...}: {
- networking.hostName = mkFleetGeneratorDefault hostName;
- })
- ];
};
+ nixosModules.networking.hostName = mkFleetGeneratorDefault hostName;
};
+ };
overlayType = mkOptionType {
name = "nixpkgs-overlay";
description = "nixpkgs overlay";
@@ -63,19 +58,14 @@
merge = lib.mergeOneOption;
};
in {
- options = with types; {
+ options = {
hosts = mkOption {
type = attrsOf (submodule hostModule);
default = {};
description = "Configurations of individual hosts";
};
nixosModules = mkOption {
- type = listOf (mkOptionType {
- name = "submodule";
- inherit (submodule {}) check;
- merge = lib.options.mergeOneOption;
- description = "Nixos modules";
- });
+ type = listOfAnyModule;
description = "Modules, which should be added to every system";
default = [];
};
@@ -89,9 +79,9 @@
nixosModules =
config.nixosModules
++ [
- ({...}: {
+ {
nixpkgs.overlays = config.overlays;
- })
+ }
];
});
nixosModules = import ../../nixos/modules/module-list.nix;
1{2 lib,3 fleetLib,4 config,5 ...6}:7with lib;8with fleetLib; let9 sharedSecret = with types; ({config, ...}: {10 freeformType = types.lazyAttrsOf unspecified;11 options = {12 expectedOwners = mkOption {13 type = nullOr (listOf str);14 description = ''15 List of hosts to encrypt secret for. null if managed by user (= via owners field from fleet.nix)1617 Secrets would be decrypted and stored to /run/secrets/$\{name} on owners18 '';19 default = null;20 };21 22 regenerateOnOwnerAdded = mkOption {23 type = bool;24 description = ''25 Is this secret owner-dependent, and needs to be regenerated on ownership set change, or it may be just reencrypted.2627 You want to have this option set to true, when this secret contains some reference to its owners, i.e x509 SANs.28 '';29 };30 regenerateOnOwnerRemoved = mkOption {31 default = config.regenerateOnOwnerAdded;32 type = bool;33 description = ''34 Should this secret be removed on owner removal, or it may be just reencrypted3536 Most probably its value should be equal to regenerateOnOwnerAdded, override only if you know what are you doing.37 Contrary to regenerateOnOwnerAdded, you may want to set this option to false, when host permissions are revoked38 in some other way than by this secret ownership, I.e by firewall/etc.39 '';40 };41 generator = mkOption {42 type = nullOr unspecified;43 description = "Derivation to evaluate for secret generation";44 default = null;45 };46 createdAt = mkOption {47 type = nullOr str;48 description = "When this secret was (re)generated";49 default = null;50 };51 expiresAt = mkOption {52 type = nullOr str;53 description = "On which date this secret will expire, someone should regenerate this secret before it expires.";54 default = null;55 };5657 owners = mkOption {58 type = listOf str;59 description = ''60 For which owners this secret is currently encrypted,61 if not matches expectedOwners - then this secret is considered outdated, and62 should be regenerated/reencrypted.6364 Imported from fleet.nix65 '';66 default = [];67 };68 };69 });70 hostSecret = with types; {71 freeformType = types.lazyAttrsOf unspecified;72 options = {73 createdAt = mkOption {74 type = nullOr str;75 default = null;76 };77 expiresAt = mkOption {78 type = nullOr str;79 default = null;80 };81 };82 };83in {84 options = with types; {85 version = mkOption {86 type = str;87 default = "";88 internal = true;89 };90 sharedSecrets = mkOption {91 type = attrsOf (submodule sharedSecret);92 default = {};93 description = "Shared secrets";94 };95 hostSecrets = mkOption {96 type = attrsOf (attrsOf (submodule hostSecret));97 default = {};98 description = "Host secrets. Imported from fleet.nix";99 internal = true;100 };101 };102 config = {103 assertions =104 mapAttrsToList105 (name: secret: {106 assertion = secret.expectedOwners == null || builtins.sort (a: b: a < b) secret.owners == builtins.sort (a: b: a < b) secret.expectedOwners;107 message = "Shared secret ${name} is expected to be encrypted for ${builtins.toJSON secret.expectedOwners}, but it is encrypted for ${builtins.toJSON secret.owners}. Run fleet secrets regenerate to fix";108 })109 config.sharedSecrets;110 hosts = hostsToAttrs (host: {111 nixosModules = let112 113 processSecret = v:114 (removeAttrs v ["createdAt" "expiresAt" "expectedOwners" "owners" "regenerateOnOwnerAdded" "regenerateOnOwnerRemoved"])115 // {116 shared = true;117 };118 in [119 {120 secrets =121 (122 mapAttrs (_: processSecret)123 (filterAttrs (_: v: builtins.elem host v.owners) config.sharedSecrets)124 )125 // (mapAttrs (_: processSecret) (config.hostSecrets.${host} or {}));126 }127 ];128 });129 130 overlays = [131 (final: prev: let132 lib = final.lib;133 inherit (lib) strings;134 inherit (strings) concatStringsSep;135 in {136 mkSecretGenerators = {recipients}: rec {137 138 139 140 mkImpureSecretGenerator = {141 script,142 143 144 impureOn ? null,145 }:146 (prev.writeShellScript "impureGenerator.sh" ''147 148 set -eu149150 export GENERATOR_HELPER_IDENTITIES="${concatStringsSep "\n" recipients}";151 export PATH=${final.fleet-generator-helper}/bin:$PATH152153 154 tmp=$(mktemp -d)155 cd $tmp156 157158 created_at=$(date -u +"%Y-%m-%dT%H:%M:%S.%NZ")159160 ${script}161162 if ! test -d $out; then163 echo "impure generator script did not produce expected \$out output"164 exit 1165 fi166167 echo -n $created_at > $out/created_at168 echo -n SUCCESS > $out/marker169 '')170 .overrideAttrs (old: {171 passthru = {172 inherit impureOn;173 generatorKind = "impure";174 };175 });176 177 mkSecretGenerator = {script}: mkImpureSecretGenerator {inherit script;};178179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 };208 })209 ];210 };211}
1{2 lib,3 fleetLib,4 config,5 ...6}: let7 inherit (fleetLib) hostsToAttrs;8 inherit (lib) mkOption mapAttrsToList mapAttrs filterAttrs concatStringsSep;9 inherit (lib.types) lazyAttrsOf unspecified nullOr listOf str bool attrsOf submodule;1011 sharedSecret = {config, ...}: {12 freeformType = lazyAttrsOf unspecified;13 options = {14 expectedOwners = mkOption {15 type = nullOr (listOf str);16 description = ''17 List of hosts to encrypt secret for. null if managed by user (= via owners field from fleet.nix)1819 Secrets would be decrypted and stored to /run/secrets/$\{name} on owners20 '';21 default = null;22 };23 24 regenerateOnOwnerAdded = mkOption {25 type = bool;26 description = ''27 Is this secret owner-dependent, and needs to be regenerated on ownership set change, or it may be just reencrypted.2829 You want to have this option set to true, when this secret contains some reference to its owners, i.e x509 SANs.30 '';31 };32 regenerateOnOwnerRemoved = mkOption {33 default = config.regenerateOnOwnerAdded;34 type = bool;35 description = ''36 Should this secret be removed on owner removal, or it may be just reencrypted3738 Most probably its value should be equal to regenerateOnOwnerAdded, override only if you know what are you doing.39 Contrary to regenerateOnOwnerAdded, you may want to set this option to false, when host permissions are revoked40 in some other way than by this secret ownership, I.e by firewall/etc.41 '';42 };43 generator = mkOption {44 type = nullOr unspecified;45 description = "Derivation to evaluate for secret generation";46 default = null;47 };48 createdAt = mkOption {49 type = nullOr str;50 description = "When this secret was (re)generated";51 default = null;52 };53 expiresAt = mkOption {54 type = nullOr str;55 description = "On which date this secret will expire, someone should regenerate this secret before it expires.";56 default = null;57 };5859 owners = mkOption {60 type = listOf str;61 description = ''62 For which owners this secret is currently encrypted,63 if not matches expectedOwners - then this secret is considered outdated, and64 should be regenerated/reencrypted.6566 Imported from fleet.nix67 '';68 default = [];69 };70 };71 };72 hostSecret = {73 freeformType = lazyAttrsOf unspecified;74 options = {75 createdAt = mkOption {76 type = nullOr str;77 default = null;78 };79 expiresAt = mkOption {80 type = nullOr str;81 default = null;82 };83 };84 };85in {86 options = {87 version = mkOption {88 type = str;89 default = "";90 internal = true;91 };92 sharedSecrets = mkOption {93 type = attrsOf (submodule sharedSecret);94 default = {};95 description = "Shared secrets";96 };97 hostSecrets = mkOption {98 type = attrsOf (attrsOf (submodule hostSecret));99 default = {};100 description = "Host secrets. Imported from fleet.nix";101 internal = true;102 };103 };104 config = {105 assertions =106 mapAttrsToList107 (name: secret: {108 assertion = secret.expectedOwners == null || builtins.sort (a: b: a < b) secret.owners == builtins.sort (a: b: a < b) secret.expectedOwners;109 message = "Shared secret ${name} is expected to be encrypted for ${builtins.toJSON secret.expectedOwners}, but it is encrypted for ${builtins.toJSON secret.owners}. Run fleet secrets regenerate to fix";110 })111 config.sharedSecrets;112 hosts = hostsToAttrs (host: {113 nixosModules = let114 115 processSecret = v:116 (removeAttrs v ["createdAt" "expiresAt" "expectedOwners" "owners" "regenerateOnOwnerAdded" "regenerateOnOwnerRemoved"])117 // {118 shared = true;119 };120 in [121 {122 secrets =123 (124 mapAttrs (_: processSecret)125 (filterAttrs (_: v: builtins.elem host v.owners) config.sharedSecrets)126 )127 // (mapAttrs (_: processSecret) (config.hostSecrets.${host} or {}));128 }129 ];130 });131 132 overlays = [133 (final: prev: {134 mkSecretGenerators = {recipients}: rec {135 136 137 138 mkImpureSecretGenerator = {139 script,140 141 142 impureOn ? null,143 }:144 (prev.writeShellScript "impureGenerator.sh" ''145 146 set -eu147148 export GENERATOR_HELPER_IDENTITIES="${concatStringsSep "\n" recipients}";149 export PATH=${final.fleet-generator-helper}/bin:$PATH150151 152 tmp=$(mktemp -d)153 cd $tmp154 155156 created_at=$(date -u +"%Y-%m-%dT%H:%M:%S.%NZ")157158 ${script}159160 if ! test -d $out; then161 echo "impure generator script did not produce expected \$out output"162 exit 1163 fi164165 echo -n $created_at > $out/created_at166 echo -n SUCCESS > $out/marker167 '')168 .overrideAttrs (old: {169 passthru = {170 inherit impureOn;171 generatorKind = "impure";172 };173 });174 175 mkSecretGenerator = {script}: mkImpureSecretGenerator {inherit script;};176177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 };206 })207 ];208 };209}
--- a/nixos/meta.nix
+++ b/nixos/meta.nix
@@ -2,11 +2,13 @@
lib,
pkgs,
...
-}:
-with lib; {
- options = with types; {
+}: let
+ inherit (lib) mkOption;
+ inherit (lib.types) listOf str submodule;
+in {
+ options = {
nixpkgs.resolvedPkgs = mkOption {
- type = types.pkgs // {description = "nixpkgs.pkgs";};
+ type = lib.types.pkgs // {description = "nixpkgs.pkgs";};
description = "Value of pkgs";
};
tags = mkOption {
@@ -30,9 +32,6 @@
};
};
description = "Network definition of host";
- };
- buildTarget = mkOption {
- type = enum ["toplevel" "sd-image" "installation-cd"];
};
};
config = {
--- a/nixos/secrets.nix
+++ b/nixos/secrets.nix
@@ -3,16 +3,17 @@
config,
pkgs,
...
-}:
-with lib; let
+}: let
inherit (lib.strings) hasPrefix removePrefix;
+ inherit (lib) mkOption mkOptionDefault mapAttrs stringAfter;
+ inherit (lib.types) submodule str attrsOf nullOr unspecified lazyAttrsOf;
plaintextPrefix = "<PLAINTEXT>";
plaintextNewlinePrefix = "<PLAINTEXT-NL>";
sysConfig = config;
secretPartType = secretName:
- types.submodule ({config, ...}: {
- options = with types; {
+ submodule ({config, ...}: {
+ options = {
raw = mkOption {
description = "Secret in fleet-specific undocumented format, do not use. Import from fleet.nix";
internal = true;
@@ -49,11 +50,11 @@
stablePath = mkOptionDefault "/run/secrets/${secretName}/${partName}";
};
});
- secretType = types.submodule ({config, ...}: let
+ secretType = submodule ({config, ...}: let
secretName = config._module.args.name;
in {
- freeformType = types.lazyAttrsOf (secretPartType secretName);
- options = with types; {
+ freeformType = lazyAttrsOf (secretPartType secretName);
+ options = {
shared = mkOption {
description = "Is this secret owned by this machine, or propagated from shared secrets";
default = false;
@@ -112,7 +113,7 @@
in {
options = {
secrets = mkOption {
- type = types.attrsOf secretType;
+ type = attrsOf secretType;
default = {};
description = "Host-local secrets";
};