clan-cli: add zerotier subcommand

This commit is contained in:
lassulus
2023-08-28 10:25:16 +02:00
parent 754bfee3fd
commit 5e2e95e9c7
3 changed files with 32 additions and 22 deletions

View File

@@ -4,7 +4,7 @@ import sys
from types import ModuleType from types import ModuleType
from typing import Optional from typing import Optional
from . import admin, config, machines, secrets, webui from . import admin, config, machines, secrets, webui, zerotier
from .errors import ClanError from .errors import ClanError
from .ssh import cli as ssh_cli from .ssh import cli as ssh_cli
@@ -41,6 +41,9 @@ 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)

View File

@@ -1,3 +1,4 @@
import argparse
import json import json
import socket import socket
import subprocess import subprocess
@@ -42,7 +43,8 @@ class ZerotierController:
def __init__(self, port: int, home: Path) -> None: def __init__(self, port: int, home: Path) -> None:
self.port = port self.port = port
self.home = home self.home = home
self.secret = (home / "authtoken.secret").read_text() self.authtoken = (home / "authtoken.secret").read_text()
self.secret = (home / "identity.secret").read_text()
def _http_request( def _http_request(
self, self,
@@ -56,7 +58,7 @@ class ZerotierController:
if data is not None: if data is not None:
body = json.dumps(data).encode("ascii") body = json.dumps(data).encode("ascii")
headers["Content-Type"] = "application/json" headers["Content-Type"] = "application/json"
headers["X-ZT1-AUTH"] = self.secret headers["X-ZT1-AUTH"] = self.authtoken
url = f"http://127.0.0.1:{self.port}{path}" url = f"http://127.0.0.1:{self.port}{path}"
req = urllib.request.Request(url, headers=headers, method=method, data=body) req = urllib.request.Request(url, headers=headers, method=method, data=body)
resp = urllib.request.urlopen(req) resp = urllib.request.urlopen(req)
@@ -75,11 +77,6 @@ class ZerotierController:
def get_network(self, id: str) -> dict[str, Any]: def get_network(self, id: str) -> dict[str, Any]:
return self._http_request(f"/controller/network/{id}") return self._http_request(f"/controller/network/{id}")
def update_network(self, id: str, new_config: dict[str, Any]) -> dict[str, Any]:
return self._http_request(
f"/controller/network/{id}", method="POST", data=new_config
)
@contextmanager @contextmanager
def zerotier_controller() -> Iterator[ZerotierController]: def zerotier_controller() -> Iterator[ZerotierController]:
@@ -117,6 +114,7 @@ def zerotier_controller() -> Iterator[ZerotierController]:
"/proc", "/proc",
"--dev", "--dev",
"/dev", "/dev",
"--unshare-user",
"--uid", "--uid",
"0", "0",
"--gid", "--gid",
@@ -151,19 +149,28 @@ def zerotier_controller() -> Iterator[ZerotierController]:
p.wait() p.wait()
class ZerotierNetwork:
def __init__(self, network_id: str) -> None:
self.network_id = network_id
# TODO: allow merging more network configuration here # TODO: allow merging more network configuration here
def create_network(private: bool = False) -> ZerotierNetwork: def create_network() -> dict:
with zerotier_controller() as controller: with zerotier_controller() as controller:
network = controller.create_network() network = controller.create_network()
network_id = network["nwid"] return {
network = controller.get_network(network_id) "secret": controller.secret,
network["private"] = private "networkid": network["nwid"],
network["v6AssignMode"]["rfc4193"] = True }
controller.update_network(network_id, network)
# TODO: persist home into sops?
return ZerotierNetwork(network_id) def main(args: argparse.Namespace) -> None:
zerotier = create_network()
outpath = Path(args.outpath)
outpath.mkdir(parents=True, exist_ok=True)
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:
parser.add_argument(
"--outpath", help="directory to put the secret file to", required=True
)
parser.set_defaults(func=main)

View File

@@ -3,4 +3,4 @@ from clan_cli.zerotier import create_network
def test_create_network() -> None: def test_create_network() -> None:
network = create_network() network = create_network()
assert network.network_id assert network["networkid"]