Add nixos module to import secrets automatically #154
@ -1,5 +1,21 @@
|
||||
{
|
||||
imports = [
|
||||
./schema.nix
|
||||
];
|
||||
{ self, ... }: {
|
||||
perSystem = { pkgs, ... }: {
|
||||
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",
|
||||
"nixos-generators": "nixos-generators",
|
||||
"nixpkgs": "nixpkgs",
|
||||
"sops-nix": "sops-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": {
|
||||
"inputs": {
|
||||
"nixpkgs": [
|
||||
|
@ -7,6 +7,9 @@
|
||||
floco.inputs.nixpkgs.follows = "nixpkgs";
|
||||
disko.url = "github:nix-community/disko";
|
||||
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.inputs.nixpkgs.follows = "nixpkgs";
|
||||
flake-parts.url = "github:hercules-ci/flake-parts";
|
||||
|
@ -1,6 +1,10 @@
|
||||
{ ... }: {
|
||||
{ inputs, ... }: {
|
||||
flake.nixosModules = {
|
||||
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."""
|
||||
while True:
|
||||
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:
|
||||
user = name
|
||||
|
Loading…
Reference in New Issue
Block a user