move zerotier secret generation into nixos module
This commit is contained in:
parent
5d9ee64ddc
commit
74a3c85c29
12
flake.lock
12
flake.lock
|
@ -99,16 +99,16 @@
|
||||||
},
|
},
|
||||||
"nixpkgs": {
|
"nixpkgs": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1695360818,
|
"lastModified": 1695741452,
|
||||||
"narHash": "sha256-JlkN3R/SSoMTa+CasbxS1gq+GpGxXQlNZRUh9+LIy/0=",
|
"narHash": "sha256-pDIQmCR0fyb6FKjvURaD6yC5YnE/+rxs5iFQQGgcoNE=",
|
||||||
"owner": "NixOS",
|
"owner": "Mic92",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "e35dcc04a3853da485a396bdd332217d0ac9054f",
|
"rev": "bc160df717ed1e9defe6044092ea66950976e3ed",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
"owner": "NixOS",
|
"owner": "Mic92",
|
||||||
"ref": "nixos-unstable",
|
"ref": "fakeroot",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,9 @@
|
||||||
description = "clan.lol base operating system";
|
description = "clan.lol base operating system";
|
||||||
|
|
||||||
inputs = {
|
inputs = {
|
||||||
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
|
#nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
|
||||||
|
# https://github.com/NixOS/nixpkgs/pull/257462
|
||||||
|
nixpkgs.url = "github:Mic92/nixpkgs/fakeroot";
|
||||||
floco.url = "github:aakropotkin/floco";
|
floco.url = "github:aakropotkin/floco";
|
||||||
floco.inputs.nixpkgs.follows = "nixpkgs";
|
floco.inputs.nixpkgs.follows = "nixpkgs";
|
||||||
disko.url = "github:nix-community/disko/party";
|
disko.url = "github:nix-community/disko/party";
|
||||||
|
|
|
@ -9,6 +9,22 @@
|
||||||
one would have to define system.clan.generateSecrets and system.clan.uploadSecrets
|
one would have to define system.clan.generateSecrets and system.clan.uploadSecrets
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
|
options.clanCore.secretsDirectory = lib.mkOption {
|
||||||
|
type = lib.types.path;
|
||||||
|
description = ''
|
||||||
|
The directory where secrets are installed to. This is backend specific.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
options.clanCore.secretsPrefix = lib.mkOption {
|
||||||
|
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
|
||||||
|
@ -31,22 +47,33 @@
|
||||||
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.
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
secrets = lib.mkOption {
|
secrets =
|
||||||
type = lib.types.attrsOf (lib.types.submodule (secret: {
|
let
|
||||||
options = {
|
config' = config;
|
||||||
name = lib.mkOption {
|
in
|
||||||
type = lib.types.str;
|
lib.mkOption {
|
||||||
description = ''
|
type = lib.types.attrsOf (lib.types.submodule ({ config, ... }: {
|
||||||
name of the secret
|
options = {
|
||||||
'';
|
name = lib.mkOption {
|
||||||
default = secret.config._module.args.name;
|
type = lib.types.str;
|
||||||
|
description = ''
|
||||||
|
name of the secret
|
||||||
|
'';
|
||||||
|
default = 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.secretsDirectory}/${config'.clanCore.secretsPrefix}${config.name}";
|
||||||
|
};
|
||||||
};
|
};
|
||||||
};
|
}));
|
||||||
}));
|
description = ''
|
||||||
description = ''
|
path where the secret is located in the filesystem
|
||||||
path where the secret is located in the filesystem
|
'';
|
||||||
'';
|
};
|
||||||
};
|
|
||||||
facts = lib.mkOption {
|
facts = lib.mkOption {
|
||||||
type = lib.types.attrsOf (lib.types.submodule (fact: {
|
type = lib.types.attrsOf (lib.types.submodule (fact: {
|
||||||
options = {
|
options = {
|
||||||
|
|
|
@ -3,14 +3,8 @@ let
|
||||||
passwordstoreDir = "\${PASSWORD_STORE_DIR:-$HOME/.password-store}";
|
passwordstoreDir = "\${PASSWORD_STORE_DIR:-$HOME/.password-store}";
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
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") {
|
config = lib.mkIf (config.clanCore.secretStore == "password-store") {
|
||||||
|
clanCore.secretsDirectory = passwordstoreDir;
|
||||||
system.clan.generateSecrets = pkgs.writeScript "generate-secrets" ''
|
system.clan.generateSecrets = pkgs.writeScript "generate-secrets" ''
|
||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
set -efu
|
set -efu
|
||||||
|
|
|
@ -3,6 +3,7 @@ let
|
||||||
secretsDir = config.clanCore.clanDir + "/sops/secrets";
|
secretsDir = config.clanCore.clanDir + "/sops/secrets";
|
||||||
groupsDir = config.clanCore.clanDir + "/sops/groups";
|
groupsDir = config.clanCore.clanDir + "/sops/groups";
|
||||||
|
|
||||||
|
|
||||||
# My symlink is in the nixos module detected as a directory also it works in the repl. Is this because of pure evaluation?
|
# My symlink is in the nixos module detected as a directory also it works in the repl. Is this because of pure evaluation?
|
||||||
containsSymlink = path:
|
containsSymlink = path:
|
||||||
builtins.pathExists path && (builtins.readFileType path == "directory" || builtins.readFileType path == "symlink");
|
builtins.pathExists path && (builtins.readFileType path == "directory" || builtins.readFileType path == "symlink");
|
||||||
|
@ -22,7 +23,10 @@ let
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
config = lib.mkIf (config.clanCore.secretStore == "sops") {
|
config = lib.mkIf (config.clanCore.secretStore == "sops") {
|
||||||
|
clanCore.secretsDirectory = "/run/secrets";
|
||||||
|
clanCore.secretsPrefix = config.clanCore.machineName + "-";
|
||||||
system.clan = {
|
system.clan = {
|
||||||
|
|
||||||
generateSecrets = pkgs.writeScript "generate-secrets" ''
|
generateSecrets = pkgs.writeScript "generate-secrets" ''
|
||||||
#!${pkgs.python3}/bin/python
|
#!${pkgs.python3}/bin/python
|
||||||
import json
|
import json
|
||||||
|
|
|
@ -45,8 +45,8 @@ in
|
||||||
{
|
{
|
||||||
options.clan.networking.zerotier = {
|
options.clan.networking.zerotier = {
|
||||||
networkId = lib.mkOption {
|
networkId = lib.mkOption {
|
||||||
type = lib.types.nullOr lib.types.str;
|
type = lib.types.str;
|
||||||
default = null;
|
default = config.clanCore.secrets.zerotier.facts."zerotier-network-id".value;
|
||||||
description = ''
|
description = ''
|
||||||
zerotier networking id
|
zerotier networking id
|
||||||
'';
|
'';
|
||||||
|
@ -63,6 +63,11 @@ in
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
config = lib.mkMerge [
|
config = lib.mkMerge [
|
||||||
|
({
|
||||||
|
# Override license so that we can build zerotierone without
|
||||||
|
# having to re-import nixpkgs.
|
||||||
|
services.zerotierone.package = lib.mkDefault (pkgs.zerotierone.overrideAttrs (_old: { meta = { }; }));
|
||||||
|
})
|
||||||
(lib.mkIf (cfg.networkId != null) {
|
(lib.mkIf (cfg.networkId != null) {
|
||||||
systemd.network.networks.zerotier = {
|
systemd.network.networks.zerotier = {
|
||||||
matchConfig.Name = "zt*";
|
matchConfig.Name = "zt*";
|
||||||
|
@ -85,24 +90,20 @@ in
|
||||||
# only the controller needs to have the key in the repo, the other clients can be dynamic
|
# only the controller needs to have the key in the repo, the other clients can be dynamic
|
||||||
# we generate the zerotier code manually for the controller, since it's part of the bootstrap command
|
# we generate the zerotier code manually for the controller, since it's part of the bootstrap command
|
||||||
clanCore.secrets.zerotier = {
|
clanCore.secrets.zerotier = {
|
||||||
facts."zerotier.network.id" = { };
|
facts."zerotier-network-id" = { };
|
||||||
secrets."zerotier.identity.secret" = { };
|
secrets."zerotier-identity-secret" = { };
|
||||||
generator = ''
|
generator = ''
|
||||||
TMPDIR=$(mktemp -d)
|
export PATH=${lib.makeBinPath [ config.services.zerotierone.package pkgs.fakeroot ]}
|
||||||
trap 'rm -rf "$TMPDIR"' EXIT
|
${pkgs.python3.interpreter} ${./generate-network.py} "$facts/zerotier-network-id" "$secrets/zerotier-identity-secret"
|
||||||
${config.clanCore.clanPkgs.clan-cli}/bin/clan zerotier --outpath "$TMPDIR"
|
|
||||||
cp "$TMPDIR"/network.id "$facts"/zerotier.network.id
|
|
||||||
cp "$TMPDIR"/identity.secret "$secrets"/zerotier.identity.secret
|
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
systemd.services.zerotierone.serviceConfig.ExecStartPre = [
|
systemd.services.zerotierone.serviceConfig.ExecStartPre = [
|
||||||
"+${pkgs.writeShellScript "init_zerotier" ''
|
"+${pkgs.writeShellScript "init-zerotier" ''
|
||||||
cp /etc/secrets/zerotier.identity.secret /var/lib/zerotier-one/identity.secret
|
cp ${config.clanCore.secrets.zerotier.secrets.zerotier-identity-secret.path} /var/lib/zerotier-one/identity.secret
|
||||||
ln -sfT ${pkgs.writeText "net.json" (builtins.toJSON networkConfig)} /var/lib/zerotier-one/controller.d/network/${cfg.networkId}.json
|
ln -sfT ${pkgs.writeText "net.json" (builtins.toJSON networkConfig)} /var/lib/zerotier-one/controller.d/network/${cfg.networkId}.json
|
||||||
''}"
|
''}"
|
||||||
];
|
];
|
||||||
})
|
})
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -9,8 +9,9 @@ from pathlib import Path
|
||||||
from tempfile import TemporaryDirectory
|
from tempfile import TemporaryDirectory
|
||||||
from typing import Any, Iterator, Optional
|
from typing import Any, Iterator, Optional
|
||||||
|
|
||||||
from ..errors import ClanError
|
|
||||||
from ..nix import nix_shell, unfree_nix_shell
|
class ClanError(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
def try_bind_port(port: int) -> bool:
|
def try_bind_port(port: int) -> bool:
|
||||||
|
@ -85,37 +86,17 @@ def zerotier_controller() -> Iterator[ZerotierController]:
|
||||||
if controller_port is None:
|
if controller_port is None:
|
||||||
raise ClanError("cannot find a free port for zerotier controller")
|
raise ClanError("cannot find a free port for zerotier controller")
|
||||||
|
|
||||||
cmd = unfree_nix_shell(
|
|
||||||
["bash", "zerotierone"], ["bash", "-c", "command -v zerotier-one"]
|
|
||||||
)
|
|
||||||
res = subprocess.run(
|
|
||||||
cmd,
|
|
||||||
check=True,
|
|
||||||
text=True,
|
|
||||||
stdout=subprocess.PIPE,
|
|
||||||
)
|
|
||||||
zerotier_exe = res.stdout.strip()
|
|
||||||
if zerotier_exe is None:
|
|
||||||
raise ClanError("cannot find zerotier-one executable")
|
|
||||||
|
|
||||||
if not zerotier_exe.startswith("/nix/store"):
|
|
||||||
raise ClanError(
|
|
||||||
f"zerotier-one executable needs to come from /nix/store: {zerotier_exe}"
|
|
||||||
)
|
|
||||||
with TemporaryDirectory() as d:
|
with TemporaryDirectory() as d:
|
||||||
tempdir = Path(d)
|
tempdir = Path(d)
|
||||||
home = tempdir / "zerotier-one"
|
home = tempdir / "zerotier-one"
|
||||||
home.mkdir()
|
home.mkdir()
|
||||||
cmd = nix_shell(
|
cmd = [
|
||||||
["fakeroot"],
|
"fakeroot",
|
||||||
[
|
"--",
|
||||||
"fakeroot",
|
"zerotier-one",
|
||||||
"--",
|
f"-p{controller_port}",
|
||||||
zerotier_exe,
|
str(home),
|
||||||
f"-p{controller_port}",
|
]
|
||||||
str(home),
|
|
||||||
],
|
|
||||||
)
|
|
||||||
with subprocess.Popen(cmd) as p:
|
with subprocess.Popen(cmd) as p:
|
||||||
try:
|
try:
|
||||||
print(
|
print(
|
||||||
|
@ -146,18 +127,16 @@ def create_network() -> dict:
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
def main(args: argparse.Namespace) -> None:
|
def main() -> None:
|
||||||
|
parser = argparse.ArgumentParser()
|
||||||
|
parser.add_argument("network_id")
|
||||||
|
parser.add_argument("identity_secret")
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
zerotier = create_network()
|
zerotier = create_network()
|
||||||
outpath = Path(args.outpath)
|
Path(args.network_id).write_text(zerotier["networkid"])
|
||||||
outpath.mkdir(parents=True, exist_ok=True)
|
Path(args.identity_secret).write_text(zerotier["secret"])
|
||||||
with open(outpath / "network.id", "w+") as nwid_file:
|
|
||||||
nwid_file.write(zerotier["networkid"])
|
|
||||||
with open(outpath / "identity.secret", "w+") as secret_file:
|
|
||||||
secret_file.write(zerotier["secret"])
|
|
||||||
|
|
||||||
|
|
||||||
def register_parser(parser: argparse.ArgumentParser) -> None:
|
if __name__ == "__main__":
|
||||||
parser.add_argument(
|
main()
|
||||||
"--outpath", help="directory to put the secret file to", required=True
|
|
||||||
)
|
|
||||||
parser.set_defaults(func=main)
|
|
35
nixosModules/clanCore/zerotier/pyproject.toml
Normal file
35
nixosModules/clanCore/zerotier/pyproject.toml
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
[tool.mypy]
|
||||||
|
python_version = "3.10"
|
||||||
|
warn_redundant_casts = true
|
||||||
|
disallow_untyped_calls = true
|
||||||
|
disallow_untyped_defs = true
|
||||||
|
no_implicit_optional = true
|
||||||
|
exclude = "clan_cli.nixpkgs"
|
||||||
|
|
||||||
|
[tool.ruff]
|
||||||
|
line-length = 88
|
||||||
|
|
||||||
|
select = [ "E", "F", "I", "U", "N"]
|
||||||
|
ignore = [ "E501" ]
|
||||||
|
|
||||||
|
[tool.black]
|
||||||
|
line-length = 88
|
||||||
|
target-version = [ "py310" ]
|
||||||
|
include = "\\.pyi?$"
|
||||||
|
exclude = '''
|
||||||
|
/(
|
||||||
|
\.git
|
||||||
|
| \.hg
|
||||||
|
| \.mypy_cache
|
||||||
|
| \.tox
|
||||||
|
| \.venv
|
||||||
|
| _build
|
||||||
|
| buck-out
|
||||||
|
| build
|
||||||
|
| dist
|
||||||
|
# The following are specific to Black, you probably don't want those.
|
||||||
|
| blib2to3
|
||||||
|
| tests/data
|
||||||
|
| profiling
|
||||||
|
)/
|
||||||
|
'''
|
|
@ -3,7 +3,7 @@ import sys
|
||||||
from types import ModuleType
|
from types import ModuleType
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
from . import config, create, machines, secrets, webui, zerotier
|
from . import config, create, machines, secrets, webui
|
||||||
from .errors import ClanError
|
from .errors import ClanError
|
||||||
from .ssh import cli as ssh_cli
|
from .ssh import cli as ssh_cli
|
||||||
|
|
||||||
|
@ -47,9 +47,6 @@ def create_parser(prog: Optional[str] = None) -> argparse.ArgumentParser:
|
||||||
parser_webui = subparsers.add_parser("webui", help="start webui")
|
parser_webui = subparsers.add_parser("webui", help="start webui")
|
||||||
webui.register_parser(parser_webui)
|
webui.register_parser(parser_webui)
|
||||||
|
|
||||||
parser_zerotier = subparsers.add_parser("zerotier", help="create zerotier network")
|
|
||||||
zerotier.register_parser(parser_zerotier)
|
|
||||||
|
|
||||||
if argcomplete:
|
if argcomplete:
|
||||||
argcomplete.autocomplete(parser)
|
argcomplete.autocomplete(parser)
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,7 @@ import subprocess
|
||||||
import tempfile
|
import tempfile
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
from .dirs import nixpkgs_flake, nixpkgs_source, unfree_nixpkgs
|
from .dirs import nixpkgs_flake, nixpkgs_source
|
||||||
|
|
||||||
|
|
||||||
def nix_command(flags: list[str]) -> list[str]:
|
def nix_command(flags: list[str]) -> list[str]:
|
||||||
|
@ -82,20 +82,3 @@ def nix_shell(packages: list[str], cmd: list[str]) -> list[str]:
|
||||||
+ ["-c"]
|
+ ["-c"]
|
||||||
+ cmd
|
+ cmd
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def unfree_nix_shell(packages: list[str], cmd: list[str]) -> list[str]:
|
|
||||||
if os.environ.get("IN_NIX_SANDBOX"):
|
|
||||||
return cmd
|
|
||||||
return (
|
|
||||||
nix_command(
|
|
||||||
[
|
|
||||||
"shell",
|
|
||||||
"-f",
|
|
||||||
str(unfree_nixpkgs()),
|
|
||||||
]
|
|
||||||
)
|
|
||||||
+ packages
|
|
||||||
+ ["-c"]
|
|
||||||
+ cmd
|
|
||||||
)
|
|
||||||
|
|
|
@ -16,7 +16,6 @@
|
||||||
, sops
|
, sops
|
||||||
, stdenv
|
, stdenv
|
||||||
, wheel
|
, wheel
|
||||||
, zerotierone
|
|
||||||
, fakeroot
|
, fakeroot
|
||||||
, rsync
|
, rsync
|
||||||
, ui-assets
|
, ui-assets
|
||||||
|
@ -49,7 +48,6 @@ let
|
||||||
runtimeDependencies = [
|
runtimeDependencies = [
|
||||||
bash
|
bash
|
||||||
nix
|
nix
|
||||||
zerotierone
|
|
||||||
fakeroot
|
fakeroot
|
||||||
openssh
|
openssh
|
||||||
sshpass
|
sshpass
|
||||||
|
@ -77,10 +75,6 @@ let
|
||||||
'';
|
'';
|
||||||
nixpkgs' = runCommand "nixpkgs" { nativeBuildInputs = [ nix ]; } ''
|
nixpkgs' = runCommand "nixpkgs" { nativeBuildInputs = [ nix ]; } ''
|
||||||
mkdir $out
|
mkdir $out
|
||||||
mkdir -p $out/unfree
|
|
||||||
cat > $out/unfree/default.nix <<EOF
|
|
||||||
import "${nixpkgs}" { config = { allowUnfree = true; overlays = []; }; }
|
|
||||||
EOF
|
|
||||||
cat > $out/flake.nix << EOF
|
cat > $out/flake.nix << EOF
|
||||||
{
|
{
|
||||||
description = "dependencies for the clan-cli";
|
description = "dependencies for the clan-cli";
|
||||||
|
|
|
@ -6,16 +6,11 @@
|
||||||
};
|
};
|
||||||
packages = {
|
packages = {
|
||||||
clan-cli = pkgs.python3.pkgs.callPackage ./default.nix {
|
clan-cli = pkgs.python3.pkgs.callPackage ./default.nix {
|
||||||
inherit (self'.packages) ui-assets zerotierone;
|
inherit (self'.packages) ui-assets;
|
||||||
inherit (inputs) nixpkgs;
|
inherit (inputs) nixpkgs;
|
||||||
};
|
};
|
||||||
clan-openapi = self'.packages.clan-cli.clan-openapi;
|
clan-openapi = self'.packages.clan-cli.clan-openapi;
|
||||||
default = self'.packages.clan-cli;
|
default = self'.packages.clan-cli;
|
||||||
|
|
||||||
# Override license so that we can build zerotierone without
|
|
||||||
# having to re-import nixpkgs.
|
|
||||||
zerotierone = pkgs.zerotierone.overrideAttrs (_old: { meta = { }; });
|
|
||||||
## End optional dependencies
|
|
||||||
};
|
};
|
||||||
|
|
||||||
checks = self'.packages.clan-cli.tests;
|
checks = self'.packages.clan-cli.tests;
|
||||||
|
|
|
@ -10,19 +10,13 @@
|
||||||
clan = clan-core.lib.buildClan {
|
clan = clan-core.lib.buildClan {
|
||||||
directory = self;
|
directory = self;
|
||||||
machines = {
|
machines = {
|
||||||
vm1 = { modulesPath, ... }: {
|
vm1 = { modulesPath, lib, ... }: {
|
||||||
imports = [ "${toString modulesPath}/virtualisation/qemu-vm.nix" ];
|
imports = [ "${toString modulesPath}/virtualisation/qemu-vm.nix" ];
|
||||||
clan.networking.deploymentAddress = "__CLAN_DEPLOYMENT_ADDRESS__";
|
clan.networking.deploymentAddress = "__CLAN_DEPLOYMENT_ADDRESS__";
|
||||||
sops.age.keyFile = "__CLAN_SOPS_KEY_PATH__";
|
sops.age.keyFile = "__CLAN_SOPS_KEY_PATH__";
|
||||||
|
system.stateVersion = lib.version;
|
||||||
|
|
||||||
clanCore.secrets.testpassword = {
|
clan.networking.zerotier.controller.enable = true;
|
||||||
generator = ''
|
|
||||||
echo "secret1" > "$secrets/secret1"
|
|
||||||
echo "fact1" > "$facts/fact1"
|
|
||||||
'';
|
|
||||||
secrets.secret1 = { };
|
|
||||||
facts.fact1 = { };
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -24,15 +24,19 @@ def test_upload_secret(
|
||||||
cli.run(["secrets", "users", "add", "user1", age_keys[0].pubkey])
|
cli.run(["secrets", "users", "add", "user1", age_keys[0].pubkey])
|
||||||
cli.run(["secrets", "generate", "vm1"])
|
cli.run(["secrets", "generate", "vm1"])
|
||||||
has_secret("vm1-age.key")
|
has_secret("vm1-age.key")
|
||||||
has_secret("vm1-secret1")
|
has_secret("vm1-zerotier-identity-secret")
|
||||||
fact1 = machine_get_fact("vm1", "fact1")
|
network_id = machine_get_fact("vm1", "zerotier-network-id")
|
||||||
assert fact1 == "fact1\n"
|
assert len(network_id) == 16
|
||||||
age_key = sops_secrets_folder().joinpath("vm1-age.key").joinpath("secret")
|
age_key = sops_secrets_folder().joinpath("vm1-age.key").joinpath("secret")
|
||||||
secret1 = sops_secrets_folder().joinpath("vm1-secret1").joinpath("secret")
|
identity_secret = (
|
||||||
|
sops_secrets_folder()
|
||||||
|
.joinpath("vm1-zerotier-identity-secret")
|
||||||
|
.joinpath("secret")
|
||||||
|
)
|
||||||
age_key_mtime = age_key.lstat().st_mtime_ns
|
age_key_mtime = age_key.lstat().st_mtime_ns
|
||||||
secret1_mtime = secret1.lstat().st_mtime_ns
|
secret1_mtime = identity_secret.lstat().st_mtime_ns
|
||||||
|
|
||||||
# test idempotency
|
# test idempotency
|
||||||
cli.run(["secrets", "generate", "vm1"])
|
cli.run(["secrets", "generate", "vm1"])
|
||||||
assert age_key.lstat().st_mtime_ns == age_key_mtime
|
assert age_key.lstat().st_mtime_ns == age_key_mtime
|
||||||
assert secret1.lstat().st_mtime_ns == secret1_mtime
|
assert identity_secret.lstat().st_mtime_ns == secret1_mtime
|
||||||
|
|
|
@ -1,6 +0,0 @@
|
||||||
from clan_cli.zerotier import create_network
|
|
||||||
|
|
||||||
|
|
||||||
def test_create_network() -> None:
|
|
||||||
network = create_network()
|
|
||||||
assert network["networkid"]
|
|
Loading…
Reference in New Issue
Block a user