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.nixdiffbeforeafterbothno changes
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.nixdiffbeforeafterboth--- a/modules/fleet/secrets.nix
+++ /dev/null
@@ -1,210 +0,0 @@
-{
- lib,
- fleetLib,
- config,
- ...
-}: let
- inherit (fleetLib.options) mkHostsOption;
- inherit (lib.options) mkOption;
- inherit (lib.types) lazyAttrsOf unspecified nullOr listOf str bool attrsOf submodule;
- inherit (lib.lists) sort elem;
- inherit (lib.attrsets) mapAttrsToList mapAttrs filterAttrs;
- inherit (lib.strings) toJSON concatStringsSep;
-
- sharedSecret = {config, ...}: {
- freeformType = lazyAttrsOf unspecified;
- 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;
- };
- createdAt = mkOption {
- type = nullOr 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 = [];
- };
- };
- };
- hostSecret = {
- freeformType = lazyAttrsOf unspecified;
- options = {
- createdAt = mkOption {
- type = nullOr str;
- default = null;
- };
- expiresAt = mkOption {
- type = nullOr str;
- default = null;
- };
- };
- };
- inherit (config) hostSecrets sharedSecrets;
-in {
- options = {
- version = mkOption {
- type = str;
- internal = true;
- };
- sharedSecrets = mkOption {
- type = attrsOf (submodule sharedSecret);
- default = {};
- description = "Shared secrets";
- };
- hostSecrets = mkOption {
- type = attrsOf (attrsOf (submodule hostSecret));
- default = {};
- description = "Host secrets. Imported from fleet.nix";
- internal = true;
- };
- hosts = mkHostsOption ({config, ...}: {
- nixos = {
- secrets = let
- host = config._module.args.name;
- processSecret = v:
- (removeAttrs v ["createdAt" "expiresAt" "expectedOwners" "owners" "regenerateOnOwnerAdded" "regenerateOnOwnerRemoved"])
- // {
- shared = true;
- };
- in
- (
- mapAttrs (_: processSecret)
- (filterAttrs (_: v: elem host v.owners) sharedSecrets)
- )
- // (mapAttrs (_: processSecret) (hostSecrets.${host} or {}));
- _file = ./secrets.nix;
- };
- });
- };
- config = {
- assertions =
- mapAttrsToList
- (name: secret: {
- assertion = secret.expectedOwners == null || sort (a: b: a < b) secret.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 secret.owners}. Run fleet secrets regenerate to fix";
- })
- config.sharedSecrets;
- 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;
- # });
- };
- })
- ];
- };
-}
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}
- ''
- );
- };
-}