forked from clan/clan-core
refactor clanCore.secrets -> clanCore.facts
This commit is contained in:
parent
0676bf7283
commit
a8d35d37e7
@ -74,7 +74,7 @@
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
clanCore.secretStore = "vm";
|
clanCore.facts.secretStore = "vm";
|
||||||
clanCore.clanDir = ../..;
|
clanCore.clanDir = ../..;
|
||||||
|
|
||||||
environment.systemPackages = [
|
environment.systemPackages = [
|
||||||
|
@ -36,7 +36,7 @@
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
clanCore.secretStore = "vm";
|
clanCore.facts.secretStore = "vm";
|
||||||
|
|
||||||
clan.borgbackup.destinations.test.repo = "borg@localhost:.";
|
clan.borgbackup.destinations.test.repo = "borg@localhost:.";
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
{
|
{
|
||||||
imports = [
|
imports = [
|
||||||
./backups.nix
|
./backups.nix
|
||||||
|
./facts
|
||||||
./manual.nix
|
./manual.nix
|
||||||
./imports.nix
|
./imports.nix
|
||||||
./metadata.nix
|
./metadata.nix
|
||||||
@ -10,7 +11,6 @@
|
|||||||
./outputs.nix
|
./outputs.nix
|
||||||
./packages.nix
|
./packages.nix
|
||||||
./schema.nix
|
./schema.nix
|
||||||
./secrets
|
|
||||||
./vm.nix
|
./vm.nix
|
||||||
./wayland-proxy-virtwl.nix
|
./wayland-proxy-virtwl.nix
|
||||||
./zerotier
|
./zerotier
|
||||||
|
@ -1,47 +1,44 @@
|
|||||||
|
{ config, lib, ... }:
|
||||||
{
|
{
|
||||||
config,
|
imports = [
|
||||||
lib,
|
(lib.mkRemovedOptionModule [
|
||||||
pkgs,
|
"clanCore"
|
||||||
...
|
"secretsPrefix"
|
||||||
}:
|
] "secretsPrefix was only used by the sops module and the code is now integrated in there")
|
||||||
{
|
(lib.mkRenamedOptionModule
|
||||||
options.clanCore.secretStore = lib.mkOption {
|
[
|
||||||
type = lib.types.enum [
|
"clanCore"
|
||||||
"sops"
|
"secretStore"
|
||||||
"password-store"
|
]
|
||||||
"vm"
|
[
|
||||||
"custom"
|
"clanCore"
|
||||||
];
|
"facts"
|
||||||
default = "sops";
|
"secretStore"
|
||||||
description = ''
|
]
|
||||||
method to store secrets
|
)
|
||||||
custom can be used to define a custom secret store.
|
(lib.mkRenamedOptionModule
|
||||||
'';
|
[
|
||||||
};
|
"clanCore"
|
||||||
|
"secretsDirectory"
|
||||||
options.clanCore.secretsDirectory = lib.mkOption {
|
]
|
||||||
type = lib.types.path;
|
[
|
||||||
description = ''
|
"clanCore"
|
||||||
The directory where secrets are installed to. This is backend specific.
|
"facts"
|
||||||
'';
|
"secretDirectory"
|
||||||
};
|
]
|
||||||
|
)
|
||||||
options.clanCore.secretsUploadDirectory = lib.mkOption {
|
(lib.mkRenamedOptionModule
|
||||||
type = lib.types.nullOr lib.types.path;
|
[
|
||||||
default = null;
|
"clanCore"
|
||||||
description = ''
|
"secretsUploadDirectory"
|
||||||
The directory where secrets are uploaded into, This is backend specific.
|
]
|
||||||
'';
|
[
|
||||||
};
|
"clanCore"
|
||||||
|
"facts"
|
||||||
options.clanCore.secretsPrefix = lib.mkOption {
|
"secretUploadDirectory"
|
||||||
type = lib.types.str;
|
]
|
||||||
default = "";
|
)
|
||||||
description = ''
|
];
|
||||||
Prefix for secrets. This is backend specific.
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
|
|
||||||
options.clanCore.secrets = lib.mkOption {
|
options.clanCore.secrets = lib.mkOption {
|
||||||
default = { };
|
default = { };
|
||||||
type = lib.types.attrsOf (
|
type = lib.types.attrsOf (
|
||||||
@ -56,7 +53,7 @@
|
|||||||
};
|
};
|
||||||
generator = lib.mkOption {
|
generator = lib.mkOption {
|
||||||
type = lib.types.submodule (
|
type = lib.types.submodule (
|
||||||
{ config, ... }:
|
{ ... }:
|
||||||
{
|
{
|
||||||
options = {
|
options = {
|
||||||
path = lib.mkOption {
|
path = lib.mkOption {
|
||||||
@ -84,28 +81,6 @@
|
|||||||
The script is expected to generate all secrets and facts defined in the module.
|
The script is expected to generate all secrets and facts defined in the module.
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
finalScript = lib.mkOption {
|
|
||||||
type = lib.types.str;
|
|
||||||
readOnly = true;
|
|
||||||
internal = true;
|
|
||||||
default = ''
|
|
||||||
set -eu -o pipefail
|
|
||||||
|
|
||||||
export PATH="${lib.makeBinPath config.path}:${pkgs.coreutils}/bin"
|
|
||||||
|
|
||||||
# prepare sandbox user
|
|
||||||
mkdir -p /etc
|
|
||||||
cp ${
|
|
||||||
pkgs.runCommand "fake-etc" { } ''
|
|
||||||
export PATH="${pkgs.coreutils}/bin"
|
|
||||||
mkdir -p $out
|
|
||||||
cp /etc/* $out/
|
|
||||||
''
|
|
||||||
}/* /etc/
|
|
||||||
|
|
||||||
${config.script}
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
@ -134,11 +109,11 @@
|
|||||||
description = ''
|
description = ''
|
||||||
path to a secret which is generated by the generator
|
path to a secret which is generated by the generator
|
||||||
'';
|
'';
|
||||||
default = "${config'.clanCore.secretsDirectory}/${config'.clanCore.secretsPrefix}${config.name}";
|
default = "${config'.clanCore.facts.secretDirectory}/${config.name}";
|
||||||
defaultText = lib.literalExpression "\${config'.clanCore.secretsDirectory}/\${config'.clanCore.secretsPrefix}\${config.name}";
|
defaultText = lib.literalExpression "\${config'.clanCore.facts.secretDirectory}/\${config.name}";
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
// lib.optionalAttrs (config'.clanCore.secretStore == "sops") {
|
// lib.optionalAttrs (config'.clanCore.facts.secretStore == "sops") {
|
||||||
groups = lib.mkOption {
|
groups = lib.mkOption {
|
||||||
type = lib.types.listOf lib.types.str;
|
type = lib.types.listOf lib.types.str;
|
||||||
default = config'.clanCore.sops.defaultGroups;
|
default = config'.clanCore.sops.defaultGroups;
|
||||||
@ -190,9 +165,16 @@
|
|||||||
})
|
})
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
imports = [
|
config = lib.mkIf (config.clanCore.secrets != { }) {
|
||||||
./sops.nix
|
clanCore.facts.services = lib.mapAttrs' (
|
||||||
./password-store.nix
|
name: service:
|
||||||
./vm.nix
|
lib.warn "clanCore.secrets.${name} is deprecated, use clanCore.facts.services.${name} instead" (
|
||||||
];
|
lib.nameValuePair name ({
|
||||||
|
secret = service.secrets;
|
||||||
|
public = service.facts;
|
||||||
|
generator = service.generator;
|
||||||
|
})
|
||||||
|
)
|
||||||
|
) config.clanCore.secrets;
|
||||||
|
};
|
||||||
}
|
}
|
217
nixosModules/clanCore/facts/default.nix
Normal file
217
nixosModules/clanCore/facts/default.nix
Normal file
@ -0,0 +1,217 @@
|
|||||||
|
{
|
||||||
|
config,
|
||||||
|
lib,
|
||||||
|
pkgs,
|
||||||
|
...
|
||||||
|
}:
|
||||||
|
{
|
||||||
|
options.clanCore.facts = {
|
||||||
|
secretStore = lib.mkOption {
|
||||||
|
type = lib.types.enum [
|
||||||
|
"sops"
|
||||||
|
"password-store"
|
||||||
|
"vm"
|
||||||
|
"custom"
|
||||||
|
];
|
||||||
|
default = "sops";
|
||||||
|
description = ''
|
||||||
|
method to store secret facts
|
||||||
|
custom can be used to define a custom secret fact store.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
secretModule = lib.mkOption {
|
||||||
|
type = lib.types.str;
|
||||||
|
internal = true;
|
||||||
|
description = ''
|
||||||
|
the python import path to the secret module
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
secretDirectory = lib.mkOption {
|
||||||
|
type = lib.types.path;
|
||||||
|
description = ''
|
||||||
|
The directory where secrets are installed to. This is backend specific.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
secretUploadDirectory = lib.mkOption {
|
||||||
|
type = lib.types.nullOr lib.types.path;
|
||||||
|
default = null;
|
||||||
|
description = ''
|
||||||
|
The directory where secrets are uploaded into, This is backend specific.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
publicStore = lib.mkOption {
|
||||||
|
type = lib.types.enum [
|
||||||
|
"in_repo"
|
||||||
|
"vm"
|
||||||
|
"custom"
|
||||||
|
];
|
||||||
|
default = "in_repo";
|
||||||
|
description = ''
|
||||||
|
method to store public facts.
|
||||||
|
custom can be used to define a custom public fact store.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
publicModule = lib.mkOption {
|
||||||
|
type = lib.types.str;
|
||||||
|
internal = true;
|
||||||
|
description = ''
|
||||||
|
the python import path to the public module
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
publicDirectory = lib.mkOption {
|
||||||
|
type = lib.types.nullOr lib.types.path;
|
||||||
|
default = null;
|
||||||
|
};
|
||||||
|
|
||||||
|
services = lib.mkOption {
|
||||||
|
default = { };
|
||||||
|
type = lib.types.attrsOf (
|
||||||
|
lib.types.submodule (service: {
|
||||||
|
options = {
|
||||||
|
name = lib.mkOption {
|
||||||
|
type = lib.types.str;
|
||||||
|
default = service.config._module.args.name;
|
||||||
|
description = ''
|
||||||
|
Namespace of the service
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
generator = lib.mkOption {
|
||||||
|
type = lib.types.submodule (
|
||||||
|
{ config, ... }:
|
||||||
|
{
|
||||||
|
options = {
|
||||||
|
path = lib.mkOption {
|
||||||
|
type = lib.types.listOf (lib.types.either lib.types.path lib.types.package);
|
||||||
|
default = [ ];
|
||||||
|
description = ''
|
||||||
|
Extra paths to add to the PATH environment variable when running the generator.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
prompt = lib.mkOption {
|
||||||
|
type = lib.types.nullOr lib.types.str;
|
||||||
|
default = null;
|
||||||
|
description = ''
|
||||||
|
prompt text to ask for a value.
|
||||||
|
This value will be passed to the script as the environment variable $prompt_value.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
script = lib.mkOption {
|
||||||
|
type = lib.types.str;
|
||||||
|
description = ''
|
||||||
|
Script to generate the secret.
|
||||||
|
The script will be called with the following variables:
|
||||||
|
- facts: path to a directory where facts can be stored
|
||||||
|
- secrets: path to a directory where secrets can be stored
|
||||||
|
The script is expected to generate all secrets and facts defined in the module.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
finalScript = lib.mkOption {
|
||||||
|
type = lib.types.str;
|
||||||
|
readOnly = true;
|
||||||
|
internal = true;
|
||||||
|
default = ''
|
||||||
|
set -eu -o pipefail
|
||||||
|
|
||||||
|
export PATH="${lib.makeBinPath config.path}:${pkgs.coreutils}/bin"
|
||||||
|
|
||||||
|
# prepare sandbox user
|
||||||
|
mkdir -p /etc
|
||||||
|
cp ${
|
||||||
|
pkgs.runCommand "fake-etc" { } ''
|
||||||
|
export PATH="${pkgs.coreutils}/bin"
|
||||||
|
mkdir -p $out
|
||||||
|
cp /etc/* $out/
|
||||||
|
''
|
||||||
|
}/* /etc/
|
||||||
|
|
||||||
|
${config.script}
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
);
|
||||||
|
};
|
||||||
|
secret = lib.mkOption {
|
||||||
|
default = { };
|
||||||
|
type = lib.types.attrsOf (
|
||||||
|
lib.types.submodule (secret: {
|
||||||
|
options =
|
||||||
|
{
|
||||||
|
name = lib.mkOption {
|
||||||
|
type = lib.types.str;
|
||||||
|
description = ''
|
||||||
|
name of the secret
|
||||||
|
'';
|
||||||
|
default = secret.config._module.args.name;
|
||||||
|
};
|
||||||
|
path = lib.mkOption {
|
||||||
|
type = lib.types.str;
|
||||||
|
description = ''
|
||||||
|
path to a secret which is generated by the generator
|
||||||
|
'';
|
||||||
|
default = "${config.clanCore.facts.secretDirectory}/${secret.config.name}";
|
||||||
|
};
|
||||||
|
}
|
||||||
|
// lib.optionalAttrs (config.clanCore.facts.secretModule == "clan_cli.facts.secret_modules.sops") {
|
||||||
|
groups = lib.mkOption {
|
||||||
|
type = lib.types.listOf lib.types.str;
|
||||||
|
default = config.clanCore.sops.defaultGroups;
|
||||||
|
description = ''
|
||||||
|
Groups to decrypt the secret for. By default we always use the user's key.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
})
|
||||||
|
);
|
||||||
|
description = ''
|
||||||
|
path where the secret is located in the filesystem
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
public = lib.mkOption {
|
||||||
|
default = { };
|
||||||
|
type = lib.types.attrsOf (
|
||||||
|
lib.types.submodule (fact: {
|
||||||
|
options = {
|
||||||
|
name = lib.mkOption {
|
||||||
|
type = lib.types.str;
|
||||||
|
description = ''
|
||||||
|
name of the public fact
|
||||||
|
'';
|
||||||
|
default = fact.config._module.args.name;
|
||||||
|
};
|
||||||
|
path = lib.mkOption {
|
||||||
|
type = lib.types.path;
|
||||||
|
description = ''
|
||||||
|
path to a fact which is generated by the generator
|
||||||
|
'';
|
||||||
|
defaultText = lib.literalExpression "\${config.clanCore.clanDir}/machines/\${config.clanCore.machineName}/facts/\${fact.config.name}";
|
||||||
|
default =
|
||||||
|
config.clanCore.clanDir + "/machines/${config.clanCore.machineName}/facts/${fact.config.name}";
|
||||||
|
};
|
||||||
|
value = lib.mkOption {
|
||||||
|
defaultText = lib.literalExpression "\${config.clanCore.clanDir}/\${fact.config.path}";
|
||||||
|
type = lib.types.nullOr lib.types.str;
|
||||||
|
default =
|
||||||
|
if builtins.pathExists fact.config.path then lib.strings.fileContents fact.config.path else null;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
})
|
||||||
|
);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
})
|
||||||
|
);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
imports = [
|
||||||
|
./compat.nix
|
||||||
|
|
||||||
|
./secret/sops.nix
|
||||||
|
./secret/password-store.nix
|
||||||
|
./secret/vm.nix
|
||||||
|
|
||||||
|
./public/in_repo.nix
|
||||||
|
./public/vm.nix
|
||||||
|
];
|
||||||
|
}
|
6
nixosModules/clanCore/facts/public/in_repo.nix
Normal file
6
nixosModules/clanCore/facts/public/in_repo.nix
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
{ config, lib, ... }:
|
||||||
|
{
|
||||||
|
config = lib.mkIf (config.clanCore.facts.publicStore == "in_repo") {
|
||||||
|
clanCore.facts.publicModule = "clan_cli.facts.public_modules.in_repo";
|
||||||
|
};
|
||||||
|
}
|
6
nixosModules/clanCore/facts/public/vm.nix
Normal file
6
nixosModules/clanCore/facts/public/vm.nix
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
{ config, lib, ... }:
|
||||||
|
{
|
||||||
|
config = lib.mkIf (config.clanCore.facts.publicStore == "vm") {
|
||||||
|
clanCore.facts.publicModule = "clan_cli.facts.public_modules.vm";
|
||||||
|
};
|
||||||
|
}
|
15
nixosModules/clanCore/facts/secret/password-store.nix
Normal file
15
nixosModules/clanCore/facts/secret/password-store.nix
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
{ config, lib, ... }:
|
||||||
|
{
|
||||||
|
options.clan.password-store.targetDirectory = lib.mkOption {
|
||||||
|
type = lib.types.path;
|
||||||
|
default = "/etc/secrets";
|
||||||
|
description = ''
|
||||||
|
The directory where the password store is uploaded to.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
config = lib.mkIf (config.clanCore.facts.secretStore == "password-store") {
|
||||||
|
clanCore.facts.secretDirectory = config.clan.password-store.targetDirectory;
|
||||||
|
clanCore.facts.secretUploadDirectory = config.clan.password-store.targetDirectory;
|
||||||
|
clanCore.facts.secretModule = "clan_cli.facts.secret_modules.password_store";
|
||||||
|
};
|
||||||
|
}
|
@ -41,10 +41,10 @@ in
|
|||||||
description = "The default groups to for encryption use when no groups are specified.";
|
description = "The default groups to for encryption use when no groups are specified.";
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
config = lib.mkIf (config.clanCore.secretStore == "sops") {
|
config = lib.mkIf (config.clanCore.facts.secretStore == "sops") {
|
||||||
clanCore.secretsDirectory = "/run/secrets";
|
clanCore.facts.secretDirectory = "/run/secrets";
|
||||||
clanCore.secretsPrefix = config.clanCore.machineName + "-";
|
clanCore.facts.secretModule = "clan_cli.facts.secret_modules.sops";
|
||||||
system.clan.secretFactsModule = "clan_cli.facts.secret_modules.sops";
|
clanCore.facts.secretUploadDirectory = lib.mkDefault "/var/lib/sops-nix";
|
||||||
sops.secrets = builtins.mapAttrs (name: _: {
|
sops.secrets = builtins.mapAttrs (name: _: {
|
||||||
sopsFile = config.clanCore.clanDir + "/sops/secrets/${name}/secret";
|
sopsFile = config.clanCore.clanDir + "/sops/secrets/${name}/secret";
|
||||||
format = "binary";
|
format = "binary";
|
||||||
@ -57,6 +57,5 @@ in
|
|||||||
sops.age.keyFile = lib.mkIf (builtins.pathExists (
|
sops.age.keyFile = lib.mkIf (builtins.pathExists (
|
||||||
config.clanCore.clanDir + "/sops/secrets/${config.clanCore.machineName}-age.key/secret"
|
config.clanCore.clanDir + "/sops/secrets/${config.clanCore.machineName}-age.key/secret"
|
||||||
)) (lib.mkDefault "/var/lib/sops-nix/key.txt");
|
)) (lib.mkDefault "/var/lib/sops-nix/key.txt");
|
||||||
clanCore.secretsUploadDirectory = lib.mkDefault "/var/lib/sops-nix";
|
|
||||||
};
|
};
|
||||||
}
|
}
|
8
nixosModules/clanCore/facts/secret/vm.nix
Normal file
8
nixosModules/clanCore/facts/secret/vm.nix
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
{ config, lib, ... }:
|
||||||
|
{
|
||||||
|
config = lib.mkIf (config.clanCore.facts.secretStore == "vm") {
|
||||||
|
clanCore.facts.secretDirectory = "/etc/secrets";
|
||||||
|
clanCore.facts.secretUploadDirectory = "/etc/secrets";
|
||||||
|
clanCore.facts.secretModule = "clan_cli.facts.secret_modules.vm";
|
||||||
|
};
|
||||||
|
}
|
@ -23,6 +23,7 @@
|
|||||||
};
|
};
|
||||||
clanDir = lib.mkOption {
|
clanDir = lib.mkOption {
|
||||||
type = lib.types.either lib.types.path lib.types.str;
|
type = lib.types.either lib.types.path lib.types.str;
|
||||||
|
default = ".";
|
||||||
description = ''
|
description = ''
|
||||||
the location of the flake repo, used to calculate the location of facts and secrets
|
the location of the flake repo, used to calculate the location of facts and secrets
|
||||||
'';
|
'';
|
||||||
|
@ -44,33 +44,6 @@
|
|||||||
'';
|
'';
|
||||||
default = false;
|
default = false;
|
||||||
};
|
};
|
||||||
secretsUploadDirectory = lib.mkOption {
|
|
||||||
type = lib.types.path;
|
|
||||||
description = ''
|
|
||||||
the directory on the deployment server where secrets are uploaded
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
publicFactsModule = lib.mkOption {
|
|
||||||
type = lib.types.str;
|
|
||||||
description = ''
|
|
||||||
the python import path to the facts module
|
|
||||||
'';
|
|
||||||
default = "clan_cli.facts.public_modules.in_repo";
|
|
||||||
};
|
|
||||||
secretFactsModule = lib.mkOption {
|
|
||||||
type = lib.types.str;
|
|
||||||
description = ''
|
|
||||||
the python import path to the secrets module
|
|
||||||
'';
|
|
||||||
default = "clan_cli.facts.secret_modules.sops";
|
|
||||||
};
|
|
||||||
secretsData = lib.mkOption {
|
|
||||||
type = lib.types.path;
|
|
||||||
description = ''
|
|
||||||
secret data as json for the generator
|
|
||||||
'';
|
|
||||||
default = pkgs.writers.writeJSON "secrets.json" config.clanCore.secrets;
|
|
||||||
};
|
|
||||||
vm.create = lib.mkOption {
|
vm.create = lib.mkOption {
|
||||||
type = lib.types.path;
|
type = lib.types.path;
|
||||||
description = ''
|
description = ''
|
||||||
@ -92,10 +65,9 @@
|
|||||||
# optimization for faster secret generate/upload and machines update
|
# optimization for faster secret generate/upload and machines update
|
||||||
config = {
|
config = {
|
||||||
system.clan.deployment.data = {
|
system.clan.deployment.data = {
|
||||||
inherit (config.system.clan) publicFactsModule secretFactsModule secretsData;
|
inherit (config.clanCore) facts;
|
||||||
inherit (config.clan.networking) targetHost buildHost;
|
inherit (config.clan.networking) targetHost buildHost;
|
||||||
inherit (config.clan.deployment) requireExplicitUpdate;
|
inherit (config.clan.deployment) requireExplicitUpdate;
|
||||||
inherit (config.clanCore) secretsUploadDirectory;
|
|
||||||
};
|
};
|
||||||
system.clan.deployment.file = pkgs.writeText "deployment.json" (
|
system.clan.deployment.file = pkgs.writeText "deployment.json" (
|
||||||
builtins.toJSON config.system.clan.deployment.data
|
builtins.toJSON config.system.clan.deployment.data
|
||||||
|
@ -1,15 +0,0 @@
|
|||||||
{ config, lib, ... }:
|
|
||||||
{
|
|
||||||
options.clan.password-store.targetDirectory = lib.mkOption {
|
|
||||||
type = lib.types.path;
|
|
||||||
default = "/etc/secrets";
|
|
||||||
description = ''
|
|
||||||
The directory where the password store is uploaded to.
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
config = lib.mkIf (config.clanCore.secretStore == "password-store") {
|
|
||||||
clanCore.secretsDirectory = config.clan.password-store.targetDirectory;
|
|
||||||
clanCore.secretsUploadDirectory = config.clan.password-store.targetDirectory;
|
|
||||||
system.clan.secretFactsModule = "clan_cli.facts.secret_modules.password_store";
|
|
||||||
};
|
|
||||||
}
|
|
@ -1,9 +0,0 @@
|
|||||||
{ config, lib, ... }:
|
|
||||||
{
|
|
||||||
config = lib.mkIf (config.clanCore.secretStore == "vm") {
|
|
||||||
clanCore.secretsDirectory = "/etc/secrets";
|
|
||||||
clanCore.secretsUploadDirectory = "/etc/secrets";
|
|
||||||
system.clan.secretFactsModule = "clan_cli.facts.secret_modules.vm";
|
|
||||||
system.clan.publicFactsModule = "clan_cli.facts.public_modules.vm";
|
|
||||||
};
|
|
||||||
}
|
|
@ -18,9 +18,9 @@ def check_secrets(machine: Machine, service: None | str = None) -> bool:
|
|||||||
if service:
|
if service:
|
||||||
services = [service]
|
services = [service]
|
||||||
else:
|
else:
|
||||||
services = list(machine.secrets_data.keys())
|
services = list(machine.facts_data.keys())
|
||||||
for service in services:
|
for service in services:
|
||||||
for secret_fact in machine.secrets_data[service]["secrets"]:
|
for secret_fact in machine.facts_data[service]["secret"]:
|
||||||
if isinstance(secret_fact, str):
|
if isinstance(secret_fact, str):
|
||||||
secret_name = secret_fact
|
secret_name = secret_fact
|
||||||
else:
|
else:
|
||||||
@ -31,7 +31,7 @@ def check_secrets(machine: Machine, service: None | str = None) -> bool:
|
|||||||
)
|
)
|
||||||
missing_secret_facts.append((service, secret_name))
|
missing_secret_facts.append((service, secret_name))
|
||||||
|
|
||||||
for public_fact in machine.secrets_data[service]["facts"]:
|
for public_fact in machine.facts_data[service]["public"]:
|
||||||
if not public_facts_store.exists(service, public_fact):
|
if not public_facts_store.exists(service, public_fact):
|
||||||
log.info(
|
log.info(
|
||||||
f"Public fact '{public_fact}' for service {service} is missing."
|
f"Public fact '{public_fact}' for service {service} is missing."
|
||||||
|
@ -54,13 +54,13 @@ def generate_service_facts(
|
|||||||
secrets_dir.mkdir(parents=True)
|
secrets_dir.mkdir(parents=True)
|
||||||
env["secrets"] = str(secrets_dir)
|
env["secrets"] = str(secrets_dir)
|
||||||
# compatibility for old outputs.nix users
|
# compatibility for old outputs.nix users
|
||||||
if isinstance(machine.secrets_data[service]["generator"], str):
|
if isinstance(machine.facts_data[service]["generator"], str):
|
||||||
generator = machine.secrets_data[service]["generator"]
|
generator = machine.facts_data[service]["generator"]
|
||||||
else:
|
else:
|
||||||
generator = machine.secrets_data[service]["generator"]["finalScript"]
|
generator = machine.facts_data[service]["generator"]["finalScript"]
|
||||||
if machine.secrets_data[service]["generator"]["prompt"]:
|
if machine.facts_data[service]["generator"]["prompt"]:
|
||||||
prompt_value = prompt(
|
prompt_value = prompt(
|
||||||
machine.secrets_data[service]["generator"]["prompt"]
|
machine.facts_data[service]["generator"]["prompt"]
|
||||||
)
|
)
|
||||||
env["prompt_value"] = prompt_value
|
env["prompt_value"] = prompt_value
|
||||||
# fmt: off
|
# fmt: off
|
||||||
@ -90,7 +90,7 @@ def generate_service_facts(
|
|||||||
)
|
)
|
||||||
files_to_commit = []
|
files_to_commit = []
|
||||||
# store secrets
|
# store secrets
|
||||||
for secret in machine.secrets_data[service]["secrets"]:
|
for secret in machine.facts_data[service]["secret"]:
|
||||||
if isinstance(secret, str):
|
if isinstance(secret, str):
|
||||||
# TODO: This is the old NixOS module, can be dropped everyone has updated.
|
# TODO: This is the old NixOS module, can be dropped everyone has updated.
|
||||||
secret_name = secret
|
secret_name = secret
|
||||||
@ -111,11 +111,11 @@ def generate_service_facts(
|
|||||||
files_to_commit.append(secret_path)
|
files_to_commit.append(secret_path)
|
||||||
|
|
||||||
# store facts
|
# store facts
|
||||||
for name in machine.secrets_data[service]["facts"]:
|
for name in machine.facts_data[service]["public"]:
|
||||||
fact_file = facts_dir / name
|
fact_file = facts_dir / name
|
||||||
if not fact_file.is_file():
|
if not fact_file.is_file():
|
||||||
msg = f"did not generate a file for '{name}' when running the following command:\n"
|
msg = f"did not generate a file for '{name}' when running the following command:\n"
|
||||||
msg += machine.secrets_data[service]["generator"]
|
msg += machine.facts_data[service]["generator"]
|
||||||
raise ClanError(msg)
|
raise ClanError(msg)
|
||||||
fact_file = public_facts_store.set(service, name, fact_file.read_bytes())
|
fact_file = public_facts_store.set(service, name, fact_file.read_bytes())
|
||||||
if fact_file:
|
if fact_file:
|
||||||
@ -147,7 +147,7 @@ def generate_facts(
|
|||||||
|
|
||||||
with TemporaryDirectory() as tmp:
|
with TemporaryDirectory() as tmp:
|
||||||
tmpdir = Path(tmp)
|
tmpdir = Path(tmp)
|
||||||
for service in machine.secrets_data:
|
for service in machine.facts_data:
|
||||||
generate_service_facts(
|
generate_service_facts(
|
||||||
machine=machine,
|
machine=machine,
|
||||||
service=service,
|
service=service,
|
||||||
|
@ -106,8 +106,8 @@ class SecretStore(SecretStoreBase):
|
|||||||
return local_hash.decode() == remote_hash
|
return local_hash.decode() == remote_hash
|
||||||
|
|
||||||
def upload(self, output_dir: Path) -> None:
|
def upload(self, output_dir: Path) -> None:
|
||||||
for service in self.machine.secrets_data:
|
for service in self.machine.facts_data:
|
||||||
for secret in self.machine.secrets_data[service]["secrets"]:
|
for secret in self.machine.facts_data[service]["secret"]:
|
||||||
if isinstance(secret, dict):
|
if isinstance(secret, dict):
|
||||||
secret_name = secret["name"]
|
secret_name = secret["name"]
|
||||||
else:
|
else:
|
||||||
|
@ -14,9 +14,9 @@ class SecretStore(SecretStoreBase):
|
|||||||
self.machine = machine
|
self.machine = machine
|
||||||
|
|
||||||
# no need to generate keys if we don't manage secrets
|
# no need to generate keys if we don't manage secrets
|
||||||
if not hasattr(self.machine, "secrets_data"):
|
if not hasattr(self.machine, "facts_data"):
|
||||||
return
|
return
|
||||||
if not self.machine.secrets_data:
|
if not self.machine.facts_data:
|
||||||
return
|
return
|
||||||
|
|
||||||
if has_machine(self.machine.flake_dir, self.machine.name):
|
if has_machine(self.machine.flake_dir, self.machine.name):
|
||||||
|
@ -47,7 +47,7 @@ class Machine:
|
|||||||
eval_cache: dict[str, str]
|
eval_cache: dict[str, str]
|
||||||
build_cache: dict[str, Path]
|
build_cache: dict[str, Path]
|
||||||
_flake_path: Path | None
|
_flake_path: Path | None
|
||||||
_deployment_info: None | dict[str, str]
|
_deployment_info: None | dict
|
||||||
vm: QMPWrapper
|
vm: QMPWrapper
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
@ -75,7 +75,7 @@ class Machine:
|
|||||||
self.eval_cache: dict[str, str] = {}
|
self.eval_cache: dict[str, str] = {}
|
||||||
self.build_cache: dict[str, Path] = {}
|
self.build_cache: dict[str, Path] = {}
|
||||||
self._flake_path: Path | None = None
|
self._flake_path: Path | None = None
|
||||||
self._deployment_info: None | dict[str, str] = deployment_info
|
self._deployment_info: None | dict = deployment_info
|
||||||
|
|
||||||
state_dir = vm_state_dir(flake_url=str(self.flake), vm_name=self.data.name)
|
state_dir = vm_state_dir(flake_url=str(self.flake), vm_name=self.data.name)
|
||||||
|
|
||||||
@ -88,7 +88,7 @@ class Machine:
|
|||||||
return str(self)
|
return str(self)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def deployment_info(self) -> dict[str, str]:
|
def deployment_info(self) -> dict:
|
||||||
if self._deployment_info is not None:
|
if self._deployment_info is not None:
|
||||||
return self._deployment_info
|
return self._deployment_info
|
||||||
self._deployment_info = json.loads(
|
self._deployment_info = json.loads(
|
||||||
@ -113,26 +113,21 @@ class Machine:
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def secret_facts_module(self) -> str:
|
def secret_facts_module(self) -> str:
|
||||||
return self.deployment_info["secretFactsModule"]
|
return self.deployment_info["facts"]["secretModule"]
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def public_facts_module(self) -> str:
|
def public_facts_module(self) -> str:
|
||||||
return self.deployment_info["publicFactsModule"]
|
return self.deployment_info["facts"]["publicModule"]
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def secrets_data(self) -> dict[str, dict[str, Any]]:
|
def facts_data(self) -> dict[str, dict[str, Any]]:
|
||||||
if self.deployment_info["secretsData"]:
|
if self.deployment_info["facts"]["services"]:
|
||||||
try:
|
return self.deployment_info["facts"]["services"]
|
||||||
return json.loads(Path(self.deployment_info["secretsData"]).read_text())
|
|
||||||
except json.JSONDecodeError as e:
|
|
||||||
raise ClanError(
|
|
||||||
f"Failed to parse secretsData for machine {self.data.name} as json"
|
|
||||||
) from e
|
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def secrets_upload_directory(self) -> str:
|
def secrets_upload_directory(self) -> str:
|
||||||
return self.deployment_info["secretsUploadDirectory"]
|
return self.deployment_info["facts"]["secretUploadDirectory"]
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def flake_dir(self) -> Path:
|
def flake_dir(self) -> Path:
|
||||||
|
Loading…
Reference in New Issue
Block a user