difftreelog
refactor move definitions around
in: trunk
32 files changed
flake.lockdiffbeforeafterboth--- a/flake.lock
+++ b/flake.lock
@@ -66,28 +66,11 @@
"url": "https://github.com/NixOS/nixpkgs/archive/5daf0514482af3f97abaefc78a6606365c9108e2.tar.gz"
}
},
- "nixpkgs-stable-for-tests": {
- "locked": {
- "lastModified": 1721548954,
- "narHash": "sha256-7cCC8+Tdq1+3OPyc3+gVo9dzUNkNIQfwSDJ2HSi2u3o=",
- "owner": "nixos",
- "repo": "nixpkgs",
- "rev": "63d37ccd2d178d54e7fb691d7ec76000740ea24a",
- "type": "github"
- },
- "original": {
- "owner": "nixos",
- "ref": "nixos-24.05",
- "repo": "nixpkgs",
- "type": "github"
- }
- },
"root": {
"inputs": {
"crane": "crane",
"flake-parts": "flake-parts",
"nixpkgs": "nixpkgs",
- "nixpkgs-stable-for-tests": "nixpkgs-stable-for-tests",
"rust-overlay": "rust-overlay"
}
},
flake.nixdiffbeforeafterboth--- a/flake.nix
+++ b/flake.nix
@@ -3,7 +3,6 @@
inputs = {
nixpkgs.url = "github:nixos/nixpkgs/master";
- nixpkgs-stable-for-tests.url = "github:nixos/nixpkgs/nixos-24.05";
rust-overlay = {
url = "github:oxalica/rust-overlay";
inputs = {
@@ -33,9 +32,9 @@
// {
fleetConfiguration = throw "function-based interface is deprecated, use flake-parts syntax instead";
};
- flakeModules.default = (import ./lib/flakePart.nix {
+ flakeModules.default = import ./lib/flakePart.nix {
inherit crane;
- });
+ };
flakeModule = flakeModules.default;
# To be used with https://github.com/NixOS/nix/pull/8892
@@ -92,8 +91,7 @@
};
# Reference fleet package should be built with nightly rust, specified in rust-toolchain.toml.
packages = lib.mkIf deployerSystem (let
- packages = import ./pkgs {
- inherit (pkgs) callPackage;
+ packages = pkgs.callPackages ./pkgs {
inherit craneLib;
};
in
@@ -121,14 +119,7 @@
# fleet-install-secrets will not be built normally, because they are not ran directly by user most of the time.
# checks there build packages for default nixpkgs rustPlatform packages.
checks = let
- packages = import ./pkgs {
- inherit (pkgs) callPackage;
- craneLib = crane.mkLib pkgs;
- };
- packages-with-nixpkgs-stable = import ./pkgs {
- inherit (pkgs) callPackage;
- craneLib = crane.mkLib (import inputs.nixpkgs-stable-for-tests {inherit system;});
- };
+ packages = pkgs.callPackages ./pkgs {};
prefixAttrs = prefix: attrs:
mapAttrs' (name: value: {
name = "${prefix}${name}";
@@ -139,8 +130,7 @@
attrs;
in
# `fleet` crate wants nightly rust, also little sense of supporting it on stable nixpkgs.
- (prefixAttrs "nixpkgs-" (removeAttrs packages ["fleet"]))
- // (prefixAttrs "nixpkgs-stable-" (removeAttrs packages-with-nixpkgs-stable ["fleet"]));
+ (prefixAttrs "nixpkgs-" (removeAttrs packages ["fleet"]));
formatter = pkgs.alejandra;
};
};
lib/default.nixdiffbeforeafterboth--- a/lib/default.nix
+++ b/lib/default.nix
@@ -4,7 +4,7 @@
inherit (lib.options) mkOption mergeOneOption;
inherit (lib.modules) mkOverride;
inherit (lib.types) listOf submodule attrsOf mkOptionType;
- inherit (lib.strings) optionalString;
+ inherit (lib.strings) optionalString hasPrefix removePrefix;
in rec {
types = {
overlay = mkOptionType {
@@ -16,6 +16,7 @@
listOfOverlay = listOf types.overlay;
mkHostsType = module: attrsOf (submodule module);
+ mkDataType = module: submodule module;
};
options = {
@@ -23,6 +24,10 @@
mkOption {
type = types.mkHostsType module;
};
+ mkDataOption = module:
+ mkOption {
+ type = types.mkDataType module;
+ };
};
inherit (options) mkHostsOption;
@@ -118,4 +123,18 @@
};
inherit (secrets) mkPassword mkEd25519 mkX25519 mkRsa mkBytes mkHexBytes mkBase64Bytes;
+
+ strings = let
+ plaintextPrefix = "<PLAINTEXT>";
+ plaintextNewlinePrefix = "<PLAINTEXT-NL>";
+ in {
+ decodeRawSecret = raw:
+ if hasPrefix plaintextPrefix raw
+ then removePrefix plaintextPrefix raw
+ else if hasPrefix plaintextNewlinePrefix raw
+ then removePrefix plaintextNewlinePrefix raw
+ else throw "decodeRawSecret only works with plaintext-encoded secret public parts, got ${raw}";
+ };
+
+ inherit (strings) decodeRawSecret;
}
lib/flakePart.nixdiffbeforeafterboth--- a/lib/flakePart.nix
+++ b/lib/flakePart.nix
@@ -7,6 +7,7 @@
inherit (lib.options) mkOption;
inherit (lib.attrsets) mapAttrs;
inherit (lib.types) lazyAttrsOf deferredModule unspecified;
+ inherit (lib.strings) isPath;
inherit (fleetLib.options) mkHostsOption;
in {
options.fleetModules = mkOption {
@@ -36,19 +37,25 @@
bootstrapNixpkgs = bootstrapEval.config.nixpkgs.buildUsing;
normalEval = bootstrapNixpkgs.lib.evalModules {
modules =
- (import ../modules/fleet/_modules.nix)
+ (import ../modules/module-list.nix)
++ [
- data
module
{
options.hosts = mkHostsOption {
nixos.nixpkgs.overlays = [
- (final: prev: {
- # FIXME: make this name not conflicting
- craneLib = crane.mkLib prev;
- })
+ (final: prev:
+ import ../pkgs {
+ inherit (prev) callPackage;
+ craneLib = crane.mkLib prev;
+ })
];
};
+ config = {
+ data =
+ if isPath data
+ then import data
+ else data;
+ };
}
];
specialArgs.fleetLib = import ../lib {
modules/assertions.nixdiffbeforeafterboth--- /dev/null
+++ b/modules/assertions.nix
@@ -0,0 +1,64 @@
+{
+ lib,
+ config,
+ ...
+}: let
+ inherit (lib.options) mkOption;
+ inherit (lib.types) listOf unspecified str;
+ inherit (lib.lists) map filter;
+
+ errors = mkOption {
+ type = listOf str;
+ internal = true;
+ description = ''
+ Similar to warnings, however build will fail if any error exists.
+ '';
+ };
+in {
+ options = {
+ assertions = mkOption {
+ type = listOf unspecified;
+ internal = true;
+ default = [];
+ example = [
+ {
+ assertion = false;
+ message = "you can't enable this for that reason";
+ }
+ ];
+ description = ''
+ This option allows modules to express conditions that must
+ hold for the evaluation of the system configuration to
+ succeed, along with associated error messages for the user.
+ '';
+ };
+
+ warnings = mkOption {
+ internal = true;
+ default = [];
+ 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
+ the evaluation of the system configuration.
+ '';
+ };
+
+ inherit errors;
+ };
+ config = {
+ errors =
+ map (v: v.message)
+ (filter (v: !v.assertion) config.assertions);
+
+ nixos = {config, ...}: {
+ _file = ./assertions.nix;
+ options = {
+ inherit errors;
+ };
+ config.errors =
+ map (v: v.message)
+ (filter (v: !v.assertion) config.assertions);
+ };
+ };
+}
modules/fleet/_modules.nixdiffbeforeafterboth--- a/modules/fleet/_modules.nix
+++ /dev/null
@@ -1,9 +0,0 @@
-[
- ./assertions.nix
- ./fleetLib.nix
- ./hosts.nix
- ./meta.nix
- ./nixos.nix
- ./nixpkgs.nix
- ./secrets.nix
-]
modules/fleet/assertions.nixdiffbeforeafterboth--- a/modules/fleet/assertions.nix
+++ /dev/null
@@ -1,49 +0,0 @@
-{
- lib,
- config,
- ...
-}: let
- inherit (lib.options) mkOption;
- inherit (lib.types) listOf unspecified str;
- inherit (lib.lists) map filter;
-in {
- options = {
- assertions = mkOption {
- type = listOf unspecified;
- internal = true;
- default = [];
- example = [
- {
- assertion = false;
- message = "you can't enable this for that reason";
- }
- ];
- description = ''
- This option allows modules to express conditions that must
- hold for the evaluation of the system configuration to
- succeed, along with associated error messages for the user.
- '';
- };
-
- warnings = mkOption {
- internal = true;
- default = [];
- 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
- the evaluation of the system configuration.
- '';
- };
- errors = mkOption {
- type = listOf str;
- internal = true;
- description = ''
- Similar to warnings, however build will fail if any error exists.
- '';
- };
- };
- config.errors =
- map (v: v.message)
- (filter (v: !v.assertion) config.assertions);
-}
modules/fleet/fleetLib.nixdiffbeforeafterboth--- a/modules/fleet/fleetLib.nix
+++ /dev/null
@@ -1,9 +0,0 @@
-{
- lib,
- config,
- ...
-}: {
- _module.args.fleetLib = import ../../lib {
- inherit lib;
- };
-}
modules/fleet/hosts.nixdiffbeforeafterboth--- a/modules/fleet/hosts.nix
+++ /dev/null
@@ -1,40 +0,0 @@
-{
- lib,
- fleetLib,
- ...
-}: let
- inherit (fleetLib.modules) mkFleetGeneratorDefault;
- inherit (fleetLib.types) mkHostsType;
- inherit (lib.options) mkOption;
- inherit (lib.types) str listOf;
-in {
- options = {
- hosts = mkOption {
- type = mkHostsType ({config, ...}: {
- options = {
- system = mkOption {
- type = str;
- description = "Type of the system.";
- };
- # TODO: This is part of fleet.nix, move it to separate toplevel data config option.
- encryptionKey = mkOption {
- type = str;
- description = "Rage SSH encryption key for secrets.";
- };
- tags = mkOption {
- type = listOf str;
- description = "Host tag. In CLI, you can refer to all hosts having this tag using @tag syntax.";
- };
- };
- config = {
- nixos.networking.hostName = mkFleetGeneratorDefault config._module.args.name;
- tags = ["all"];
- };
- _file = ./meta.nix;
- });
- default = {};
- description = "Configurations of individual hosts";
- };
- };
- _file = ./meta.nix;
-}
modules/fleet/meta.nixdiffbeforeafterboth--- a/modules/fleet/meta.nix
+++ /dev/null
@@ -1,8 +0,0 @@
-{lib, ...}: let
- inherit (lib.modules) mkRemovedOptionModule;
-in {
- imports = [
- (mkRemovedOptionModule ["fleetModules"] "replaced with imports.")
- (mkRemovedOptionModule ["data"] "data is now provided by fleet itself, you can remove your import.")
- ];
-}
modules/fleet/nixos.nixdiffbeforeafterboth--- a/modules/fleet/nixos.nix
+++ /dev/null
@@ -1,55 +0,0 @@
-{
- lib,
- fleetLib,
- config,
- ...
-}: let
- inherit (lib.attrsets) mapAttrs;
- inherit (lib.options) mkOption;
- inherit (lib.types) deferredModule deferredModuleWith;
- inherit (lib.modules) mkRemovedOptionModule;
- inherit (fleetLib.options) mkHostsOption;
-
- _file = ./nixos.nix;
-in {
- options = {
- nixos = mkOption {
- description = ''
- Nixos configuration for all hosts.
- '';
- type = deferredModule;
- };
- hosts = mkHostsOption (hostArgs: {
- inherit _file;
- options = {
- nixos = mkOption {
- description = ''
- Nixos configuration for the current host.
- '';
- type = deferredModuleWith {
- staticModules = import ../../nixos/modules/module-list.nix;
- };
- apply = module:
- config.nixpkgs.buildUsing.lib.nixosSystem {
- inherit (hostArgs.config) system;
- modules = [module];
- };
- };
- };
- config = {
- # imports = [
- # (mkRemovedOptionModule ["nixosModules"] "replaced with hosts.*.nixos.imports.")
- # ];
- nixos = {
- imports = [
- config.nixos
- ];
- config._module.args.fleet = mapAttrs (_: value: value.nixos.config) config.hosts;
- };
- };
- });
- };
- imports = [
- (mkRemovedOptionModule ["nixosModules"] "replaced with nixos.imports.")
- ];
-}
modules/fleet/nixpkgs.nixdiffbeforeafterboth--- a/modules/fleet/nixpkgs.nix
+++ /dev/null
@@ -1,58 +0,0 @@
-{
- lib,
- fleetLib,
- config,
- ...
-}: let
- inherit (lib.options) mkOption;
- inherit (lib.types) path;
- inherit (lib.modules) mkRemovedOptionModule;
- inherit (fleetLib.options) mkHostsOption;
- inherit (fleetLib.types) listOfOverlay;
-
- _file = ./nixpkgs.lib;
-in {
- options = {
- nixpkgs = {
- buildUsing = mkOption {
- description = ''
- Default nixpkgs to use for building the systems.
- '';
- type = path;
- };
- overlays = mkOption {
- description = ''
- Package overlays to apply for all the hosts, gets propagated into
- `hosts.*.nixosModules.nixpkgs.overlays`.
- '';
- type = listOfOverlay;
- };
- };
- hosts = mkHostsOption {
- inherit _file;
- options.nixpkgs.buildUsing = mkOption {
- description = ''
- Nixpkgs to use for building the system.
-
- Note that this option is defined at the host level, not the nixosModules level,
- nixosModules will be evaluated using this flake input.
- '';
- type = path;
- default = config.nixpkgs.buildUsing;
- };
- # imports = [
- # (mkRemovedOptionModule ["nixpkgs" "overlays"] "this option needs to be specified at nixosModules level")
- # ];
- config.nixos = {
- inherit _file;
- nixpkgs.overlays = config.nixpkgs.overlays;
- imports = [
- (mkRemovedOptionModule ["nixpkgs" "buildUsing"] "this option should be specified at the host level, not the nixosModules level")
- ];
- };
- };
- };
- config.nixpkgs.overlays = [
- (final: prev: import ../../pkgs {inherit (final) callPackage craneLib;})
- ];
-}
modules/fleet/secrets.nixdiffbeforeafterboth1{2 lib,3 fleetLib,4 config,5 ...6}: let7 inherit (fleetLib.options) mkHostsOption;8 inherit (lib.options) mkOption;9 inherit (lib.types) lazyAttrsOf unspecified nullOr listOf str bool attrsOf submodule;10 inherit (lib.lists) sort elem;11 inherit (lib.attrsets) mapAttrsToList mapAttrs filterAttrs;12 inherit (lib.strings) toJSON concatStringsSep;1314 sharedSecret = {config, ...}: {15 freeformType = lazyAttrsOf unspecified;16 options = {17 expectedOwners = mkOption {18 type = nullOr (listOf str);19 description = ''20 List of hosts to encrypt secret for. null if managed by user (= via owners field from fleet.nix)2122 Secrets would be decrypted and stored to /run/secrets/$\{name} on owners23 '';24 default = null;25 };26 # TODO: Aren't those options may be just desugared to data/expectedData?27 regenerateOnOwnerAdded = mkOption {28 type = bool;29 description = ''30 Is this secret owner-dependent, and needs to be regenerated on ownership set change, or it may be just reencrypted.3132 You want to have this option set to true, when this secret contains some reference to its owners, i.e x509 SANs.33 '';34 };35 regenerateOnOwnerRemoved = mkOption {36 default = config.regenerateOnOwnerAdded;37 type = bool;38 description = ''39 Should this secret be removed on owner removal, or it may be just reencrypted4041 Most probably its value should be equal to regenerateOnOwnerAdded, override only if you know what are you doing.42 Contrary to regenerateOnOwnerAdded, you may want to set this option to false, when host permissions are revoked43 in some other way than by this secret ownership, I.e by firewall/etc.44 '';45 };46 generator = mkOption {47 type = nullOr unspecified;48 description = "Derivation to evaluate for secret generation";49 default = null;50 };51 createdAt = mkOption {52 type = nullOr str;53 description = "When this secret was (re)generated";54 default = null;55 };56 expiresAt = mkOption {57 type = nullOr str;58 description = "On which date this secret will expire, someone should regenerate this secret before it expires.";59 default = null;60 };6162 owners = mkOption {63 type = listOf str;64 description = ''65 For which owners this secret is currently encrypted,66 if not matches expectedOwners - then this secret is considered outdated, and67 should be regenerated/reencrypted.6869 Imported from fleet.nix70 '';71 default = [];72 };73 };74 };75 hostSecret = {76 freeformType = lazyAttrsOf unspecified;77 options = {78 createdAt = mkOption {79 type = nullOr str;80 default = null;81 };82 expiresAt = mkOption {83 type = nullOr str;84 default = null;85 };86 };87 };88 inherit (config) hostSecrets sharedSecrets;89in {90 options = {91 version = mkOption {92 type = str;93 internal = true;94 };95 sharedSecrets = mkOption {96 type = attrsOf (submodule sharedSecret);97 default = {};98 description = "Shared secrets";99 };100 hostSecrets = mkOption {101 type = attrsOf (attrsOf (submodule hostSecret));102 default = {};103 description = "Host secrets. Imported from fleet.nix";104 internal = true;105 };106 hosts = mkHostsOption ({config, ...}: {107 nixos = {108 secrets = let109 host = config._module.args.name;110 processSecret = v:111 (removeAttrs v ["createdAt" "expiresAt" "expectedOwners" "owners" "regenerateOnOwnerAdded" "regenerateOnOwnerRemoved"])112 // {113 shared = true;114 };115 in116 (117 mapAttrs (_: processSecret)118 (filterAttrs (_: v: elem host v.owners) sharedSecrets)119 )120 // (mapAttrs (_: processSecret) (hostSecrets.${host} or {}));121 _file = ./secrets.nix;122 };123 });124 };125 config = {126 assertions =127 mapAttrsToList128 (name: secret: {129 assertion = secret.expectedOwners == null || sort (a: b: a < b) secret.owners == sort (a: b: a < b) secret.expectedOwners;130 message = "Shared secret ${name} is expected to be encrypted for ${toJSONsecret.expectedOwners}, but it is encrypted for ${toJSONsecret.owners}. Run fleet secrets regenerate to fix";131 })132 config.sharedSecrets;133 nixpkgs.overlays = [134 (final: prev: {135 mkSecretGenerators = {recipients}: rec {136 # TODO: Merge both generators to one with consistent options syntax?137 # Impure generator is built on local machine, then built closure is copied to remote machine,138 # and then it is ran in inpure context, so that this generator may access HSMs and other things.139 mkImpureSecretGenerator = {140 script,141 # If set - script will be run on remote machine, otherwise it will be run with fleet project in CWD142 # (Some secrets-encryption-in-git/managed PKI solution is expected)143 impureOn ? null,144 }:145 (prev.writeShellScript "impureGenerator.sh" ''146 #!/bin/sh147 set -eu148149 export GENERATOR_HELPER_IDENTITIES="${concatStringsSep"\n"recipients}";150 export PATH=${final.fleet-generator-helper}/bin:$PATH151152 # TODO: Provide tempdir from outside, to make it securely erasurable as needed?153 tmp=mktemp-d154 cd $tmp155 # cd /var/empty156157 created_at=date-u"%Y-%m-%dT%H:%M:%S.%NZ"158159 ${script}160161 if ! test -d $out; then162 echo "impure generator script did not produce expected \$out output"163 exit 1164 fi165166 echo -n $created_at > $out/created_at167 echo -n SUCCESS > $out/marker168 '')169 .overrideAttrs (old: {170 passthru = {171 inherit impureOn;172 generatorKind = "impure";173 };174 });175 # Pure generators are disabled for now176 mkSecretGenerator = {script}: mkImpureSecretGenerator {inherit script;};177178 # TODO: Implement consistent naming179 # Pure secret generator is supposed to be run entirely by nix, using `__impure` derivation type...180 # But for now, it is ran the same way as `impureSecretGenerator`, but on the local machine.181 # mkSecretGenerator = {script}:182 # (prev.writeShellScript "generator.sh" ''183 # #!/bin/sh184 # set -eu185 # # TODO: make nix daemon build secret, not just the script.186 # cd /var/empty187 #188 # created_at=$(date -u +"%Y-%m-%dT%H:%M:%S.%NZ")189 #190 # ${script}191 # if ! test -d $out; then192 # echo "impure generator script did not produce expected \$out output"193 # exit 1194 # fi195 #196 # echo -n $created_at > $out/created_at197 # echo -n SUCCESS > $out/marker198 # '')199 # .overrideAttrs (old: {200 # passthru = {201 # generatorKind = "pure";202 # };203 # # TODO: make nix daemon build secret, not just the script.204 # # __impure = true;205 # });206 };207 })208 ];209 };210}modules/fleetLib.nixdiffbeforeafterboth--- /dev/null
+++ b/modules/fleetLib.nix
@@ -0,0 +1,9 @@
+{
+ lib,
+ config,
+ ...
+}: {
+ _module.args.fleetLib = import ../../lib {
+ inherit lib;
+ };
+}
modules/hosts.nixdiffbeforeafterboth--- /dev/null
+++ b/modules/hosts.nix
@@ -0,0 +1,75 @@
+{
+ lib,
+ fleetLib,
+ ...
+}: let
+ inherit (fleetLib.modules) mkFleetGeneratorDefault;
+ inherit (fleetLib.types) mkHostsType mkDataType;
+ inherit (lib.options) mkOption;
+ inherit (lib.types) str listOf attrsOf submodule;
+in {
+ options = {
+ data = mkOption {
+ type = mkDataType {
+ options = {
+ version = mkOption {
+ type = str;
+ internal = true;
+ };
+ hosts = mkOption {
+ type = attrsOf (submodule {
+ options.encryptionKey = mkOption {
+ type = str;
+ description = "Rage SSH encryption key for secrets.";
+ };
+ });
+ };
+ };
+ };
+ description = ''
+ Configuration provided from outside.
+ Usually used to persist fleet data between runs.
+ '';
+ };
+ hosts = mkOption {
+ type = mkHostsType ({config, ...}: {
+ options = {
+ system = mkOption {
+ description = "Type of the system.";
+ type = str;
+ example = "x86_64-linux";
+ };
+ tags = mkOption {
+ description = "Host tag. In CLI, you can refer to all hosts having this tag using @tag syntax.";
+ type = listOf str;
+ };
+ network = mkOption {
+ type = submodule {
+ options = {
+ internalIps = mkOption {
+ description = "Internal ips";
+ type = listOf str;
+ default = [];
+ };
+ externalIps = mkOption {
+ description = "External ips";
+ type = listOf str;
+ default = [];
+ };
+ };
+ };
+ description = "Network definition of host";
+ };
+ };
+ config = {
+ nixos.networking.hostName = mkFleetGeneratorDefault config._module.args.name;
+ tags = ["all"];
+ };
+ _file = ./meta.nix;
+ });
+ default = {};
+ description = "Configurations of individual hosts";
+ };
+ };
+ _file = ./meta.nix;
+}
modules/meta.nixdiffbeforeafterboth--- /dev/null
+++ b/modules/meta.nix
@@ -0,0 +1,7 @@
+{lib, ...}: let
+ inherit (lib.modules) mkRemovedOptionModule;
+in {
+ imports = [
+ (mkRemovedOptionModule ["fleetModules"] "replaced with imports.")
+ ];
+}
modules/module-list.nixdiffbeforeafterboth--- /dev/null
+++ b/modules/module-list.nix
@@ -0,0 +1,10 @@
+[
+ ./assertions.nix
+ ./fleetLib.nix
+ ./hosts.nix
+ ./meta.nix
+ ./nixos.nix
+ ./nixpkgs.nix
+ ./secrets.nix
+ ./secrets-data.nix
+]
modules/nixos.nixdiffbeforeafterboth--- /dev/null
+++ b/modules/nixos.nix
@@ -0,0 +1,62 @@
+{
+ lib,
+ fleetLib,
+ config,
+ ...
+}: let
+ inherit (lib.attrsets) mapAttrs;
+ inherit (lib.options) mkOption;
+ inherit (lib.types) deferredModule;
+ inherit (lib.modules) mkRemovedOptionModule;
+ inherit (fleetLib.options) mkHostsOption;
+
+ _file = ./nixos.nix;
+in {
+ options = {
+ nixos = mkOption {
+ description = ''
+ Nixos configuration for all hosts.
+ '';
+ type = deferredModule;
+ };
+ hosts = mkHostsOption (hostArgs: {
+ inherit _file;
+ options = {
+ nixos = mkOption {
+ description = ''
+ Nixos configuration for the current host.
+ '';
+ type = deferredModule;
+ apply = module:
+ config.nixpkgs.buildUsing.lib.nixosSystem {
+ inherit (hostArgs.config) system;
+ modules = [
+ (module // {key = "attr<host.nixos>";})
+ (config.nixos // {key = "attr<fleet.nixos>";})
+ ];
+ specialArgs = {
+ inherit fleetLib;
+ };
+ };
+ };
+ };
+ config = {
+ # imports = [
+ # (mkRemovedOptionModule ["nixosModules"] "replaced with hosts.*.nixos.imports.")
+ # ];
+ nixos = {
+ config._module.args = {
+ nixosHosts = mapAttrs (_: value: value.nixos.config) config.hosts;
+ hosts = config.hosts;
+ host = hostArgs.config;
+ };
+ };
+ };
+ });
+ };
+ imports = [
+ (mkRemovedOptionModule ["nixosModules"] "replaced with nixos.imports.")
+ ];
+ config.nixos.imports =
+ import ./nixos/module-list.nix;
+}
modules/nixos/meta.nixdiffbeforeafterboth--- /dev/null
+++ b/modules/nixos/meta.nix
@@ -0,0 +1,24 @@
+{
+ lib,
+ pkgs,
+ ...
+}: let
+ inherit (lib.options) mkOption;
+ inherit (lib.modules) mkRemovedOptionModule;
+in {
+ options = {
+ # TODO: Give a real name.
+ # Previously it was nixpkgs.resolvedPkgs, which was erroreously merged with nixpkgs override attribute.
+ _resolvedPkgs = mkOption {
+ type = lib.types.pkgs // {description = "nixpkgs.pkgs";};
+ description = "Value of pkgs";
+ };
+ };
+ imports = [
+ (mkRemovedOptionModule ["tags"] "tags are now defined at the host level, not the nixos system level for fast filtering without evaluating unnecessary hosts.")
+ (mkRemovedOptionModule ["network"] "network is now defined at the host level, not the nixos system level")
+ ];
+ config = {
+ _resolvedPkgs = pkgs;
+ };
+}
modules/nixos/module-list.nixdiffbeforeafterboth--- /dev/null
+++ b/modules/nixos/module-list.nix
@@ -0,0 +1,6 @@
+[
+ ./meta.nix
+ ./secrets.nix
+ ./rollback.nix
+ ./nix-sign.nix
+]
modules/nixos/nix-sign.nixdiffbeforeafterboth--- /dev/null
+++ b/modules/nixos/nix-sign.nix
@@ -0,0 +1,21 @@
+# Required for nix copy in build_systems.rs
+{
+ lib,
+ config,
+ ...
+}: let
+ inherit (lib.modules) mkIf;
+ hasPersistentHostname = config.networking.hostName != "";
+in {
+ # https://github.com/NixOS/nix/issues/3023
+ systemd.services.generate-nix-cache-key = mkIf hasPersistentHostname {
+ wantedBy = ["multi-user.target"];
+ serviceConfig.Type = "oneshot";
+ path = [config.nix.package];
+ script = ''
+ [[ -f /etc/nix/private-key ]] && exit
+ nix-store --generate-binary-cache-key ${config.networking.hostName}-1 /etc/nix/private-key /etc/nix/public-key
+ '';
+ };
+ nix.settings.secret-key-files = mkIf hasPersistentHostname "/etc/nix/private-key";
+}
modules/nixos/rollback.nixdiffbeforeafterboth--- /dev/null
+++ b/modules/nixos/rollback.nix
@@ -0,0 +1,48 @@
+# Tied to build_systems.rs
+{config, ...}: {
+ # TODO: Make it work with systemd-initrd approach.
+ # In this case we can't just switch generation and re-run activation script, since the root filesystem might not be
+ # mounted yet. We need to explicitly remove the last generation, and this needs deeper integration with systemd/grub/
+ # whatever user uses. boot.json also might help here.
+
+ systemd.services.rollback-watchdog = {
+ description = "Rollback watchdog";
+ script = ''
+ set -eux
+ if [ -f /etc/fleet_rollback_marker ]; then
+ echo "found the rollback marker, switching to older generation"
+ target=$(cat /etc/fleet_rollback_marker)
+ echo "rolling back profile"
+ nix profile rollback --profile /nix/var/nix/profiles/system --to "$target"
+ echo "executing activation script"
+ "/nix/var/nix/profiles/system-$target-link/bin/switch-to-configuration" switch || true
+ echo "removing rollback marker"
+ rm -f /etc/fleet_rollback_marker
+ else
+ echo "rollback marker was removed, upgrade is succeeded"
+ fi
+ '';
+ path = [
+ # Should have nix-command support
+ config.nix.package
+ ];
+ serviceConfig.Type = "exec";
+ unitConfig = {
+ X-StopOnRemoval = false;
+ X-RestartIfChanged = false;
+ X-StopIfChanged = false;
+ };
+ };
+
+ systemd.timers.rollback-watchdog = {
+ description = "Timer for rollback watchdog";
+ wantedBy = ["timers.target"];
+ timerConfig = {
+ OnActiveSec = "3min";
+ RemainAfterElapse = false;
+ };
+ unitConfig = {
+ ConditionPathExists = "/etc/fleet_rollback_marker";
+ };
+ };
+}
modules/nixos/secrets.nixdiffbeforeafterboth--- /dev/null
+++ b/modules/nixos/secrets.nix
@@ -0,0 +1,155 @@
+{
+ lib,
+ fleetLib,
+ config,
+ pkgs,
+ ...
+}: let
+ inherit (builtins) hashString;
+ inherit (lib.stringsWithDeps) stringAfter;
+ inherit (lib.options) mkOption;
+ inherit (lib.lists) optional;
+ inherit (lib.attrsets) mapAttrs;
+ inherit (lib.modules) mkIf;
+ inherit (lib.types) submodule str attrsOf nullOr unspecified lazyAttrsOf;
+ inherit (fleetLib.strings) decodeRawSecret;
+
+ sysConfig = config;
+ secretPartType = secretName:
+ submodule ({config, ...}: let
+ partName = config._module.args.name;
+ in {
+ options = {
+ raw = mkOption {
+ type = str;
+ internal = true;
+ description = "Encoded & Encrypted secret part data, passed from fleet.nix";
+ };
+ hash = mkOption {
+ type = str;
+ description = "Hash of secret in encoded format";
+ };
+ path = mkOption {
+ type = str;
+ description = "Path to secret part, incorporating data hash (thus it will be updated on secret change)";
+ };
+ stablePath = mkOption {
+ type = str;
+ description = "Path to secret part, incorporating data hash (thus it will be updated on secret change)";
+ };
+ data = mkOption {
+ type = str;
+ description = "Secret public data (only available for plaintext)";
+ };
+ };
+ config = {
+ hash = hashString "sha1" config.raw;
+ data = decodeRawSecret config.raw;
+ path = "/run/secrets/${secretName}/${config.hash}-${partName}";
+ stablePath = "/run/secrets/${secretName}/${partName}";
+ };
+ });
+ secretType = submodule ({config, ...}: let
+ secretName = config._module.args.name;
+ in {
+ freeformType = lazyAttrsOf (secretPartType secretName);
+ options = {
+ shared = mkOption {
+ description = "Is this secret owned by this machine, or propagated from shared secrets";
+ default = false;
+ };
+
+ generator = mkOption {
+ type = nullOr unspecified;
+ description = "Derivation to evaluate for secret generation";
+ default = null;
+ };
+ mode = mkOption {
+ type = str;
+ description = "Secret mode";
+ default = "0440";
+ };
+ owner = mkOption {
+ type = str;
+ description = "Owner of the secret";
+ default = "root";
+ };
+ group = mkOption {
+ type = str;
+ description = "Group of the secret";
+ default = sysConfig.users.users.${config.owner}.group;
+ };
+ };
+ });
+ processPart = part: {
+ inherit (part) raw path stablePath;
+ };
+ processSecret = secret:
+ {
+ inherit (secret) group mode owner;
+ }
+ // (mapAttrs (_: processPart) (removeAttrs secret [
+ "shared"
+ "generator"
+ "mode"
+ "group"
+ "owner"
+ ]));
+ secretsFile = pkgs.writeTextFile {
+ name = "secrets.json";
+ text =
+ builtins.toJSON (mapAttrs (_: processSecret)
+ config.secrets);
+ };
+ useSysusers = (config.systemd ? sysusers && config.systemd.sysusers.enable) || (config ? userborn && config.userborn.enable);
+in {
+ options = {
+ secrets = mkOption {
+ type = attrsOf secretType;
+ default = {};
+ description = "Host-local secrets";
+ };
+ };
+ config = {
+ environment.systemPackages = [pkgs.fleet-install-secrets];
+
+ systemd.services.fleet-install-secrets = mkIf useSysusers {
+ wantedBy = ["sysinit.target"];
+ after = ["systemd-sysusers.service"];
+ restartTriggers = [
+ secretsFile
+ ];
+ aliases = [
+ "sops-install-secrets"
+ "agenix-install-secrets"
+ ];
+
+ unitConfig.DefaultDependencies = false;
+
+ serviceConfig = {
+ Type = "oneshot";
+ RemainAfterExit = true;
+ ExecStart = "${pkgs.fleet-install-secrets}/bin/fleet-install-secrets install ${secretsFile}";
+ };
+ };
+ system.activationScripts.decryptSecrets =
+ mkIf (!useSysusers)
+ (
+ stringAfter (
+ [
+ # secrets are owned by user/group, thus we need to refer to those
+ "users"
+ "groups"
+ "specialfs"
+ ]
+ # nixos-impermanence compatibility: secrets are encrypted by host-key,
+ # but with impermanence we expect that the host-key is installed by
+ # persist-file activation script.
+ ++ (optional (config.system.activationScripts ? "persist-files") "persist-files")
+ ) ''
+ 1>&2 echo "setting up secrets"
+ ${pkgs.fleet-install-secrets}/bin/fleet-install-secrets install ${secretsFile}
+ ''
+ );
+ };
+}
modules/nixpkgs.nixdiffbeforeafterboth--- /dev/null
+++ b/modules/nixpkgs.nix
@@ -0,0 +1,55 @@
+{
+ lib,
+ fleetLib,
+ config,
+ ...
+}: let
+ inherit (lib.options) mkOption;
+ inherit (lib.types) path;
+ inherit (lib.modules) mkRemovedOptionModule;
+ inherit (fleetLib.options) mkHostsOption;
+ inherit (fleetLib.types) listOfOverlay;
+
+ _file = ./nixpkgs.lib;
+in {
+ options = {
+ nixpkgs = {
+ buildUsing = mkOption {
+ description = ''
+ Default nixpkgs to use for building the systems.
+ '';
+ type = path;
+ };
+ overlays = mkOption {
+ description = ''
+ Package overlays to apply for all the hosts, gets propagated into
+ `hosts.*.nixosModules.nixpkgs.overlays`.
+ '';
+ type = listOfOverlay;
+ };
+ };
+ hosts = mkHostsOption {
+ inherit _file;
+ options.nixpkgs.buildUsing = mkOption {
+ description = ''
+ Nixpkgs to use for building the system.
+
+ Note that this option is defined at the host level, not the nixosModules level,
+ nixosModules will be evaluated using this flake input.
+ '';
+ type = path;
+ default = config.nixpkgs.buildUsing;
+ };
+ # imports = [
+ # (mkRemovedOptionModule ["nixpkgs" "overlays"] "this option needs to be specified at nixosModules level")
+ # ];
+ config.nixos = {
+ inherit _file;
+ nixpkgs.overlays = config.nixpkgs.overlays;
+ imports = [
+ (mkRemovedOptionModule ["nixpkgs" "buildUsing"] "this option should be specified at the host level, not the nixosModules level")
+ ];
+ };
+ };
+ };
+}
modules/secrets-data.nixdiffbeforeafterboth--- /dev/null
+++ b/modules/secrets-data.nix
@@ -0,0 +1,105 @@
+{
+ lib,
+ fleetLib,
+ config,
+ ...
+}: let
+ inherit (fleetLib.options) mkDataOption;
+ inherit (lib.options) mkOption;
+ inherit (lib.types) lazyAttrsOf nullOr listOf str attrsOf submodule bool;
+ inherit (lib.attrsets) mapAttrsToList mapAttrs catAttrs filterAttrs genAttrs;
+ inherit (lib.lists) sort unique concatLists;
+ inherit (lib.strings) toJSON;
+
+ secretDataValue = {
+ options = {
+ raw = mkOption {
+ type = nullOr str;
+ description = "Encrypted + encoded secret data";
+ default = null;
+ };
+ };
+ };
+
+ sharedSecretData = {
+ freeformType = attrsOf (submodule secretDataValue);
+ options = {
+ createdAt = mkOption {
+ type = str;
+ description = "When this secret was (re)generated";
+ default = null;
+ };
+ expiresAt = mkOption {
+ type = nullOr str;
+ description = "On which date this secret will expire, someone should regenerate this secret before it expires.";
+ default = null;
+ };
+
+ owners = mkOption {
+ type = listOf str;
+ description = ''
+ For which owners this secret is currently encrypted,
+ if not matches expectedOwners - then this secret is considered outdated, and
+ should be regenerated/reencrypted.
+
+ Imported from fleet.nix
+ '';
+ default = [];
+ };
+ };
+ };
+
+ hostSecretData = {
+ freeformType = attrsOf (submodule secretDataValue);
+ options = {
+ createdAt = mkOption {
+ type = str;
+ description = "When this secret was (re)generated";
+ default = null;
+ };
+ expiresAt = mkOption {
+ type = nullOr str;
+ description = "On which date this secret will expire, someone should regenerate this secret before it expires.";
+ default = null;
+ };
+ shared = mkOption {
+ type = bool;
+ description = "On which date this secret will expire, someone should regenerate this secret before it expires.";
+ default = false;
+ };
+ };
+ };
+in {
+ options.data = mkDataOption ({config, ...}: {
+ options = {
+ sharedSecrets = mkOption {
+ type = attrsOf (submodule sharedSecretData);
+ default = {};
+ description = "Stored shared secret data.";
+ };
+ hostSecrets = mkOption {
+ type = attrsOf (attrsOf (submodule hostSecretData));
+ default = {};
+ description = "Host secrets.";
+ internal = true;
+ };
+ };
+ config.hostSecrets = let
+ hostsWithSharedSecrets = unique (concatLists (mapAttrsToList (_: s: s.owners) config.sharedSecrets));
+ secretsHavingHost = host: filterAttrs (_: secret: lib.elem host secret.owners) config.sharedSecrets;
+ toHostSecret = _: secret: (removeAttrs secret ["owners"]) // {shared = true;};
+ in
+ genAttrs hostsWithSharedSecrets (host: mapAttrs toHostSecret (secretsHavingHost host));
+ });
+ config = {
+ assertions =
+ mapAttrsToList
+ (name: secret: {
+ assertion = secret.expectedOwners == null || sort (a: b: a < b) config.data.sharedSecrets.${name}.owners == sort (a: b: a < b) secret.expectedOwners;
+ message = "Shared secret ${name} is expected to be encrypted for ${toJSON secret.expectedOwners}, but it is encrypted for ${toJSON config.data.sharedSecrets.${name}.owners}. Run fleet secrets regenerate to fix";
+ })
+ config.sharedSecrets;
+ sharedSecrets =
+ mapAttrs (_: _: {}) config.data.sharedSecrets;
+ };
+}
modules/secrets.nixdiffbeforeafterboth--- /dev/null
+++ b/modules/secrets.nix
@@ -0,0 +1,134 @@
+{lib, config, ...}: let
+ inherit (lib.options) mkOption;
+ inherit (lib.types) unspecified nullOr listOf str bool attrsOf submodule;
+ inherit (lib.strings) concatStringsSep;
+ inherit (lib.attrsets) mapAttrs;
+
+ sharedSecret = {config, ...}: {
+ options = {
+ expectedOwners = mkOption {
+ type = nullOr (listOf str);
+ description = ''
+ List of hosts to encrypt secret for. null if managed by user (= via owners field from fleet.nix)
+
+ Secrets would be decrypted and stored to /run/secrets/$\{name} on owners
+ '';
+ default = null;
+ };
+ # TODO: Aren't those options may be just desugared to data/expectedData?
+ regenerateOnOwnerAdded = mkOption {
+ type = bool;
+ description = ''
+ Is this secret owner-dependent, and needs to be regenerated on ownership set change, or it may be just reencrypted.
+
+ You want to have this option set to true, when this secret contains some reference to its owners, i.e x509 SANs.
+ '';
+ };
+ regenerateOnOwnerRemoved = mkOption {
+ default = config.regenerateOnOwnerAdded;
+ type = bool;
+ description = ''
+ Should this secret be removed on owner removal, or it may be just reencrypted
+
+ Most probably its value should be equal to regenerateOnOwnerAdded, override only if you know what are you doing.
+ Contrary to regenerateOnOwnerAdded, you may want to set this option to false, when host permissions are revoked
+ in some other way than by this secret ownership, I.e by firewall/etc.
+ '';
+ };
+ generator = mkOption {
+ type = nullOr unspecified;
+ description = "Derivation to evaluate for secret generation";
+ default = null;
+ };
+ };
+ };
+in {
+ options = {
+ sharedSecrets = mkOption {
+ type = attrsOf (submodule sharedSecret);
+ default = {};
+ description = "Shared secrets";
+ };
+ };
+ config = {
+ hosts = mapAttrs (_: secretMap: {
+ nixos.secrets = mapAttrs (_: s: removeAttrs s ["createdAt" "expiresAt"]) secretMap;
+ }) config.data.hostSecrets;
+ nixpkgs.overlays = [
+ (final: prev: {
+ mkSecretGenerators = {recipients}: rec {
+ # TODO: Merge both generators to one with consistent options syntax?
+ # Impure generator is built on local machine, then built closure is copied to remote machine,
+ # and then it is ran in inpure context, so that this generator may access HSMs and other things.
+ mkImpureSecretGenerator = {
+ script,
+ # If set - script will be run on remote machine, otherwise it will be run with fleet project in CWD
+ # (Some secrets-encryption-in-git/managed PKI solution is expected)
+ impureOn ? null,
+ }:
+ (prev.writeShellScript "impureGenerator.sh" ''
+ #!/bin/sh
+ set -eu
+
+ export GENERATOR_HELPER_IDENTITIES="${concatStringsSep "\n" recipients}";
+ export PATH=${final.fleet-generator-helper}/bin:$PATH
+
+ # TODO: Provide tempdir from outside, to make it securely erasurable as needed?
+ tmp=$(mktemp -d)
+ cd $tmp
+ # cd /var/empty
+
+ created_at=$(date -u +"%Y-%m-%dT%H:%M:%S.%NZ")
+
+ ${script}
+
+ if ! test -d $out; then
+ echo "impure generator script did not produce expected \$out output"
+ exit 1
+ fi
+
+ echo -n $created_at > $out/created_at
+ echo -n SUCCESS > $out/marker
+ '')
+ .overrideAttrs (old: {
+ passthru = {
+ inherit impureOn;
+ generatorKind = "impure";
+ };
+ });
+ # Pure generators are disabled for now
+ mkSecretGenerator = {script}: mkImpureSecretGenerator {inherit script;};
+
+ # TODO: Implement consistent naming
+ # Pure secret generator is supposed to be run entirely by nix, using `__impure` derivation type...
+ # But for now, it is ran the same way as `impureSecretGenerator`, but on the local machine.
+ # mkSecretGenerator = {script}:
+ # (prev.writeShellScript "generator.sh" ''
+ # #!/bin/sh
+ # set -eu
+ # # TODO: make nix daemon build secret, not just the script.
+ # cd /var/empty
+ #
+ # created_at=$(date -u +"%Y-%m-%dT%H:%M:%S.%NZ")
+ #
+ # ${script}
+ # if ! test -d $out; then
+ # echo "impure generator script did not produce expected \$out output"
+ # exit 1
+ # fi
+ #
+ # echo -n $created_at > $out/created_at
+ # echo -n SUCCESS > $out/marker
+ # '')
+ # .overrideAttrs (old: {
+ # passthru = {
+ # generatorKind = "pure";
+ # };
+ # # TODO: make nix daemon build secret, not just the script.
+ # # __impure = true;
+ # });
+ };
+ })
+ ];
+ };
+}
nixos/assertions.nixdiffbeforeafterboth--- a/nixos/assertions.nix
+++ /dev/null
@@ -1,24 +0,0 @@
-# Similar module exists for fleet, however it also defines assertions and warnings,
-# which are already defined for nixos.
-{
- lib,
- config,
- ...
-}: let
- inherit (lib.options) mkOption;
- inherit (lib.lists) map filter;
- inherit (lib.types) listOf str;
-in {
- options = {
- errors = mkOption {
- type = listOf str;
- internal = true;
- description = ''
- Similar to warnings, however build will fail if any error exists.
- '';
- };
- };
- config.errors =
- map (v: v.message)
- (filter (v: !v.assertion) config.assertions);
-}
nixos/meta.nixdiffbeforeafterboth--- a/nixos/meta.nix
+++ /dev/null
@@ -1,42 +0,0 @@
-{
- lib,
- pkgs,
- ...
-}: let
- inherit (lib.options) mkOption;
- inherit (lib.types) listOf str submodule;
- inherit (lib.modules) mkRemovedOptionModule;
-in {
- options = {
- # TODO: Give a real name.
- # Previously it was nixpkgs.resolvedPkgs, which was erroreously merged with nixpkgs override attribute.
- _resolvedPkgs = mkOption {
- type = lib.types.pkgs // {description = "nixpkgs.pkgs";};
- description = "Value of pkgs";
- };
- network = mkOption {
- type = submodule {
- options = {
- internalIps = mkOption {
- type = listOf str;
- description = "Internal ips";
- default = [];
- };
- externalIps = mkOption {
- type = listOf str;
- description = "External ips";
- default = [];
- };
- };
- };
- description = "Network definition of host";
- };
- };
- imports = [
- (mkRemovedOptionModule ["tags"] "tags are now defined at the host level, not the nixos system level for fast filtering without evaluating unnecessary hosts.")
- ];
- config = {
- network = {};
- _resolvedPkgs = pkgs;
- };
-}
nixos/modules/module-list.nixdiffbeforeafterboth--- a/nixos/modules/module-list.nix
+++ /dev/null
@@ -1,7 +0,0 @@
-[
- ../assertions.nix
- ../meta.nix
- ../secrets.nix
- ../rollback.nix
- ../nix-sign.nix
-]
nixos/nix-sign.nixdiffbeforeafterboth--- a/nixos/nix-sign.nix
+++ /dev/null
@@ -1,19 +0,0 @@
-# Required for nix copy in build_systems.rs
-{lib, config, ...}:
-let
- inherit (lib.modules) mkIf;
- hasPersistentHostname = config.networking.hostName != "";
-in
-{
- # https://github.com/NixOS/nix/issues/3023
- systemd.services.generate-nix-cache-key = mkIf hasPersistentHostname {
- wantedBy = ["multi-user.target"];
- serviceConfig.Type = "oneshot";
- path = [config.nix.package];
- script = ''
- [[ -f /etc/nix/private-key ]] && exit
- nix-store --generate-binary-cache-key ${config.networking.hostName}-1 /etc/nix/private-key /etc/nix/public-key
- '';
- };
- nix.settings.secret-key-files = mkIf hasPersistentHostname "/etc/nix/private-key";
-}
nixos/rollback.nixdiffbeforeafterboth--- a/nixos/rollback.nix
+++ /dev/null
@@ -1,48 +0,0 @@
-# Tied to build_systems.rs
-{config, ...}: {
- # TODO: Make it work with systemd-initrd approach.
- # In this case we can't just switch generation and re-run activation script, since the root filesystem might not be
- # mounted yet. We need to explicitly remove the last generation, and this needs deeper integration with systemd/grub/
- # whatever user uses. boot.json also might help here.
-
- systemd.services.rollback-watchdog = {
- description = "Rollback watchdog";
- script = ''
- set -eux
- if [ -f /etc/fleet_rollback_marker ]; then
- echo "found the rollback marker, switching to older generation"
- target=$(cat /etc/fleet_rollback_marker)
- echo "rolling back profile"
- nix profile rollback --profile /nix/var/nix/profiles/system --to "$target"
- echo "executing activation script"
- "/nix/var/nix/profiles/system-$target-link/bin/switch-to-configuration" switch || true
- echo "removing rollback marker"
- rm -f /etc/fleet_rollback_marker
- else
- echo "rollback marker was removed, upgrade is succeeded"
- fi
- '';
- path = [
- # Should have nix-command support
- config.nix.package
- ];
- serviceConfig.Type = "exec";
- unitConfig = {
- X-StopOnRemoval = false;
- X-RestartIfChanged = false;
- X-StopIfChanged = false;
- };
- };
-
- systemd.timers.rollback-watchdog = {
- description = "Timer for rollback watchdog";
- wantedBy = ["timers.target"];
- timerConfig = {
- OnActiveSec = "3min";
- RemainAfterElapse = false;
- };
- unitConfig = {
- ConditionPathExists = "/etc/fleet_rollback_marker";
- };
- };
-}
nixos/secrets.nixdiffbeforeafterboth--- a/nixos/secrets.nix
+++ /dev/null
@@ -1,168 +0,0 @@
-{
- lib,
- config,
- pkgs,
- ...
-}: let
- inherit (lib.strings) hasPrefix removePrefix;
- inherit (lib.stringsWithDeps) stringAfter;
- inherit (lib.options) mkOption;
- inherit (lib.lists) optional;
- inherit (lib.attrsets) mapAttrs;
- inherit (lib.modules) mkOptionDefault mkIf;
- inherit (lib.types) submodule str attrsOf nullOr unspecified lazyAttrsOf;
- plaintextPrefix = "<PLAINTEXT>";
- plaintextNewlinePrefix = "<PLAINTEXT-NL>";
-
- sysConfig = config;
- secretPartType = secretName:
- submodule ({config, ...}: {
- options = {
- raw = mkOption {
- description = "Secret in fleet-specific undocumented format, do not use. Import from fleet.nix";
- internal = true;
- };
- hash = mkOption {
- type = str;
- description = "Hash of secret in encoded format";
- };
- path = mkOption {
- type = str;
- description = "Path to secret part, incorporating data hash (thus it will be updated on secret change)";
- };
- stablePath = mkOption {
- type = str;
- description = "Path to secret part, incorporating data hash (thus it will be updated on secret change)";
- };
- data = mkOption {
- type = str;
- description = "Secret public data (only available for plaintext)";
- };
- };
- config = let
- partName = config._module.args.name;
- in {
- hash = mkOptionDefault (builtins.hashString "sha1" config.raw);
- data = mkOptionDefault (
- if hasPrefix plaintextPrefix config.raw
- then removePrefix plaintextPrefix config.raw
- else if hasPrefix plaintextNewlinePrefix config.raw
- then removePrefix plaintextNewlinePrefix config.raw
- else throw "secret.part.data attribute only works for public plaintext secret parts, got ${config.raw}"
- );
- path = mkOptionDefault "/run/secrets/${secretName}/${config.hash}-${partName}";
- stablePath = mkOptionDefault "/run/secrets/${secretName}/${partName}";
- };
- });
- secretType = submodule ({config, ...}: let
- secretName = config._module.args.name;
- in {
- freeformType = lazyAttrsOf (secretPartType secretName);
- options = {
- shared = mkOption {
- description = "Is this secret owned by this machine, or propagated from shared secrets";
- default = false;
- };
- expectedOwners = mkOption {
- type = nullOr unspecified;
- default = null;
- internal = true;
- };
-
- generator = mkOption {
- type = nullOr unspecified;
- description = "Derivation to evaluate for secret generation";
- default = null;
- };
- mode = mkOption {
- type = str;
- description = "Secret mode";
- default = "0440";
- };
- owner = mkOption {
- type = str;
- description = "Owner of the secret";
- default = "root";
- };
- group = mkOption {
- type = str;
- description = "Group of the secret";
- default = sysConfig.users.users.${config.owner}.group;
- };
- };
- });
- processPart = part: {
- inherit (part) raw path stablePath;
- };
- processSecret = secret:
- {
- inherit (secret) group mode owner;
- }
- // (mapAttrs (_: processPart) (removeAttrs secret [
- "shared"
- "generator"
- "mode"
- "group"
- "owner"
-
- # FIXME: Some of those removed attributes shouldn't be here, but there is some error in passing shared secrets from fleet to nixos.
- "expectedOwners"
- ]));
- secretsFile = pkgs.writeTextFile {
- name = "secrets.json";
- text =
- builtins.toJSON (mapAttrs (_: processSecret)
- config.secrets);
- };
- useSysusers = (config.systemd ? sysusers && config.systemd.sysusers.enable) || (config ? userborn && config.userborn.enable);
-in {
- options = {
- secrets = mkOption {
- type = attrsOf secretType;
- default = {};
- description = "Host-local secrets";
- };
- };
- config = {
- environment.systemPackages = [pkgs.fleet-install-secrets];
-
- systemd.services.fleet-install-secrets = mkIf useSysusers {
- wantedBy = ["sysinit.target"];
- after = ["systemd-sysusers.service"];
- restartTriggers = [
- secretsFile
- ];
- aliases = [
- "sops-install-secrets"
- "agenix-install-secrets"
- ];
-
- unitConfig.DefaultDependencies = false;
-
- serviceConfig = {
- Type = "oneshot";
- RemainAfterExit = true;
- ExecStart = "${pkgs.fleet-install-secrets}/bin/fleet-install-secrets install ${secretsFile}";
- };
- };
- system.activationScripts.decryptSecrets =
- mkIf (!useSysusers)
- (
- stringAfter (
- [
- # secrets are owned by user/group, thus we need to refer to those
- "users"
- "groups"
- "specialfs"
- ]
- # nixos-impermanence compatibility: secrets are encrypted by host-key,
- # but with impermanence we expect that the host-key is installed by
- # persist-file activation script.
- ++ (optional (config.system.activationScripts ? "persist-files") "persist-files")
- ) ''
- 1>&2 echo "setting up secrets"
- ${pkgs.fleet-install-secrets}/bin/fleet-install-secrets install ${secretsFile}
- ''
- );
- };
-}