def _generate_cert(name: str, org: str, duration: int, size: int, show: bool, get: bool): """Generate SSL certificate keypair.""" from hyperglass_agent.cli.actions import write_cert, find_app_path if get: app_path = find_app_path() cert_path = app_path / "agent_cert.pem" if not cert_path.exists(): warning("Certificate & key files have not been generated.") do_gen = confirm( style("Would you like to generate them now?", **WARNING)) if not do_gen: error("Certificate & key files do not yet exist.") else: write_cert(name=name, org=org, duration=duration, starttime=CERT_START, size=size, show=show) else: with cert_path.open("r") as f: cert = f.read() label(f"Public Key:\n\n{cert}") else: write_cert(name=name, org=org, duration=duration, starttime=CERT_START, size=size, show=show)
def start_web_server() -> None: """Start web server.""" find_app_path() try: from hyperglass_agent.config import params from hyperglass_agent.api.web import start msg_start = "Starting hyperglass agent web server on" msg_uri = "http://" msg_host = str(params.listen_address) msg_port = str(params.port) msg_len = len("".join( [msg_start, WS[1], msg_uri, msg_host, CL[1], msg_port])) echo(NL[1] + WS[msg_len + 8] + E.ROCKET + NL[1] + E.CHECK + style(msg_start, fg="green", bold=True) + WS[1] + style(msg_uri, fg="white") + style(msg_host, fg="blue", bold=True) + style(CL[1], fg="white") + style(msg_port, fg="magenta", bold=True) + WS[1] + E.ROCKET + NL[1] + WS[1] + NL[1]) start() except Exception as e: error("Failed to start web server: {e}", e=e)
def gen_cert(name, org, duration, size, show, get): """Generate SSL certificate keypair. Arguments: name {str} -- Common Name org {str} -- Organization duration -- Validity in years size {int} -- Key Size show {bool} -- Show private key in CLI """ from hyperglass_agent.cli.actions import write_cert, find_app_path if get: app_path = find_app_path() cert_path = app_path / "agent_cert.pem" if not cert_path.exists(): warning("Certificate & key files have not been generated.") do_gen = confirm( style("Would you like to generate them now?", **WARNING)) if not do_gen: error("Certificate & key files do not yet exist.") else: write_cert(name=name, org=org, duration=duration, size=size, show=show) else: with cert_path.open("r") as f: cert = f.read() label(f"Public Key:\n\n{cert}") else: write_cert(name=name, org=org, duration=duration, size=size, show=show)
def create_dir(path: Any, **kwargs: Any) -> bool: """Validate and attempt to create a directory, if it does not exist.""" # If input path is not a path object, try to make it one if not isinstance(path, Path): try: path = Path(path) except TypeError: error("{p} is not a valid path", p=path) # If path does not exist, try to create it if not path.exists(): try: path.mkdir(**kwargs) except PermissionError: error( "{u} does not have permission to create {p}. Try running with sudo?", u=os.getlogin(), p=path, ) # Verify the path was actually created if path.exists(): success("Created {p}", p=path) # If the path already exists, inform the user elif path.exists(): info("{p} already exists", p=path) return True
def install_systemd(service_path): """Installs generated systemd file to system's systemd directory. Arguments: app_path {Path} -- hyperglass runtime path Raises: ClickException: Raised if the /etc/systemd/system does not exist ClickException: Raised if the symlinked file does not exit Returns: {bool} -- True if successful """ systemd = Path("/etc/systemd/system") installed = systemd / "hyperglass-service.service" if not systemd.exists(): error("{e} does not exist. Unable to install systemd service.", e=systemd) if installed.is_symlink(): installed.unlink() installed.symlink_to(service_path) if not installed.exists(): error("Unable to symlink {s} to {d}", s=service_path, d=installed) success("Symlinked {s} to {d}", s=service_path, d=installed) return True
def write_cert(name: str, org: str, duration: int, starttime: str, size: int, show: bool) -> None: """Generate SSL certificate keypair.""" app_path = find_app_path() cert_path = app_path / "agent_cert.pem" key_path = app_path / "agent_key.pem" start = starttime end = start + timedelta(days=duration * 365) label("Hostname: {cn}", cn=name) status(""" A self-signed certificate with the above hostname as the common name attribute will be generated. This hostname must be resolvable by hyperglass via either DNS or a host file, and must match the device's `address:` field in hyperglass's devices.yaml.""") use_name = confirm("Is this the correct hostname?", default=True) if not use_name: name = prompt("Please enter the correct hostname", type=str) all_ips = [f"{a} [{i}]" for i, a in get_addresses()] status(""" hyperglass-agent adds any IP addresses reachable by hyperglass as subject alternative names to the SSL certificate. Please select any IP addresses over which hyperglass may communicate with hyperglass-agent.""") ips = [Checkbox("ips", message="Select IPs", choices=all_ips)] selected = [i.split("[")[0].strip() for i in inquire(ips)["ips"]] selected_ips = [ip_address(i) for i in selected] cert, key = make_cert(cn=name, sans=selected_ips, o=org, start=start, end=end, size=size) if show: info(f'Public Key:\n{cert.decode("utf8")}') info(f'Private Key:\n{key.decode("utf8")}') with cert_path.open("wb") as cf: cf.write(cert) if not cert_path.exists(): error("Error writing public key to {f}", f=cert_path.absolute()) success("Wrote public key to: {f}", f=cert_path.absolute()) with key_path.open("wb") as kf: kf.write(key) if not key_path.exists(): error("Error writing private key to {f}", f=key_path.absolute()) success("Wrote private key to: {f}", f=key_path.absolute())
def make_systemd(): """Generate a systemd file based on the local system. Arguments: user {str} -- User hyperglass-agent needs to be run as Returns: {str} -- Generated systemd template """ from shutil import which from getpass import getuser template = """ [Unit] Description=hyperglass-agent After=network.target [Service] User={user} Group={group} ExecStart={bin_path} start [Install] WantedBy=multi-user.target """ app_path = find_app_path() service_path = app_path / "hyperglass-agent.service" cmd_path = which("hyperglass-agent") if not cmd_path: bin_path = "python3 -m hyperglass_agent.console" warning("hyperglass executable not found, using {h}", h=bin_path) else: bin_path = cmd_path if app_path == Path.home(): user = getuser() else: user = "******" systemd = template.format(user=user, group=user, bin_path=bin_path) info(f"Generated systemd service:\n{systemd}") if service_path.exists(): service_path.unlink() with service_path.open("w") as f: f.write(systemd) if not service_path.exists(): error("Error writing systemd file to {f}", f=service_path) install_systemd(service_path) return True
def migrate_config(force=False): """Copy example config file and remove .example extensions.""" import os import shutil import secrets app_path = os.environ.get("hyperglass_agent_directory") if app_path is None: app_path = find_app_path() else: app_path = Path(app_path) example = Path(__file__).parent.parent / "example_config.yaml" target_file = app_path / "config.yaml" def copy(): shutil.copyfile(example, target_file) if not target_file.exists(): raise FileNotFoundError(str(target_file) + "does not exist.") with target_file.open("r") as f: data = f.read() gen_secret = secrets.token_urlsafe(32) data = data.replace("secret: null", "secret: '{}'".format(gen_secret)) with target_file.open("w") as f: f.write(data) success("Successfully migrated example config file to {t}", t=target_file) try: if target_file.exists(): if not force: info("{f} already exists", f=str(target_file)) else: copy() else: copy() except Exception as e: error("Failed to migrate '{f}': {e}", f=str(target_file), e=e)
def install_systemd(service_path: Path) -> bool: """Installs generated systemd file to system's systemd directory.""" systemd = Path("/etc/systemd/system") installed = systemd / "hyperglass-agent.service" if not systemd.exists(): error("{e} does not exist. Unable to install systemd service.", e=systemd) if installed.is_symlink(): installed.unlink() installed.symlink_to(service_path) if not installed.exists(): error("Unable to symlink {s} to {d}", s=service_path, d=installed) success("Symlinked {s} to {d}", s=service_path, d=installed) return True
def find_app_path(): """Try to find the app_path, prompt user to set one if it is not found.""" import os import inquirer from hyperglass_agent.util import set_app_path from hyperglass_agent.constants import APP_PATHS try: set_app_path(required=True) app_path = Path(os.environ["hyperglass_agent_directory"]) except RuntimeError: warning( "None of the supported paths for hyperglass-agent were found.\n" + "Checked:\n{one}\n{two}", one=APP_PATHS[0], two=APP_PATHS[1], ) create = confirm(style("Would you like to create one?", **WARNING)) if not create: error("hyperglass-agent requires an application path, " + "but you've chosen not to create one.") elif create: available_paths = [ inquirer.List( "selected", message="Choose a directory for hyperglass-agent", choices=APP_PATHS, ) ] answer = inquirer.prompt(available_paths) if answer is None: error("A directory for hyperglass-agent is required") selected = answer["selected"] if not selected.exists(): create_dir(selected) app_path = selected return app_path
def send_cert(): """Send this device's public key to hyperglass.""" import platform from hyperglass_agent.config import params from hyperglass_agent.util import send_public_key from pydantic import AnyHttpUrl, create_model, ValidationError app_path = find_app_path() cert_file = app_path / "agent_cert.pem" if params.ssl is not None and not params.ssl.enable: confirm( "SSL is disabled. Proceed with sending certificate to hyperglass?", default=False, abort=True, ) if not cert_file.exists(): error("File {f} does not exist", f=cert_file) with cert_file.open("r") as f: cert = f.read().strip() _hg_url = prompt("Enter hyperglass URL (e.g. https://lg.example.com)", type=str) url_model = create_model("UrlModel", url=(AnyHttpUrl, ...)) try: hg_url = url_model(url=_hg_url) except ValidationError as ve: msg = ve.errors()[0]["msg"] warning("URL {u} is invalid: {e}", u=_hg_url, e=msg) _hg_url = prompt("Enter hyperglass URL (e.g. https://lg.example.com)", type=str) try: hg_url = url_model(url=_hg_url) except ValidationError as ve: msg = ve.errors()[0]["msg"] error("URL {u} is invalid: {e}", u=_hg_url, e=msg) device_name = platform.node() keep_discovered = confirm( f"This device's hostname appears to be '{device_name}'. " + "Does this match this device's definition in hyperglass?") if not keep_discovered: device_name = prompt( "Enter the device's name as configured in hyperglass") try: status = send_public_key(str(hg_url.url), device_name=device_name, certificate=cert, params=params) success(status) except RuntimeError as re: error(str(re))
def write_cert(name, org, duration, size, show): """Generate SSL certificate keypair. Arguments: name {str} -- Common Name org {str} -- Organization duration -- Validity in years size {int} -- Key Size show {bool} -- Show private key in CLI """ app_path = find_app_path() cert_path = app_path / "agent_cert.pem" key_path = app_path / "agent_key.pem" start = datetime.now() end = start + timedelta(days=duration * 365) cert, key = make_cert(cn=name, o=org, start=start, end=end, size=size) if show: info(f'Public Key:\n{cert.decode("utf8")}') info(f'Private Key:\n{key.decode("utf8")}') with cert_path.open("wb") as cf: cf.write(cert) if not cert_path.exists(): error("Error writing public key to {f}", f=cert_path.absolute()) success("Wrote public key to: {f}", f=cert_path.absolute()) with key_path.open("wb") as kf: kf.write(key) if not key_path.exists(): error("Error writing private key to {f}", f=key_path.absolute()) success("Wrote private key to: {f}", f=key_path.absolute())
def wrapper(*args, **kwargs): try: val = func(*args, **kwargs) except BaseException as err: error(err) return val