This commit is contained in:
2026-06-06 00:53:26 +04:00
parent 90eca4e469
commit 9a385176cb
3 changed files with 58 additions and 41 deletions
+47 -34
View File
@@ -16,14 +16,6 @@ in
default = [ ]; default = [ ];
description = "A list of master keys for encrypting secrets."; description = "A list of master keys for encrypting secrets.";
}; };
homeIdentities = mkOption {
type = types.listOf types.str;
default = [ ];
example = [
"laptop-home"
"wawa-wawa-home"
];
};
secretsDir = mkOption { secretsDir = mkOption {
type = types.str; type = types.str;
default = null; default = null;
@@ -56,47 +48,68 @@ in
config = config =
let let
secrets = builtins.fromJSON (builtins.readFile "${inputs.self}/${cfg.secretsDir}/secrets.json"); secrets = builtins.fromJSON (builtins.readFile "${inputs.self}/${cfg.secretsDir}/secrets.json");
all_keys = lib.flatten (lib.concatAttrValues per_host_keys);
per_host_keys = lib.mergeAttrsList ( toKeyList =
lib.flatten ( v:
map ( if builtins.isString v then
x:
builtins.mapAttrs (
name: value:
let
v = value.sopsPublic or [ ];
type = builtins.typeOf v;
vv =
if type == "string" then
[ v ] [ v ]
else if type == "list" then else if builtins.isList v then
v v
else else
throw "Unexpected type ${type} for sopsPublic in host ${name}"; throw "Unexpected type ${builtins.typeOf v} for sopsPublic";
in
vv # Every host across every system: hostName -> [host pubkeys].
) x host_keys = lib.mergeAttrsList (
lib.flatten (
map (
perSystem:
lib.mapAttrsToList (_: host: { ${host.hostName} = toKeyList (host.sopsPublic or [ ]); }) perSystem
) (builtins.attrValues den.hosts) ) (builtins.attrValues den.hosts)
) )
); );
# Every user on every host is a home identity "<hostName>@<userName>".
home_keys = lib.mergeAttrsList (
lib.flatten (
map (
perSystem:
lib.mapAttrsToList (
_: host:
lib.mapAttrsToList (
_: user: { "${host.hostName}@${user.userName}" = toKeyList (user.sopsPublic or [ ]); }
) (host.users or { })
) perSystem
) (builtins.attrValues den.hosts)
)
);
# Replaces the old hand-maintained `homeIdentities` option.
homeIdentities = builtins.attrNames home_keys;
# A secret's `hosts` may target either a host or a home identity.
identity_keys = host_keys // home_keys;
all_host_keys = lib.flatten (lib.attrValues host_keys);
all_home_keys = lib.flatten (lib.attrValues home_keys);
secret_map = lib.mapAttrs ( secret_map = lib.mapAttrs (
name: value: name: value:
let let
sopskeys = lib.unique ( sopskeys = lib.unique (
lib.flatten (map (k: per_host_keys.${k}) value.hosts) lib.flatten (map (k: identity_keys.${k}) value.hosts)
++ (lib.optionals value.global all_keys) ++ (lib.optionals value.globalHosts all_host_keys)
++ (lib.optionals value.globalHomes all_home_keys)
++ cfg.masterKeys ++ cfg.masterKeys
); );
# Descriptive flag: is this secret consumed by a home-manager user? # Descriptive flag: is this secret consumed by a home-manager user?
# True when it is global or targets any identity in `homeIdentities`. # True when it is global to homes or targets any home identity.
home = value.global || lib.any (h: lib.elem h cfg.homeIdentities) value.hosts; home = value.globalHomes || lib.any (h: lib.elem h homeIdentities) value.hosts;
in in
{ {
inherit (value) inherit (value)
format format
neededForUsers neededForUsers
hosts hosts
global globalHosts
globalHomes
; ;
keys = value.keys or [ ]; keys = value.keys or [ ];
inherit sopskeys home; inherit sopskeys home;
@@ -114,7 +127,7 @@ in
lib.mapAttrsToList ( lib.mapAttrsToList (
name: value: name: value:
let let
hasHost = (lib.elem host value.hosts) || value.global; hasHost = (lib.elem host value.hosts) || value.globalHosts;
isYamlOrJson = value.format == "yaml" || value.format == "json"; isYamlOrJson = value.format == "yaml" || value.format == "json";
in in
( (
@@ -158,7 +171,7 @@ in
lib.mapAttrsToList ( lib.mapAttrsToList (
name: value: name: value:
let let
hasHost = (lib.elem identity value.hosts) || value.global; hasHost = (lib.elem identity value.hosts) || value.globalHomes;
isYamlOrJson = value.format == "yaml" || value.format == "json"; isYamlOrJson = value.format == "yaml" || value.format == "json";
in in
( (
@@ -193,8 +206,8 @@ in
flake.secretsManifest = { flake.secretsManifest = {
secretsDir = cfg.secretsDir; secretsDir = cfg.secretsDir;
masterKeys = cfg.masterKeys; masterKeys = cfg.masterKeys;
hosts = lib.mapAttrs (_: keys: { inherit keys; }) per_host_keys; hosts = lib.mapAttrs (_: keys: { inherit keys; }) host_keys;
homeIdentities = cfg.homeIdentities; inherit homeIdentities;
secrets = secret_map; secrets = secret_map;
}; };
secrets.nixosModule = { secrets.nixosModule = {
+4 -1
View File
@@ -25,7 +25,10 @@ pub struct HostInfo {
#[derive(Debug, Clone, Deserialize)] #[derive(Debug, Clone, Deserialize)]
pub struct SecretInfo { pub struct SecretInfo {
pub format: SecretFormat, pub format: SecretFormat,
pub global: bool, #[serde(rename = "globalHosts")]
pub global_hosts: bool,
#[serde(rename = "globalHomes")]
pub global_homes: bool,
pub hosts: Vec<String>, pub hosts: Vec<String>,
#[serde(rename = "neededForUsers")] #[serde(rename = "neededForUsers")]
pub needed_for_users: bool, pub needed_for_users: bool,
+6 -5
View File
@@ -81,16 +81,17 @@ fn render_secret_detail(state: &App, area: Rect, buf: &mut Buffer) {
])); ]));
// Scope // Scope
let scope = if secret.global { let scope = match (secret.global_hosts, secret.global_homes) {
"global".to_string() (true, true) => "global (hosts + homes)".to_string(),
} else { (true, false) => "global (hosts)".to_string(),
"host-specific".to_string() (false, true) => "global (homes)".to_string(),
(false, false) => "host-specific".to_string(),
}; };
lines.push(Line::from(vec![ lines.push(Line::from(vec![
Span::styled(" Scope: ", Style::default().fg(Color::DarkGray)), Span::styled(" Scope: ", Style::default().fg(Color::DarkGray)),
Span::styled(scope, Style::default().fg(Color::White)), Span::styled(scope, Style::default().fg(Color::White)),
])); ]));
if !secret.global { if !(secret.global_hosts && secret.global_homes) {
// Hosts / identities. Home-manager identities are coloured distinctly // Hosts / identities. Home-manager identities are coloured distinctly
// so it is obvious which targets are user (home) rather than system. // so it is obvious which targets are user (home) rather than system.
let mut host_spans: Vec<Span> = vec![Span::styled( let mut host_spans: Vec<Span> = vec![Span::styled(