Add nixos module to import secrets automatically #154
@ -1,5 +1,21 @@
|
|||||||
{
|
{ self, ... }: {
|
||||||
imports = [
|
perSystem = { pkgs, ... }: {
|
||||||
./schema.nix
|
checks =
|
||||||
];
|
let
|
||||||
|
nixosTestArgs = {
|
||||||
|
# reference to nixpkgs for the current system
|
||||||
|
inherit pkgs;
|
||||||
|
# this gives us a reference to our flake but also all flake inputs
|
||||||
|
inherit self;
|
||||||
|
};
|
||||||
|
nixosTests = {
|
||||||
|
# import our test
|
||||||
|
secrets = import ./secrets nixosTestArgs;
|
||||||
|
};
|
||||||
|
schemaTests = pkgs.callPackages ./schemas.nix {
|
||||||
|
inherit self;
|
||||||
|
};
|
||||||
|
in
|
||||||
|
nixosTests // schemaTests;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
18
checks/lib/test-base.nix
Normal file
18
checks/lib/test-base.nix
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
test:
|
||||||
|
{ pkgs
|
||||||
|
, self
|
||||||
|
, ...
|
||||||
|
}:
|
||||||
|
let
|
||||||
|
inherit (pkgs) lib;
|
||||||
|
nixos-lib = import (pkgs.path + "/nixos/lib") { };
|
||||||
|
in
|
||||||
|
(nixos-lib.runTest {
|
||||||
|
hostPkgs = pkgs;
|
||||||
|
# speed-up evaluation
|
||||||
|
defaults.documentation.enable = lib.mkDefault false;
|
||||||
|
# to accept external dependencies such as disko
|
||||||
|
node.specialArgs.self = self;
|
||||||
|
imports = [ test ];
|
||||||
|
}).config.result
|
||||||
|
|
34
checks/schemas.nix
Normal file
34
checks/schemas.nix
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
{ self, runCommand, check-jsonschema, pkgs, lib, ... }:
|
||||||
|
let
|
||||||
|
clanModules = self.clanModules;
|
||||||
|
|
||||||
|
baseModule = {
|
||||||
|
imports =
|
||||||
|
(import (pkgs.path + "/nixos/modules/module-list.nix"))
|
||||||
|
++ [{
|
||||||
|
nixpkgs.hostPlatform = "x86_64-linux";
|
||||||
|
}];
|
||||||
|
};
|
||||||
|
|
||||||
|
optionsFromModule = module:
|
||||||
|
let
|
||||||
|
evaled = lib.evalModules {
|
||||||
|
modules = [ module baseModule ];
|
||||||
|
};
|
||||||
|
in
|
||||||
|
evaled.options.clan.networking;
|
||||||
|
|
||||||
|
clanModuleSchemas = lib.mapAttrs (_: module: self.lib.jsonschema.parseOptions (optionsFromModule module)) clanModules;
|
||||||
|
|
||||||
|
mkTest = name: schema: runCommand "schema-${name}" { } ''
|
||||||
|
${check-jsonschema}/bin/check-jsonschema \
|
||||||
|
--check-metaschema ${builtins.toFile "schema-${name}" (builtins.toJSON schema)}
|
||||||
|
touch $out
|
||||||
|
'';
|
||||||
|
in
|
||||||
|
lib.mapAttrs'
|
||||||
|
(name: schema: {
|
||||||
|
name = "schema-${name}";
|
||||||
|
value = mkTest name schema;
|
||||||
|
})
|
||||||
|
clanModuleSchemas
|
0
checks/secrets/.clan-flake
Normal file
0
checks/secrets/.clan-flake
Normal file
6
checks/secrets/clan-secrets
Executable file
6
checks/secrets/clan-secrets
Executable file
@ -0,0 +1,6 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
set -eux -o pipefail
|
||||||
|
SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )
|
||||||
|
export SOPS_AGE_KEY_FILE="${SCRIPT_DIR}/key.age"
|
||||||
|
nix run .# -- secrets "$@"
|
16
checks/secrets/default.nix
Normal file
16
checks/secrets/default.nix
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
(import ../lib/test-base.nix) {
|
||||||
|
name = "secrets";
|
||||||
|
|
||||||
|
nodes.machine = { self, config, ... }: {
|
||||||
|
imports = [
|
||||||
|
self.nixosModules.secrets
|
||||||
|
];
|
||||||
|
environment.etc."secret".source = config.sops.secrets.foo.path;
|
||||||
|
sops.age.keyFile = ./key.age;
|
||||||
|
clan.sops.sopsDirectory = ./sops;
|
||||||
|
networking.hostName = "machine";
|
||||||
|
};
|
||||||
|
testScript = ''
|
||||||
|
machine.succeed("cat /etc/secret >&2")
|
||||||
|
'';
|
||||||
|
}
|
1
checks/secrets/key.age
Normal file
1
checks/secrets/key.age
Normal file
@ -0,0 +1 @@
|
|||||||
|
AGE-SECRET-KEY-1UCXEUJH6JXF8LFKWFHDM4N9AQE2CCGQZGXLUNV4TKR5KY0KC8FDQ2TY4NX
|
4
checks/secrets/sops/machines/machine/key.json
Executable file
4
checks/secrets/sops/machines/machine/key.json
Executable file
@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"publickey": "age15x8u838dwqflr3t6csf4tlghxm4tx77y379ncqxav7y2n8qp7yzqgrwt00",
|
||||||
|
"type": "age"
|
||||||
|
}
|
1
checks/secrets/sops/secrets/foo/machines/machine
Symbolic link
1
checks/secrets/sops/secrets/foo/machines/machine
Symbolic link
@ -0,0 +1 @@
|
|||||||
|
../../../machines/machine
|
20
checks/secrets/sops/secrets/foo/secret
Normal file
20
checks/secrets/sops/secrets/foo/secret
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"data": "ENC[AES256_GCM,data:bhxF,iv:iNs+IfSU/7EwssZ0GVTF2raxJkVlddfQEPGIBeUYAy8=,tag:JMOKTMW3/ic3UTj9eT9YFQ==,type:str]",
|
||||||
|
"sops": {
|
||||||
|
"kms": null,
|
||||||
|
"gcp_kms": null,
|
||||||
|
"azure_kv": null,
|
||||||
|
"hc_vault": null,
|
||||||
|
"age": [
|
||||||
|
{
|
||||||
|
"recipient": "age15x8u838dwqflr3t6csf4tlghxm4tx77y379ncqxav7y2n8qp7yzqgrwt00",
|
||||||
|
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBxS0g4TEt4S09LQnFKdCtk\nZTlUQWhNUHZmcmZqdGtuZkhhTkMzZDVaWWdNCi9vNnZQeklNaFBBU2x0ditlUDR0\nNGJlRmFFb09WSUFGdEh5TGViTWtacFEKLS0tIE1OMWdQMHhGeFBwSlVEamtHUkcy\ndzI1VHRkZ1o4SStpekVNZmpQSnRkeUkKYmPS9sR6U0NHxd55DjRk29LNFINysOl6\nEM2MTrntLxOHFWZ1QgNx34l4rYIIXx97ONvR0SRpxN0ECL9VonQeZg==\n-----END AGE ENCRYPTED FILE-----\n"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"lastmodified": "2023-08-23T09:11:08Z",
|
||||||
|
"mac": "ENC[AES256_GCM,data:8z819mP4FJXE/ExWM1+/dhaXIXzCglhBuZwE6ikl/jNLUAnv3jYL9c9vPrPFl2by3wXSNzqB4AOiTKDQoxDx2SBQKxeWaUnOajD6hbzskoLqCCBfVx7qOHrk/BULcBvMSxBca4RnzXXoMFTwKs2A1fXqAPvSQd1X4gX6Xm9VXWM=,iv:3YxZX+gaEcRKDN0Kuf9y1oWL+sT/J5B/5CtCf4iur9Y=,tag:0dwyjpvjCqbm9vIrz6WSWQ==,type:str]",
|
||||||
|
"pgp": null,
|
||||||
|
"unencrypted_suffix": "_unencrypted",
|
||||||
|
"version": "3.7.3"
|
||||||
|
}
|
||||||
|
}
|
1
checks/secrets/sops/secrets/foo/users/admin
Symbolic link
1
checks/secrets/sops/secrets/foo/users/admin
Symbolic link
@ -0,0 +1 @@
|
|||||||
|
../../../users/admin
|
4
checks/secrets/sops/users/admin/key.json
Executable file
4
checks/secrets/sops/users/admin/key.json
Executable file
@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"publickey": "age15x8u838dwqflr3t6csf4tlghxm4tx77y379ncqxav7y2n8qp7yzqgrwt00",
|
||||||
|
"type": "age"
|
||||||
|
}
|
22
flake.lock
22
flake.lock
@ -119,9 +119,31 @@
|
|||||||
"floco": "floco",
|
"floco": "floco",
|
||||||
"nixos-generators": "nixos-generators",
|
"nixos-generators": "nixos-generators",
|
||||||
"nixpkgs": "nixpkgs",
|
"nixpkgs": "nixpkgs",
|
||||||
|
"sops-nix": "sops-nix",
|
||||||
"treefmt-nix": "treefmt-nix"
|
"treefmt-nix": "treefmt-nix"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"sops-nix": {
|
||||||
|
"inputs": {
|
||||||
|
"nixpkgs": [
|
||||||
|
"sops-nix"
|
||||||
|
],
|
||||||
|
"nixpkgs-stable": []
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1692500916,
|
||||||
|
"narHash": "sha256-iKADqEOHmyi+LCJ5LzWcM2zH0DP3WHFETjX98blH0tE=",
|
||||||
|
"owner": "Mic92",
|
||||||
|
"repo": "sops-nix",
|
||||||
|
"rev": "4f0f113b7dbcb92edb9c901515fcab0b91c6def7",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "Mic92",
|
||||||
|
"repo": "sops-nix",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
"treefmt-nix": {
|
"treefmt-nix": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"nixpkgs": [
|
"nixpkgs": [
|
||||||
|
@ -7,6 +7,9 @@
|
|||||||
floco.inputs.nixpkgs.follows = "nixpkgs";
|
floco.inputs.nixpkgs.follows = "nixpkgs";
|
||||||
disko.url = "github:nix-community/disko";
|
disko.url = "github:nix-community/disko";
|
||||||
disko.inputs.nixpkgs.follows = "nixpkgs";
|
disko.inputs.nixpkgs.follows = "nixpkgs";
|
||||||
|
sops-nix.url = "github:Mic92/sops-nix";
|
||||||
|
sops-nix.inputs.nixpkgs.follows = "sops-nix";
|
||||||
|
sops-nix.inputs.nixpkgs-stable.follows = "";
|
||||||
nixos-generators.url = "github:nix-community/nixos-generators";
|
nixos-generators.url = "github:nix-community/nixos-generators";
|
||||||
nixos-generators.inputs.nixpkgs.follows = "nixpkgs";
|
nixos-generators.inputs.nixpkgs.follows = "nixpkgs";
|
||||||
flake-parts.url = "github:hercules-ci/flake-parts";
|
flake-parts.url = "github:hercules-ci/flake-parts";
|
||||||
|
@ -1,6 +1,10 @@
|
|||||||
{ ... }: {
|
{ inputs, ... }: {
|
||||||
flake.nixosModules = {
|
flake.nixosModules = {
|
||||||
hidden-ssh-announce.imports = [ ./hidden-ssh-announce.nix ];
|
hidden-ssh-announce.imports = [ ./hidden-ssh-announce.nix ];
|
||||||
installer.imports = [ ./installer.nix ];
|
installer.imports = [ ./installer ];
|
||||||
|
secrets.imports = [
|
||||||
|
inputs.sops-nix.nixosModules.sops
|
||||||
|
./secrets
|
||||||
|
];
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
39
nixosModules/secrets/default.nix
Normal file
39
nixosModules/secrets/default.nix
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
{ lib, config, ... }:
|
||||||
|
let
|
||||||
|
encryptedForThisMachine = name: type:
|
||||||
|
let
|
||||||
|
symlink = config.clan.sops.sopsDirectory + "/secrets/${name}/machines/${config.clan.sops.machineName}";
|
||||||
|
in
|
||||||
|
# WTF, nix bug, my symlink is in the nixos module detected as a directory also it works in the repl
|
||||||
|
type == "directory" && (builtins.readFileType symlink == "directory" || builtins.readFileType symlink == "symlink");
|
||||||
|
secrets = lib.filterAttrs encryptedForThisMachine (builtins.readDir (config.clan.sops.sopsDirectory + "/secrets"));
|
||||||
|
in
|
||||||
|
{
|
||||||
|
imports = [
|
||||||
|
];
|
||||||
|
options = {
|
||||||
|
clan.sops = {
|
||||||
|
machineName = lib.mkOption {
|
||||||
|
type = lib.types.str;
|
||||||
|
default = config.networking.hostName;
|
||||||
|
description = ''
|
||||||
|
Machine used to lookup secrets in the sops directory.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
sopsDirectory = lib.mkOption {
|
||||||
|
type = lib.types.path;
|
||||||
|
description = ''
|
||||||
|
Sops toplevel directory that stores users, machines, groups and secrets.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
config = {
|
||||||
|
sops.secrets = builtins.mapAttrs
|
||||||
|
(name: _: {
|
||||||
|
sopsFile = config.clan.sops.sopsDirectory + "/secrets/${name}/secret";
|
||||||
|
format = "binary";
|
||||||
|
})
|
||||||
|
secrets;
|
||||||
|
};
|
||||||
|
}
|
@ -43,7 +43,7 @@ def get_user_name(user: str) -> str:
|
|||||||
"""Ask the user for their name until a unique one is provided."""
|
"""Ask the user for their name until a unique one is provided."""
|
||||||
while True:
|
while True:
|
||||||
name = input(
|
name = input(
|
||||||
f"Enter your user name for which your sops key will be stored in the repository [default: {user}]: "
|
f"Your key is not yet added to the repository. Enter your user name for which your sops key will be stored in the repository [default: {user}]: "
|
||||||
)
|
)
|
||||||
if name:
|
if name:
|
||||||
user = name
|
user = name
|
||||||
|
Loading…
Reference in New Issue
Block a user