clanServices: Add role descriptions to all our services
This commit is contained in:
@@ -27,7 +27,9 @@
|
||||
modules.new-service = {
|
||||
_class = "clan.service";
|
||||
manifest.name = "new-service";
|
||||
roles.peer = { };
|
||||
roles.peer = {
|
||||
description = "A peer that uses the new-service to generate some files.";
|
||||
};
|
||||
perMachine = {
|
||||
nixosModule = {
|
||||
# This should be generated by:
|
||||
|
||||
@@ -34,7 +34,9 @@ nixosLib.runTest (
|
||||
modules.new-service = {
|
||||
_class = "clan.service";
|
||||
manifest.name = "new-service";
|
||||
roles.peer = { };
|
||||
roles.peer = {
|
||||
description = "A peer that uses the new-service to generate some files.";
|
||||
};
|
||||
perMachine = {
|
||||
nixosModule = {
|
||||
# This should be generated by:
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
{
|
||||
_class = "clan.service";
|
||||
manifest.name = "clan-core/admin";
|
||||
manifest.description = "Convenient Administration for the Clan App";
|
||||
manifest.description = "Adds a root user with ssh access";
|
||||
manifest.categories = [ "Utility" ];
|
||||
|
||||
roles.default = {
|
||||
description = "Placeholder role to apply the admin service";
|
||||
interface =
|
||||
{ lib, ... }:
|
||||
{
|
||||
|
||||
options = {
|
||||
allowedKeys = lib.mkOption {
|
||||
default = { };
|
||||
|
||||
@@ -9,7 +9,7 @@ inventory.instances = {
|
||||
};
|
||||
roles.client.machines."jon".settings = {
|
||||
destinations."storagebox" = {
|
||||
repo = "username@$hostname:/./borgbackup";
|
||||
repo = "username@hostname:/./borgbackup";
|
||||
rsh = ''ssh -oPort=23 -i /run/secrets/vars/borgbackup/borgbackup.ssh'';
|
||||
};
|
||||
};
|
||||
|
||||
@@ -62,7 +62,7 @@
|
||||
};
|
||||
|
||||
roles.client = {
|
||||
description = "A borgbackup client that backs up to one or more borgbackup servers.";
|
||||
description = "A borgbackup client that backs up to all borgbackup server roles.";
|
||||
interface =
|
||||
{
|
||||
lib,
|
||||
|
||||
@@ -2,12 +2,12 @@
|
||||
{
|
||||
_class = "clan.service";
|
||||
manifest.name = "certificates";
|
||||
manifest.description = "Sets up a certificates internal to your Clan";
|
||||
manifest.description = "Sets up a PKI certificate chain using step-ca";
|
||||
manifest.categories = [ "Network" ];
|
||||
manifest.readme = builtins.readFile ./README.md;
|
||||
|
||||
roles.ca = {
|
||||
|
||||
description = "A certificate authority that issues and signs certificates for other machines.";
|
||||
interface =
|
||||
{ lib, ... }:
|
||||
{
|
||||
@@ -184,6 +184,7 @@
|
||||
|
||||
# Empty role, so we can add non-ca machins to the instance to trust the CA
|
||||
roles.default = {
|
||||
description = "A machine that trusts the CA and can get certificates issued by it.";
|
||||
interface =
|
||||
{ lib, ... }:
|
||||
{
|
||||
|
||||
@@ -45,13 +45,15 @@ inventory = {
|
||||
# Add the default role to all machines, including `client`
|
||||
roles.default.tags.all = { };
|
||||
|
||||
# DNS server
|
||||
# DNS server queries to http://<name>.foo are resolved here
|
||||
roles.server.machines."dnsserver".settings = {
|
||||
ip = "192.168.1.2";
|
||||
tld = "foo";
|
||||
};
|
||||
|
||||
# First service
|
||||
# Registers http://one.foo will resolve to 192.168.1.3
|
||||
# underlying service runs on server01
|
||||
roles.default.machines."server01".settings = {
|
||||
ip = "192.168.1.3";
|
||||
services = [ "one" ];
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
manifest.readme = builtins.readFile ./README.md;
|
||||
|
||||
roles.server = {
|
||||
|
||||
description = "A DNS server that resolves services in the clan network.";
|
||||
interface =
|
||||
{ lib, ... }:
|
||||
{
|
||||
@@ -103,6 +103,7 @@
|
||||
};
|
||||
|
||||
roles.default = {
|
||||
description = "A machine that registers the 'server' role as resolver and registers services under the configured TLD in the resolver.";
|
||||
interface =
|
||||
{ lib, ... }:
|
||||
{
|
||||
|
||||
@@ -101,6 +101,7 @@ in
|
||||
manifest.readme = builtins.readFile ./README.md;
|
||||
|
||||
roles.admin = {
|
||||
description = "A data-mesher admin node that bootstraps the network and can sign new nodes into the network.";
|
||||
interface =
|
||||
{ lib, ... }:
|
||||
{
|
||||
@@ -177,6 +178,7 @@ in
|
||||
};
|
||||
|
||||
roles.signer = {
|
||||
description = "A data-mesher signer node that can sign new nodes into the network.";
|
||||
interface = sharedInterface;
|
||||
perInstance =
|
||||
{
|
||||
@@ -208,6 +210,7 @@ in
|
||||
};
|
||||
|
||||
roles.peer = {
|
||||
description = "A data-mesher peer node that connects to the network.";
|
||||
interface = sharedInterface;
|
||||
perInstance =
|
||||
{
|
||||
|
||||
@@ -2,11 +2,12 @@
|
||||
{
|
||||
_class = "clan.service";
|
||||
manifest.name = "clan-core/dyndns";
|
||||
manifest.description = "A dynamic DNS service to update domain IPs";
|
||||
manifest.description = "A dynamic DNS service to auto update domain IPs";
|
||||
manifest.categories = [ "Network" ];
|
||||
manifest.readme = builtins.readFile ./README.md;
|
||||
|
||||
roles.default = {
|
||||
description = "Placeholder role to apply the dyndns service";
|
||||
interface =
|
||||
{ lib, ... }:
|
||||
{
|
||||
|
||||
@@ -2,31 +2,34 @@
|
||||
{
|
||||
_class = "clan.service";
|
||||
manifest.name = "clan-core/emergency-access";
|
||||
manifest.description = "Set recovery password for emergency access to machine";
|
||||
manifest.description = "Set recovery password for emergency access to machine to debug boot issues";
|
||||
manifest.categories = [ "System" ];
|
||||
manifest.readme = builtins.readFile ./README.md;
|
||||
|
||||
roles.default.perInstance = {
|
||||
nixosModule =
|
||||
{ config, pkgs, ... }:
|
||||
{
|
||||
boot.initrd.systemd.emergencyAccess =
|
||||
config.clan.core.vars.generators.emergency-access.files.password-hash.value;
|
||||
roles.default = {
|
||||
description = "Placeholder role to apply the emergency-access service";
|
||||
perInstance = {
|
||||
nixosModule =
|
||||
{ config, pkgs, ... }:
|
||||
{
|
||||
boot.initrd.systemd.emergencyAccess =
|
||||
config.clan.core.vars.generators.emergency-access.files.password-hash.value;
|
||||
|
||||
clan.core.vars.generators.emergency-access = {
|
||||
runtimeInputs = [
|
||||
pkgs.coreutils
|
||||
pkgs.mkpasswd
|
||||
pkgs.xkcdpass
|
||||
];
|
||||
files.password.deploy = false;
|
||||
files.password-hash.secret = false;
|
||||
clan.core.vars.generators.emergency-access = {
|
||||
runtimeInputs = [
|
||||
pkgs.coreutils
|
||||
pkgs.mkpasswd
|
||||
pkgs.xkcdpass
|
||||
];
|
||||
files.password.deploy = false;
|
||||
files.password-hash.secret = false;
|
||||
|
||||
script = ''
|
||||
xkcdpass --numwords 4 --delimiter - --count 1 | tr -d "\n" > $out/password
|
||||
mkpasswd -s -m sha-512 < $out/password | tr -d "\n" > $out/password-hash
|
||||
'';
|
||||
script = ''
|
||||
xkcdpass --numwords 4 --delimiter - --count 1 | tr -d "\n" > $out/password
|
||||
mkpasswd -s -m sha-512 < $out/password | tr -d "\n" > $out/password-hash
|
||||
'';
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
manifest.categories = [ "System" ];
|
||||
|
||||
roles.default = {
|
||||
|
||||
description = "Placeholder role to apply the garage service";
|
||||
perInstance.nixosModule =
|
||||
{
|
||||
config,
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
# defined in this file directly (e.g. the "morning" role) or split up into a
|
||||
# separate file (e.g. the "evening" role)
|
||||
roles.morning = {
|
||||
description = "A morning greeting machine";
|
||||
interface =
|
||||
{ lib, ... }:
|
||||
{
|
||||
@@ -67,6 +68,7 @@
|
||||
# the interface here, so we can see all settings of the service in one place,
|
||||
# but you can also move it to the respective file
|
||||
roles.evening = {
|
||||
description = "An evening greeting machine";
|
||||
interface =
|
||||
{ lib, ... }:
|
||||
{
|
||||
|
||||
@@ -6,5 +6,7 @@
|
||||
manifest.categories = [ "Utility" ];
|
||||
manifest.readme = builtins.readFile ./README.md;
|
||||
|
||||
roles.default = { };
|
||||
roles.default = {
|
||||
description = "Placeholder role to apply the importer service";
|
||||
};
|
||||
}
|
||||
|
||||
@@ -2,12 +2,13 @@
|
||||
{
|
||||
_class = "clan.service";
|
||||
manifest.name = "clan-core/internet";
|
||||
manifest.description = "direct access (or via ssh jumphost) to machines";
|
||||
manifest.description = "Part of the clan networking abstraction to define how to reach machines from outside the clan network over the internet, if defined has the highest priority";
|
||||
manifest.categories = [
|
||||
"System"
|
||||
"Network"
|
||||
];
|
||||
roles.default = {
|
||||
description = "Placeholder role to apply the internet service";
|
||||
interface =
|
||||
{ lib, ... }:
|
||||
{
|
||||
|
||||
@@ -2,11 +2,12 @@
|
||||
{
|
||||
_class = "clan.service";
|
||||
manifest.name = "localbackup";
|
||||
manifest.description = "Automatically backups current machine to local directory.";
|
||||
manifest.description = "Automatically backups current machine to local directory or a mounted drive.";
|
||||
manifest.categories = [ "System" ];
|
||||
manifest.readme = builtins.readFile ./README.md;
|
||||
|
||||
roles.default = {
|
||||
description = "Placeholder role to apply the localbackup service";
|
||||
interface =
|
||||
{ lib, ... }:
|
||||
{
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
manifest.categories = [ "Social" ];
|
||||
|
||||
roles.default = {
|
||||
description = "Placeholder role to apply the matrix-synapse service";
|
||||
interface =
|
||||
{ lib, ... }:
|
||||
{
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
manifest.readme = builtins.readFile ./README.md;
|
||||
|
||||
roles.telegraf = {
|
||||
description = "Placeholder role to apply the telegraf monitoring agent";
|
||||
interface =
|
||||
{ lib, ... }:
|
||||
{
|
||||
|
||||
@@ -2,13 +2,14 @@
|
||||
{
|
||||
_class = "clan.service";
|
||||
manifest.name = "clan-core/mycelium";
|
||||
manifest.description = "End-2-end encrypted IPv6 overlay network";
|
||||
manifest.description = "End-2-end encrypted P2P IPv6 overlay network";
|
||||
manifest.categories = [
|
||||
"System"
|
||||
"Network"
|
||||
];
|
||||
|
||||
roles.peer = {
|
||||
description = "A peer in the mycelium network";
|
||||
interface =
|
||||
{ lib, ... }:
|
||||
{
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
];
|
||||
|
||||
roles.default = {
|
||||
description = "Placeholder role to apply the packages service";
|
||||
interface =
|
||||
{ lib, ... }:
|
||||
{
|
||||
|
||||
@@ -1,36 +1,91 @@
|
||||
The `sshd` Clan service manages SSH to make it easy to securely access your machines over the internet. The service uses `vars` to store the SSH host keys for each machine to ensure they remain stable across deployments.
|
||||
# Clan service: sshd
|
||||
What it does
|
||||
- Generates and persists SSH host keys via `vars`.
|
||||
- Optionally issues CA‑signed host certificates for servers.
|
||||
- Installs the `server` CA public key into `clients` `known_hosts` for TOFU‑less verification.
|
||||
|
||||
`sshd` also generates SSH certificates for both servers and clients allowing for certificate-based authentication for SSH.
|
||||
|
||||
The service also disables password-based authentication over SSH, to access your machines you'll need to use public key authentication or certificate-based authentication.
|
||||
When to use it
|
||||
- Zero‑TOFU SSH for dynamic fleets: admins/CI can connect to frequently rebuilt hosts (e.g., server-1.example.com) without prompts or per‑host `known_hosts` churn.
|
||||
|
||||
## Usage
|
||||
Roles
|
||||
- Server: runs sshd, presents a CA‑signed host certificate for `<machine>.<domain>`.
|
||||
- Client: trusts the CA for the given domains to verify servers’ certificates.
|
||||
Tip: assign both roles to a machine if it should both present a cert and verify others.
|
||||
|
||||
Quick start (with host certificates)
|
||||
Useful if you never want to get a prompt about trusting the ssh fingerprint.
|
||||
```nix
|
||||
{
|
||||
inventory.instances = {
|
||||
sshd-with-certs = {
|
||||
module = { name = "sshd"; input = "clan-core"; };
|
||||
# Servers present certificates for <machine>.example.com
|
||||
roles.server.tags.all = { };
|
||||
roles.server.settings = {
|
||||
certificate.searchDomains = [ "example.com" ];
|
||||
# Optional: also add RSA host keys
|
||||
# hostKeys.rsa.enable = true;
|
||||
};
|
||||
# Clients trust the CA for *.example.com
|
||||
roles.client.tags.all = { };
|
||||
roles.client.settings = {
|
||||
certificate.searchDomains = [ "example.com" ];
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
Basic: only add persistent host keys (ed25519), no certificates
|
||||
Useful if you want to get an ssh "trust this server" prompt once and then never again.
|
||||
```nix
|
||||
{
|
||||
inventory.instances = {
|
||||
# By default this service only generates ed25519 host keys
|
||||
sshd-basic = {
|
||||
module = {
|
||||
name = "sshd";
|
||||
input = "clan-core";
|
||||
};
|
||||
roles.server.tags.all = { };
|
||||
roles.client.tags.all = { };
|
||||
};
|
||||
|
||||
# Also generate RSA host keys for all servers
|
||||
sshd-with-rsa = {
|
||||
module = {
|
||||
name = "sshd";
|
||||
input = "clan-core";
|
||||
};
|
||||
roles.server.tags.all = { };
|
||||
roles.server.settings = {
|
||||
hostKeys.rsa.enable = true;
|
||||
};
|
||||
roles.client.tags.all = { };
|
||||
};
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
Example: selective trust per environment
|
||||
Admins should trust only production; CI should trust prod and staging. Servers are reachable under both domains.
|
||||
```nix
|
||||
{
|
||||
inventory.instances = {
|
||||
sshd-env-scoped = {
|
||||
module = { name = "sshd"; input = "clan-core"; };
|
||||
|
||||
# Servers present certs for both prod and staging FQDNs
|
||||
roles.server.tags.all = { };
|
||||
roles.server.settings = {
|
||||
certificate.searchDomains = [ "prod.example.com" "staging.example.com" ];
|
||||
};
|
||||
|
||||
# Admin laptop: trust prod only
|
||||
roles.client.machines."admin-laptop".settings = {
|
||||
certificate.searchDomains = [ "prod.example.com" ];
|
||||
};
|
||||
|
||||
# CI runner: trust prod and staging
|
||||
roles.client.machines."ci-runner-1".settings = {
|
||||
certificate.searchDomains = [ "prod.example.com" "staging.example.com" ];
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
```
|
||||
- Admin -> server1.prod.example.com: zero‑TOFU (verified via cert).
|
||||
- Admin -> server1.staging.example.com: falls back to TOFU (or is blocked by policy).
|
||||
- CI -> either prod or staging: zero‑TOFU for both.
|
||||
Note: server and client searchDomains don’t have to be identical; they only need to overlap for the hostnames you actually use.
|
||||
|
||||
Notes
|
||||
- Connect using a name that matches a cert principal (e.g., `server1.example.com`); wildcards are not allowed inside the certificate.
|
||||
- CA private key stays in `vars` (not deployed); only the CA public key is distributed.
|
||||
- Logins still require your user SSH keys on the server (passwords are disabled).
|
||||
@@ -10,13 +10,13 @@
|
||||
manifest.readme = builtins.readFile ./README.md;
|
||||
|
||||
roles.client = {
|
||||
description = "Installs the SSH CA public key into known_hosts for the configured domains, so this machine can verify servers’ host certificates without TOFU prompts.";
|
||||
interface =
|
||||
{ lib, ... }:
|
||||
{
|
||||
options.certificate = {
|
||||
searchDomains = lib.mkOption {
|
||||
type = lib.types.listOf lib.types.str;
|
||||
default = [ ];
|
||||
example = [ "mydomain.com" ];
|
||||
description = ''
|
||||
List of domains to include in the certificate.
|
||||
@@ -38,7 +38,6 @@
|
||||
...
|
||||
}:
|
||||
{
|
||||
|
||||
clan.core.vars.generators.openssh-ca = lib.mkIf (settings.certificate.searchDomains != [ ]) {
|
||||
share = true;
|
||||
files.id_ed25519.deploy = false;
|
||||
@@ -64,11 +63,12 @@
|
||||
};
|
||||
|
||||
roles.server = {
|
||||
description = "Runs sshd with persistent host keys and (if certificate.searchDomains is set) a CA‑signed host certificate for <machine>.<domain>, enabling TOFU‑less verification by clients that trust the CA.";
|
||||
interface =
|
||||
{ lib, ... }:
|
||||
{
|
||||
options = {
|
||||
hostKeys.rsa.enable = lib.mkEnableOption "Generate RSA host key";
|
||||
hostKeys.rsa.enable = lib.mkEnableOption "Also generates an RSA host key";
|
||||
|
||||
certificate = {
|
||||
searchDomains = lib.mkOption {
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
}
|
||||
```
|
||||
|
||||
Now the folder `~/syncthing/documents` will be shared with all your machines.
|
||||
Now the folder `~/syncthing/documents` will be shared and kept in sync with all your machines.
|
||||
|
||||
|
||||
## Documentation
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
manifest.readme = builtins.readFile ./README.md;
|
||||
|
||||
roles.peer = {
|
||||
description = "A peer in the syncthing cluster that syncs files with other peers.";
|
||||
interface =
|
||||
{ lib, ... }:
|
||||
{
|
||||
|
||||
@@ -2,13 +2,17 @@
|
||||
{
|
||||
_class = "clan.service";
|
||||
manifest.name = "clan-core/tor";
|
||||
manifest.description = "Onion routing, use Hidden services to connect your machines";
|
||||
manifest.description = "Part of the clan networking abstraction to define how to reach machines through the Tor network, if used has the lowest priority";
|
||||
manifest.categories = [
|
||||
"System"
|
||||
"Network"
|
||||
];
|
||||
|
||||
roles.client = {
|
||||
description = ''
|
||||
Enables a continuosly running Tor proxy on the machine, allowing access to other machines via the Tor network.
|
||||
If not enabled, a Tor proxy will be started automatically when required.
|
||||
'';
|
||||
perInstance =
|
||||
{
|
||||
...
|
||||
@@ -31,6 +35,7 @@
|
||||
};
|
||||
|
||||
roles.server = {
|
||||
description = "Sets up a Tor onion service for the machine, thus making it reachable over Tor.";
|
||||
# interface =
|
||||
# { lib, ... }:
|
||||
# {
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
manifest.readme = builtins.readFile ./README.md;
|
||||
|
||||
roles.default = {
|
||||
|
||||
description = "Placeholder role to apply the trusted-nix-caches service";
|
||||
perInstance =
|
||||
{ ... }:
|
||||
{
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
manifest.readme = builtins.readFile ./README.md;
|
||||
|
||||
roles.default = {
|
||||
description = "Placeholder role to apply the user service";
|
||||
interface =
|
||||
{ lib, ... }:
|
||||
{
|
||||
|
||||
21
clanServices/wifi/README.md
Normal file
21
clanServices/wifi/README.md
Normal file
@@ -0,0 +1,21 @@
|
||||
This module allows you to pre-configure WiFi networks for automatic connection.
|
||||
Each attribute in `settings.network` serves as an internal identifier, not the actual SSID.
|
||||
After defining your networks, you will be prompted for the SSID and password for each one.
|
||||
|
||||
This module leverages NetworkManager for managing connections.
|
||||
|
||||
```nix
|
||||
instances = {
|
||||
wifi = {
|
||||
module.name = "wifi";
|
||||
module.input = "clan-core";
|
||||
|
||||
roles.default = {
|
||||
machines."jon" = {
|
||||
settings.networks.home = { };
|
||||
settings.networks.work = { keyMgmt = "wpa-eap"; };
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
```
|
||||
@@ -9,8 +9,11 @@ in
|
||||
{
|
||||
_class = "clan.service";
|
||||
manifest.name = "wifi";
|
||||
manifest.description = "Pre configure wifi networks to connect to";
|
||||
manifest.readme = builtins.readFile ./README.md;
|
||||
|
||||
roles.default = {
|
||||
description = "Placeholder role to apply the wifi service";
|
||||
interface = {
|
||||
options.networks = lib.mkOption {
|
||||
type = lib.types.attrsOf (
|
||||
@@ -42,7 +45,18 @@ in
|
||||
)
|
||||
);
|
||||
default = { };
|
||||
description = "Wifi networks to predefine";
|
||||
example = {
|
||||
home = { };
|
||||
guest = {
|
||||
autoConnect = false;
|
||||
keyMgmt = "wpa-eap";
|
||||
};
|
||||
};
|
||||
description = ''
|
||||
List of wifi networks to configure for connection.
|
||||
Each attribute name is an internal identifier (not the SSID).
|
||||
For each network, you will be prompted to enter the SSID and password as secrets.
|
||||
'';
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -146,6 +146,7 @@ in
|
||||
|
||||
# Peer options and configuration
|
||||
roles.peer = {
|
||||
description = "A peer that connects to one or more controllers.";
|
||||
interface =
|
||||
{ lib, ... }:
|
||||
{
|
||||
@@ -261,6 +262,7 @@ in
|
||||
|
||||
# Controller options and configuration
|
||||
roles.controller = {
|
||||
description = "A controller that routes peer traffic. Must be publicly reachable.";
|
||||
interface =
|
||||
{ lib, ... }:
|
||||
{
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
manifest.description = "Yggdrasil encrypted IPv6 routing overlay network";
|
||||
|
||||
roles.default = {
|
||||
description = "Placeholder role to apply the yggdrasil service";
|
||||
interface =
|
||||
{ lib, ... }:
|
||||
{
|
||||
|
||||
@@ -2,11 +2,12 @@
|
||||
{
|
||||
_class = "clan.service";
|
||||
manifest.name = "clan-core/zerotier";
|
||||
manifest.description = "Configuration of the secure and efficient Zerotier VPN";
|
||||
manifest.description = "Zerotier Mesh VPN Service for secure P2P networking between machines";
|
||||
manifest.categories = [ "Utility" ];
|
||||
manifest.readme = builtins.readFile ./README.md;
|
||||
|
||||
roles.peer = {
|
||||
description = "A peer that connects to your private Zerotier network.";
|
||||
perInstance =
|
||||
{
|
||||
instanceName,
|
||||
@@ -51,6 +52,7 @@
|
||||
};
|
||||
|
||||
roles.moon = {
|
||||
description = "A moon acts as a relay node to connect other nodes in the zerotier network that are not publicly reachable. Each moon must be publicly reachable.";
|
||||
interface =
|
||||
{ lib, ... }:
|
||||
{
|
||||
@@ -101,6 +103,7 @@
|
||||
};
|
||||
|
||||
roles.controller = {
|
||||
description = "Manages network membership and is responsible for admitting new peers to your Zerotier network.";
|
||||
interface =
|
||||
{ lib, ... }:
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user