From 3ab8bfbb91c4ad75129f5d9fee9f23394d5a25d5 Mon Sep 17 00:00:00 2001 From: "Finn@MiBook" Date: Tue, 8 Sep 2026 15:43:36 +0200 Subject: [PATCH] feat(sops): add sops-nix input, core module and devShell tooling Wires sops-nix into every machine via machines/core/sops.nix so hosts decrypt with their existing SSH host key. Adds a devShell exposing sops/age/ssh-to-age for the operator workflow, which also fixes .envrc's previously no-op `use flake`. --- .gitignore | 4 +++- .sops.yaml | 17 +++++++++++++++++ flake.lock | 23 ++++++++++++++++++++++- flake.nix | 13 +++++++++++++ machines/configuration.nix | 2 ++ machines/core/default.nix | 1 + machines/core/sops.nix | 31 +++++++++++++++++++++++++++++++ 7 files changed, 89 insertions(+), 2 deletions(-) create mode 100644 .sops.yaml create mode 100644 machines/core/sops.nix diff --git a/.gitignore b/.gitignore index 47dc3e3..869bc14 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,3 @@ -AGENTS.md \ No newline at end of file +AGENTS.md +*.dec +keys.txt \ No newline at end of file diff --git a/.sops.yaml b/.sops.yaml new file mode 100644 index 0000000..e46142f --- /dev/null +++ b/.sops.yaml @@ -0,0 +1,17 @@ +keys: + - &admin_finn age1a0v442ct2qfjcdhyktn25spw02yq4fl08plrrvuradwwxzsle4mq8thln4 + - &host_mibook age1n3uqtee5ymykrls6k04dznq50cwmj99hw7ny83yxjndt4y2ncf5qhjwa2p + # - &host_jupiter age1... # TODO: Phase 9, see docs/secrets.md + +creation_rules: + - path_regex: ^secrets/mibook\.yaml$ + key_groups: + - age: + - *admin_finn + - *host_mibook + # - path_regex: ^secrets/jupiter\.yaml$ # TODO: Phase 9 + # key_groups: + # - age: [ *admin_finn, *host_jupiter ] + # - path_regex: ^secrets/common\.yaml$ # TODO: Phase 9 + # key_groups: + # - age: [ *admin_finn, *host_mibook, *host_jupiter ] diff --git a/flake.lock b/flake.lock index 7515072..ac82b7d 100644 --- a/flake.lock +++ b/flake.lock @@ -264,7 +264,28 @@ "nixos-generators": "nixos-generators", "nixos-hardware": "nixos-hardware", "nixpkgs": "nixpkgs_3", - "nixpkgs-unstable": "nixpkgs-unstable" + "nixpkgs-unstable": "nixpkgs-unstable", + "sops-nix": "sops-nix" + } + }, + "sops-nix": { + "inputs": { + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1788337237, + "narHash": "sha256-gkSH8VUtCo6hnysNmb9DbTuDepH2t5pv+QWjP75xKAk=", + "owner": "Mic92", + "repo": "sops-nix", + "rev": "fbf759290e0cb0a98dfc813a4eb7d53ad1dacb57", + "type": "github" + }, + "original": { + "owner": "Mic92", + "repo": "sops-nix", + "type": "github" } } }, diff --git a/flake.nix b/flake.nix index 362e14f..0b4a512 100644 --- a/flake.nix +++ b/flake.nix @@ -11,6 +11,10 @@ url = "github:nix-community/nixos-generators"; inputs.nixpkgs.follows = "nixpkgs"; }; + sops-nix = { + url = "github:Mic92/sops-nix"; + inputs.nixpkgs.follows = "nixpkgs"; + }; }; @@ -36,6 +40,15 @@ _module.args.pkgs = import inputs.nixpkgs { inherit system; }; + + devShells.default = pkgs.mkShell { + packages = with pkgs; [ + sops + age + ssh-to-age + nixfmt + ]; + }; }; systems = [ "x86_64-linux" ]; diff --git a/machines/configuration.nix b/machines/configuration.nix index c6af679..eee3f25 100644 --- a/machines/configuration.nix +++ b/machines/configuration.nix @@ -4,6 +4,7 @@ let nixpkgs nixpkgs-unstable nixos-hardware + sops-nix #hyprland ; nixosSystem = nixpkgs.lib.makeOverridable nixpkgs.lib.nixosSystem; @@ -40,6 +41,7 @@ let ]; } ../modules + sops-nix.nixosModules.sops # ../profiles ]; defaultModules = baseModules ++ customModules; diff --git a/machines/core/default.nix b/machines/core/default.nix index 8f95b17..aafd72a 100644 --- a/machines/core/default.nix +++ b/machines/core/default.nix @@ -3,5 +3,6 @@ ./modules.nix ./network.nix ./nix.nix + ./sops.nix ./users.nix ] diff --git a/machines/core/sops.nix b/machines/core/sops.nix new file mode 100644 index 0000000..dc48f15 --- /dev/null +++ b/machines/core/sops.nix @@ -0,0 +1,31 @@ +# sops-nix plumbing — applied to every machine. +# See docs/secrets.md for the day-to-day workflow. +{ + config, + lib, + pkgs, + ... +}: +{ + # Each host defaults to its own secrets/.yaml. Override per-secret + # with sops.secrets..sopsFile for shared files. + sops.defaultSopsFile = lib.mkDefault (../../secrets + "/${config.networking.hostName}.yaml"); + sops.defaultSopsFormat = "yaml"; + sops.validateSopsFiles = true; + + # Decrypt with the host's own SSH host key, converted to age. + # Explicit rather than inheriting the openssh-derived default, so this does + # not silently break if services.openssh is ever disabled. + sops.age.sshKeyPaths = [ "/etc/ssh/ssh_host_ed25519_key" ]; + sops.age.generateKey = false; + + # Age only. The default here is the RSA host key imported as a GPG key, + # which is deprecated and an extra failure surface. + sops.gnupg.sshKeyPaths = [ ]; + + environment.systemPackages = with pkgs; [ + sops + age + ssh-to-age + ]; +}