add zerotier-members script

This commit is contained in:
Jörg Thalheim 2023-09-29 10:51:38 +02:00
parent 36fc57c0be
commit 5f15ec4495
4 changed files with 100 additions and 0 deletions

View File

@ -99,6 +99,8 @@ in
${pkgs.python3.interpreter} ${./generate-network.py} "$facts/zerotier-network-id" "$secrets/zerotier-identity-secret"
'';
};
environment.etc."zerotier/network-id".text = facts.zerotier-network-id.value;
environment.systemPackages = [ config.clanCore.clanPkgs.zerotier-members ];
})
(lib.mkIf ((config.clanCore.secrets ? zerotier) && (facts.zerotier-network-id.value != null)) {
clan.networking.zerotier.networkId = facts.zerotier-network-id.value;
@ -109,6 +111,11 @@ in
ln -sfT ${pkgs.writeText "net.json" (builtins.toJSON networkConfig)} /var/lib/zerotier-one/controller.d/network/${cfg.networkId}.json
''}"
];
systemd.services.zerotierone.serviceConfig.ExecStartPost = [
"+${pkgs.writeShellScript "whitelist-controller" ''
${config.clanCore.clanPkgs.zerotier-members}/bin/zerotier-members allow ${builtins.substring 0 10 cfg.networkId}
''}"
];
})
];
}

View File

@ -8,6 +8,7 @@
perSystem = { pkgs, config, ... }: {
packages = {
tea-create-pr = pkgs.callPackage ./tea-create-pr { };
zerotier-members = pkgs.callPackage ./zerotier-members { };
merge-after-ci = pkgs.callPackage ./merge-after-ci {
inherit (config.packages) tea-create-pr;
};

View File

@ -0,0 +1,14 @@
{ stdenv, python3, lib }:
stdenv.mkDerivation {
name = "zerotier-members";
src = ./.;
buildInputs = [ python3 ];
installPhase = ''
install -Dm755 ${./zerotier-members.py} $out/bin/zerotier-members
'';
meta = with lib; {
description = "A tool to list/allow members of a ZeroTier network";
license = licenses.mit;
};
}

View File

@ -0,0 +1,78 @@
#!/usr/bin/env python
import argparse
import http.client
import json
import sys
from pathlib import Path
ZEROTIER_STATE_DIR = Path("/var/lib/zerotier-one")
class ClanError(Exception):
pass
# this is managed by the nixos module
def get_network_id() -> str:
p = Path("/etc/zerotier/network-id")
if not p.exists():
raise ClanError(
f"{p} file not found. Have you enabled the zerotier controller on this host?"
)
return p.read_text().strip()
def allow_member(args: argparse.Namespace) -> None:
member_id = args.member_id
network_id = get_network_id()
token = ZEROTIER_STATE_DIR.joinpath("authtoken.secret").read_text()
conn = http.client.HTTPConnection("localhost", 9993)
conn.request(
"POST",
f"/controller/network/{network_id}/member/{member_id}",
'{"authorized": true}',
{"X-ZT1-AUTH": token},
)
resp = conn.getresponse()
if resp.status != 200:
raise ClanError(
f"the zerotier daemon returned this error: {resp.status} {resp.reason}"
)
print(resp.status, resp.reason)
def list_members(args: argparse.Namespace) -> None:
network_id = get_network_id()
networks = ZEROTIER_STATE_DIR / "controller.d" / "network" / network_id / "member"
if not networks.exists():
return
for member in networks.iterdir():
with member.open() as f:
data = json.load(f)
try:
member_id = data["id"]
except KeyError:
raise ClanError(f"error: {member} does not contain an id")
print(member_id)
def main() -> None:
parser = argparse.ArgumentParser()
subparser = parser.add_subparsers(dest="command")
parser_allow = subparser.add_parser("allow", help="Allow a member to join")
parser_allow.add_argument("member_id")
parser_allow.set_defaults(func=allow_member)
parser_list = subparser.add_parser("list", help="List members")
parser_list.set_defaults(func=list_members)
args = parser.parse_args()
try:
args.func(args)
except ClanError as e:
print(e)
sys.exit(1)
if __name__ == "__main__":
main()