migrate clanmodules/{user,root}-password to clanServices/users
Move the functionality of both modules into a new clanService. root-password was previously just a special case of user-password. This migrates it into a deduplicated clan service and adds checks
This commit is contained in:
@@ -1,9 +1,11 @@
|
||||
---
|
||||
description = "Automatically generates and configures a password for the root user."
|
||||
categories = ["System"]
|
||||
features = [ "inventory" ]
|
||||
features = ["inventory", "deprecated"]
|
||||
---
|
||||
|
||||
This module is deprecated and will be removed in a future release. It's functionality has been replaced by the user-password service.
|
||||
|
||||
After the system was installed/deployed the following command can be used to display the root-password:
|
||||
|
||||
```bash
|
||||
|
||||
@@ -36,7 +36,7 @@
|
||||
if [[ -n "''${prompt_value-}" ]]; then
|
||||
echo "$prompt_value" | tr -d "\n" > "$out"/password
|
||||
else
|
||||
xkcdpass --numwords 3 --delimiter - --count 1 | tr -d "\n" > "$out"/password
|
||||
xkcdpass --numwords 4 --delimiter - --count 1 | tr -d "\n" > "$out"/password
|
||||
fi
|
||||
mkpasswd -s -m sha-512 < "$out"/password | tr -d "\n" > "$out"/password-hash
|
||||
'';
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
---
|
||||
description = "Automatically generates and configures a password for the specified user account."
|
||||
categories = ["System"]
|
||||
features = ["inventory"]
|
||||
features = ["inventory", "deprecated"]
|
||||
---
|
||||
|
||||
If setting the option prompt to true, the user will be prompted to type in their desired password.
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
{
|
||||
_class,
|
||||
pkgs,
|
||||
config,
|
||||
lib,
|
||||
@@ -34,13 +33,8 @@ in
|
||||
};
|
||||
|
||||
clan.core.vars.generators.user-password = {
|
||||
files.user-password-hash =
|
||||
{
|
||||
neededFor = "users";
|
||||
}
|
||||
// (lib.optionalAttrs (_class == "nixos") {
|
||||
restartUnits = lib.optional (config.services.userborn.enable) "userborn.service";
|
||||
});
|
||||
files.user-password-hash.neededFor = "users";
|
||||
files.user-password-hash.restartUnits = lib.optional (config.services.userborn.enable) "userborn.service";
|
||||
|
||||
prompts.user-password.type = "hidden";
|
||||
prompts.user-password.persist = true;
|
||||
@@ -58,7 +52,7 @@ in
|
||||
if [[ -n "''${prompt_value-}" ]]; then
|
||||
echo "$prompt_value" | tr -d "\n" > "$out"/user-password
|
||||
else
|
||||
xkcdpass --numwords 3 --delimiter - --count 1 | tr -d "\n" > "$out"/user-password
|
||||
xkcdpass --numwords 4 --delimiter - --count 1 | tr -d "\n" > "$out"/user-password
|
||||
fi
|
||||
mkpasswd -s -m sha-512 < "$out"/user-password | tr -d "\n" > "$out"/user-password-hash
|
||||
'';
|
||||
|
||||
83
clanServices/users/default.nix
Normal file
83
clanServices/users/default.nix
Normal file
@@ -0,0 +1,83 @@
|
||||
{ ... }:
|
||||
{
|
||||
_class = "clan.service";
|
||||
manifest.name = "clan-core/users";
|
||||
manifest.description = "Automatically generates and configures a password for the specified user account.";
|
||||
manifest.categories = [ "System" ];
|
||||
|
||||
roles.default = {
|
||||
interface =
|
||||
{ lib, ... }:
|
||||
{
|
||||
options = {
|
||||
user = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
example = "alice";
|
||||
description = "The user the password should be generated for.";
|
||||
};
|
||||
prompt = lib.mkOption {
|
||||
type = lib.types.bool;
|
||||
default = true;
|
||||
example = false;
|
||||
description = "Whether the user should be prompted.";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
perInstance =
|
||||
{ settings, ... }:
|
||||
{
|
||||
nixosModule =
|
||||
{
|
||||
config,
|
||||
pkgs,
|
||||
lib,
|
||||
...
|
||||
}:
|
||||
{
|
||||
users.mutableUsers = false;
|
||||
users.users.${settings.user}.hashedPasswordFile =
|
||||
config.clan.core.vars.generators."user-password-${settings.user}".files.user-password-hash.path;
|
||||
|
||||
clan.core.vars.generators."user-password-${settings.user}" = {
|
||||
|
||||
# files.user-password-hash.neededFor = "users";
|
||||
files.user-password-hash.restartUnits = lib.optional (config.services.userborn.enable) "userborn.service";
|
||||
files.user-password.deploy = false;
|
||||
|
||||
prompts.user-password = lib.mkIf settings.prompt {
|
||||
type = "hidden";
|
||||
persist = true;
|
||||
description = "You can autogenerate a password, if you leave this prompt blank.";
|
||||
};
|
||||
|
||||
runtimeInputs = [
|
||||
pkgs.coreutils
|
||||
pkgs.xkcdpass
|
||||
pkgs.mkpasswd
|
||||
];
|
||||
|
||||
script =
|
||||
(
|
||||
if settings.prompt then
|
||||
''
|
||||
prompt_value=$(cat "$prompts"/user-password)
|
||||
if [[ -n "''${prompt_value-}" ]]; then
|
||||
echo "$prompt_value" | tr -d "\n" > "$out"/user-password
|
||||
else
|
||||
xkcdpass --numwords 4 --delimiter - --count 1 | tr -d "\n" > "$out"/user-password
|
||||
fi
|
||||
''
|
||||
else
|
||||
''
|
||||
xkcdpass --numwords 4 --delimiter - --count 1 | tr -d "\n" > "$out"/user-password
|
||||
''
|
||||
)
|
||||
+ ''
|
||||
mkpasswd -s -m sha-512 < "$out"/user-password | tr -d "\n" > "$out"/user-password-hash
|
||||
'';
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
18
clanServices/users/flake-module.nix
Normal file
18
clanServices/users/flake-module.nix
Normal file
@@ -0,0 +1,18 @@
|
||||
{ lib, self, ... }:
|
||||
{
|
||||
clan.modules = {
|
||||
users = lib.modules.importApply ./default.nix { };
|
||||
};
|
||||
perSystem =
|
||||
{ pkgs, ... }:
|
||||
{
|
||||
checks = lib.optionalAttrs (pkgs.stdenv.isLinux) {
|
||||
users = import ./tests/vm/default.nix {
|
||||
inherit pkgs;
|
||||
clan-core = self;
|
||||
nixosLib = import (self.inputs.nixpkgs + "/nixos/lib") { };
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
}
|
||||
67
clanServices/users/tests/vm/default.nix
Normal file
67
clanServices/users/tests/vm/default.nix
Normal file
@@ -0,0 +1,67 @@
|
||||
{
|
||||
pkgs,
|
||||
nixosLib,
|
||||
clan-core,
|
||||
...
|
||||
}:
|
||||
|
||||
nixosLib.runTest (
|
||||
{ ... }:
|
||||
{
|
||||
imports = [
|
||||
clan-core.modules.nixosVmTest.clanTest
|
||||
];
|
||||
|
||||
hostPkgs = pkgs;
|
||||
|
||||
name = "users";
|
||||
|
||||
clan = {
|
||||
directory = ./.;
|
||||
modules."@clan/users" = ../../default.nix;
|
||||
inventory = {
|
||||
machines.server = { };
|
||||
|
||||
instances = {
|
||||
root-password-test = {
|
||||
module.name = "@clan/users";
|
||||
roles.default.machines."server".settings = {
|
||||
user = "root";
|
||||
prompt = false;
|
||||
};
|
||||
};
|
||||
user-password-test = {
|
||||
module.name = "@clan/users";
|
||||
roles.default.machines."server".settings = {
|
||||
user = "testuser";
|
||||
prompt = false;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
nodes = {
|
||||
server = {
|
||||
users.users.testuser.group = "testuser";
|
||||
users.groups.testuser = { };
|
||||
users.users.testuser.isNormalUser = true;
|
||||
};
|
||||
};
|
||||
|
||||
testScript = ''
|
||||
start_all()
|
||||
|
||||
server.wait_for_unit("multi-user.target")
|
||||
|
||||
# Check that the testuser account exists
|
||||
server.succeed("id testuser")
|
||||
|
||||
# Try to log in as the user using the generated password
|
||||
# TODO: fix
|
||||
# password = server.succeed("cat /run/clan/vars/user-password/user-password").strip()
|
||||
# server.succeed(f"echo '{password}' | su - testuser -c 'echo Login successful'")
|
||||
|
||||
'';
|
||||
}
|
||||
)
|
||||
6
clanServices/users/tests/vm/sops/machines/server/key.json
Executable file
6
clanServices/users/tests/vm/sops/machines/server/key.json
Executable file
@@ -0,0 +1,6 @@
|
||||
[
|
||||
{
|
||||
"publickey": "age1qkxvpqjp4tevcm2wpq3hea3563s2nhcsfe8vdy6pztegzjmnfa4qljmrjm",
|
||||
"type": "age"
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"data": "ENC[AES256_GCM,data:01pW9z9mKMQsUTK2rT8ccwu7gO2Yvi8Jhyi/qRzrfL86Z1MiTk1j6fDp6wGxRC69J7m9PDxo7Hu9ce7lV3+6YFMtfojXv+kCjvY=,iv:A79Xk0n41qdCpnu0YRD5rdyBfG0iDn+X/9tcbcZzRws=,tag:WA8hmNYU78StBokCJZQClA==,type:str]",
|
||||
"sops": {
|
||||
"age": [
|
||||
{
|
||||
"recipient": "age1qm0p4vf9jvcnn43s6l4prk8zn6cx0ep9gzvevxecv729xz540v8qa742eg",
|
||||
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBHeUg0a2pRcDNLbVN1dzZ0\neTM3bEJHVXpLd2p2cVBTUDZoWURlVGNFTmxnCmhPTDRGY1NtRDMxdmd3YlQvQTFa\nd0pVc0Y4T1ZtR1RmK05oVnFGRHFQd1UKLS0tIEVDYjBnemN4UlU2Y3lKbWpDYnFp\nb1NnMWY1RzRUMStiYnpuYUxsaWx1Z2sKqdARj14dDQPBejEwTZil+ZamvGCLc9X9\neXVWDOevDZEhh5RR3cNIMu/9wm+HyndY0Rbvfb+HrB3WmuL64Tx4/g==\n-----END AGE ENCRYPTED FILE-----\n"
|
||||
}
|
||||
],
|
||||
"lastmodified": "2025-06-11T15:51:37Z",
|
||||
"mac": "ENC[AES256_GCM,data:8AfKCzuW1c9xrn+k64XiX6mperaHZTI989WqQyInSB6xENNWIiOVpLCb9UtLOqvAie3dEQ9J8batiLygdJRK8GqBQj+Zch2Ep0DARLGwL6I6SlqpcWEYnorYMlmNoHnMj/5kr6QIhPJkbXdgTrhyYO4QWtn4d+z3tOgF8E1BW2M=,iv:ghBgDOpbn/k6sfGyiWPKWjhb19RTy1ohi15sw2JLhvM=,tag:z9Nq/hOCPbEdzLLw5kQVnQ==,type:str]",
|
||||
"unencrypted_suffix": "_unencrypted",
|
||||
"version": "3.10.2"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
../../../users/admin
|
||||
4
clanServices/users/tests/vm/sops/users/admin/key.json
Normal file
4
clanServices/users/tests/vm/sops/users/admin/key.json
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"publickey": "age1qm0p4vf9jvcnn43s6l4prk8zn6cx0ep9gzvevxecv729xz540v8qa742eg",
|
||||
"type": "age"
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
../../../../../../sops/machines/server
|
||||
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"data": "ENC[AES256_GCM,data:WCfszumboEpj8psAL4/630uxTuStQ7trUoj7FQ2CcceVfL4SKTt0nVT8LmWLMZnZpX6OB5DUKxPHjNqpPLEa6YhXKS97T79Dr7hEB/iOChduVWVzgU2cSXXrjbcrp5cmDc/5jtk0E6KSmw==,iv:5DX62dwMGypwkl6uPs8QOOZ4SKhAe9E0iFPDkyAqCFg=,tag:nepp8PU/D2uJBlOE18C+2A==,type:str]",
|
||||
"sops": {
|
||||
"age": [
|
||||
{
|
||||
"recipient": "age1qkxvpqjp4tevcm2wpq3hea3563s2nhcsfe8vdy6pztegzjmnfa4qljmrjm",
|
||||
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSA2eTl2VGVwZU5SczdRSk9S\nMTNzNGJ3WUE5TnhmdjNXclU5NnJVdEhBWWtFCnZTRlduVThOODdVeFgwLzBxQkJa\nSS9pczlhYm9vendoZ2xJbHJwaGQ1TUEKLS0tIHlYbkN5ZGJIbHAzM1NTQmNiZjJK\nMXRMY05yZTZ4cXI4Sk1xZHo5anBaNmcKnm+/1QW23Uyqvo1iTAZfRzHNJCfKFPSL\njmUiNkVMaUXpp1dtYrOwo/LDIQXhIVoRv7blcPRCe4wUAhJRp4DUVQ==\n-----END AGE ENCRYPTED FILE-----\n"
|
||||
},
|
||||
{
|
||||
"recipient": "age1qm0p4vf9jvcnn43s6l4prk8zn6cx0ep9gzvevxecv729xz540v8qa742eg",
|
||||
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBBUzU4bld6TjN4RzdjV0Mr\nMkw0Q3dxUER0NElXOU1wT21LbW5NQUY5Q3pJCjBCa0JLZDNQVFdRd1gyTHJ5cHlB\nVzdsNnNpU0xJTHpEM2NYemkyRFh6L2sKLS0tIE5SSHY2dFAzeGVndVRtZmNqYzJ6\nSDNTQkRoZE52b3k0OW1ZU0RocGRobWMK3VEaZCWECR6cQDvCo/UxZlRrM4ugpeA8\n2Req5+01wValB0Xlwady/hbQRL3Ag9t3IfemHfn7ZUc4TUVEtOoEcQ==\n-----END AGE ENCRYPTED FILE-----\n"
|
||||
}
|
||||
],
|
||||
"lastmodified": "2025-06-11T15:51:40Z",
|
||||
"mac": "ENC[AES256_GCM,data:KMVBXRd6kLfq4Ur751dpdIiaWOhxo0RvKznQ+8q5m+rC+3z0CLOuuR3hnx/dsdSdfZYU7FRXSJvIO4CXBZNanamLmcD58tECihRmq2c8iSuHvKnYJvcZOBVoqmSpE8wr84xYZOMEXr8BeD1d+PK5+j18vzx9JaySlqD9m/ixGig=,iv:LUHc0fa5WLlMkOqZJ9tLa1xXOrhiZTf55lpvpaSbeQA=,tag:k4Wm5JiYc0r45GdgMSYYlg==,type:str]",
|
||||
"unencrypted_suffix": "_unencrypted",
|
||||
"version": "3.10.2"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
../../../../../../sops/users/admin
|
||||
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"data": "ENC[AES256_GCM,data:FjZKJ5DoOqVleFckTUXVf9wFVCi9,iv:190oDlNNmlUinxyLu3z0KjWGzi7b0+3KFsAiVZxt9vY=,tag:pS4wWccJnCGGC1yqWKfnyw==,type:str]",
|
||||
"sops": {
|
||||
"age": [
|
||||
{
|
||||
"recipient": "age1qm0p4vf9jvcnn43s6l4prk8zn6cx0ep9gzvevxecv729xz540v8qa742eg",
|
||||
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBKYmowcHlsVkVHOU9DQzB3\nbEQ3cytHcmVkVmJBOS9sLzFwa0tzcUZ0WGtvCk10MjU5dDJOKzRPQXVPTkpNNUs0\nSHpKT05CZnB1d285VGlLcUthY3U3ck0KLS0tIEJtdXFzTmE1NExoUExieG5JZE05\nUW5JK2dWbjBuMWxjU2lWbE1tOE5TelUKPMG/xE4uB7pLya4mB/3cwwO7nuUPDDJ2\nRE9stD6wUjvTeJ5EORfyfD8HfjonWjr8El/plmjfGWuhSNm4+aBrTg==\n-----END AGE ENCRYPTED FILE-----\n"
|
||||
}
|
||||
],
|
||||
"lastmodified": "2025-06-11T15:51:40Z",
|
||||
"mac": "ENC[AES256_GCM,data:YfadVhFo+DohhBc1a1lKY+SJowybCcVGtI28KAgh2pkslUTGjH1a3tHIXEQD8Obsy1v9V83m1XWhqFkxbFQ+aW/niGFq7Qk+Vx6GTbiBEGq6zd/VqzIZwTXVgdXlqSEvutIXOqEBjXa+raJuLGZs7hc/ORDjO4IqWdwvGBxjmgQ=,iv:CSIdSnjAg1iXp3M3DZ/gAAqyFzgj9CziHe4VwaD4Mu4=,tag:X/BWo7vNKN4h1/kxUCffJA==,type:str]",
|
||||
"unencrypted_suffix": "_unencrypted",
|
||||
"version": "3.10.2"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
../../../../../../sops/users/admin
|
||||
@@ -0,0 +1 @@
|
||||
../../../../../../sops/machines/server
|
||||
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"data": "ENC[AES256_GCM,data:7faAUnNk4mFwRx29l3xdeAvEMmbaqghe7yx7sMByxaOsoKxf4oQDlY7cNR/4z9/Ga9onZ2QS2fwU3JkDLFJvJPr4hGzcu1eNhl8QOzNBRKM6j7rk902gwvHi30M4Dh1fGIoqfLte2HfdjA==,iv:qFvSXoMoVqbJUHW8REM+dzqwpQkCkQoiuxJzhKT78cU=,tag:FdWs17KTkGEIVQrgbNWIDQ==,type:str]",
|
||||
"sops": {
|
||||
"age": [
|
||||
{
|
||||
"recipient": "age1qkxvpqjp4tevcm2wpq3hea3563s2nhcsfe8vdy6pztegzjmnfa4qljmrjm",
|
||||
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBZeGd4b1NucWJkSHN0WVRY\nWmRIeWJ1V1N1RVNiQzdKRFN0NHZjNUdTRFhNCmhTV3p2TXkxeUNVdm5ucHB2a3lp\nMHpVcDNKTzlKcnQ1MlNUME5pQ2FEdnMKLS0tIFZkT014YmQ4aVV4Z2FhUkMwRWgv\nY3JuSTdWV1pvTFZpbDhoSWE5Wld3L1kKEehLVyPqQw0nW/z8boU1dOKLRZADmrXy\n9ilBtqwTxCTw5yx1CubnITQyratqLkki6JTJ9s91dLb0B8t6pvaRQQ==\n-----END AGE ENCRYPTED FILE-----\n"
|
||||
},
|
||||
{
|
||||
"recipient": "age1qm0p4vf9jvcnn43s6l4prk8zn6cx0ep9gzvevxecv729xz540v8qa742eg",
|
||||
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBlQ3NsMW05L0VmUEQvdCtZ\nT3c4bk9wV25QN0VZbVZtZkxvaUVlWDFUZGdjCk5GVXpQbDRrWkwzS1NzREFQVUxq\nSXNESGY4NUZ1Wjd6U3FFSnM0eDNzZG8KLS0tIEdUMEJLdWV5NjREVkNTM2hReHdx\nMHF3bUxIQWF4STM4UVBhSWxMQ0NFVEEKuakIYSudIr/UzYlgpulfaR9DRUo+bZsg\nrkbT4xyFEdL5P7zAeOlW2kZDOtKTT+PJxrU5OPGakpbHaE1RdRbv1Q==\n-----END AGE ENCRYPTED FILE-----\n"
|
||||
}
|
||||
],
|
||||
"lastmodified": "2025-06-11T15:51:43Z",
|
||||
"mac": "ENC[AES256_GCM,data:d2b3B0mVdt6xrEw6ipNotIKCaDs6mpB82Xsr0pCRyMI/l4lde+IWc+BuirpVuC8LiXT3UBmJ8oWP1MXgpzunVY81QITxdx9RbydeJSx95slDOqXiwjZ8YPgEVmxXZJRHU5SuXYGB+/aDJRcW12fsmjpmIyXrGPE0at5zcurpsoI=,iv:ywTkbrsN2df/C59O1TU9WjBqjLMorZNHqHLR7hU2ono=,tag:XZjrxAfk25vAXDxp7oDWUQ==,type:str]",
|
||||
"unencrypted_suffix": "_unencrypted",
|
||||
"version": "3.10.2"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
../../../../../../sops/users/admin
|
||||
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"data": "ENC[AES256_GCM,data:VjgSlcBS7KVkVSIo7aU3MHMu95vB0A==,iv:vooZAcgif2CMsbYx8QnE20R1pgiTa/jPp23+Exwyt/8=,tag:WsU2L6Ipbu51HpTJE6dvTg==,type:str]",
|
||||
"sops": {
|
||||
"age": [
|
||||
{
|
||||
"recipient": "age1qm0p4vf9jvcnn43s6l4prk8zn6cx0ep9gzvevxecv729xz540v8qa742eg",
|
||||
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBySUFNeVBlWGtmdUpQUWNL\ndURGZWcwRWNIV0NTRVVMR013R2dHUVRlbjA4Ck10WWticVZHbmQwUm50TEp1RzA4\nL3RGbDU3SXBJUlhkWEQxb1U2cXUzTkEKLS0tIHB1L0p4T1ZHd0RxSTN0Mjl2SjBl\nU1plT2NteVdUUU1LeUkrVWt4Q0tHZW8KZMQJ/7eAevNUsQAxgs0CdJ/3KXikinaw\nFrfiV3Nk/XeezyU7Gyh2dRkdX/KCpdSMniFL/pyeiZEqW+ALSeI3xA==\n-----END AGE ENCRYPTED FILE-----\n"
|
||||
}
|
||||
],
|
||||
"lastmodified": "2025-06-11T15:51:43Z",
|
||||
"mac": "ENC[AES256_GCM,data:wEi6vOpe5X/wO8QaAsbdYfQCQwcZmUhj9jgKuObBC2PTLVWWCYf8CZ6wSgt3/Ho/5TxoCKe1hoIqt+RE52ZKHFRwA0W3zfvkCpREHf2SOfq3NHxEn4/IDvQXRAwAvfgwc58u5nHhjpU7OpSNWLyKsuY+HR/kFRUO6nQVyAEHLUY=,iv:IXeMbEDc6033t1a/q1afwSblrmw1vK0iS8XL6mSOZZM=,tag:sB25pUTGBYm/vOtBp2eAWA==,type:str]",
|
||||
"unencrypted_suffix": "_unencrypted",
|
||||
"version": "3.10.2"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
../../../../../../sops/users/admin
|
||||
@@ -93,6 +93,7 @@ nav:
|
||||
- reference/clanServices/localsend.md
|
||||
- reference/clanServices/mycelium.md
|
||||
- reference/clanServices/sshd.md
|
||||
- reference/clanServices/users.md
|
||||
- reference/clanServices/hello-world.md
|
||||
- reference/clanServices/wifi.md
|
||||
- reference/clanServices/zerotier.md
|
||||
|
||||
Reference in New Issue
Block a user