Merge pull request 'clan-cli: Make clan ssh automatically start tor' (#2623) from Qubasa/clan-core:Qubasa-main into main

This commit is contained in:
clan-bot
2024-12-17 11:19:21 +00:00
3 changed files with 46 additions and 20 deletions

View File

@@ -2,16 +2,16 @@ import argparse
import ipaddress import ipaddress
import json import json
import logging import logging
import shlex
from dataclasses import dataclass from dataclasses import dataclass
from pathlib import Path from pathlib import Path
from typing import Any from typing import Any
from clan_cli.async_run import AsyncRuntime
from clan_cli.cmd import run from clan_cli.cmd import run
from clan_cli.errors import ClanError, TorConnectionError, TorSocksError from clan_cli.errors import ClanError
from clan_cli.nix import nix_shell from clan_cli.nix import nix_shell
from clan_cli.ssh.host import Host, is_ssh_reachable from clan_cli.ssh.host import Host, is_ssh_reachable
from clan_cli.ssh.tor import TorTarget, ssh_tor_reachable from clan_cli.ssh.tor import TorTarget, spawn_tor, ssh_tor_reachable
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
@@ -63,12 +63,13 @@ def parse_qr_code(picture_file: Path) -> DeployInfo:
return DeployInfo.from_json(json.loads(data)) return DeployInfo.from_json(json.loads(data))
def ssh_shell_from_deploy(deploy_info: DeployInfo) -> None: def ssh_shell_from_deploy(deploy_info: DeployInfo, runtime: AsyncRuntime) -> None:
if host := find_reachable_host(deploy_info): if host := find_reachable_host(deploy_info):
host.connect_ssh_shell(password=deploy_info.pwd) host.connect_ssh_shell(password=deploy_info.pwd)
else: else:
log.info("Could not reach host via clearnet 'addrs'") log.info("Could not reach host via clearnet 'addrs'")
log.info(f"Trying to reach host via tor '{deploy_info.tor}'") log.info(f"Trying to reach host via tor '{deploy_info.tor}'")
spawn_tor(runtime)
if not deploy_info.tor: if not deploy_info.tor:
msg = "No tor address provided, please provide a tor address." msg = "No tor address provided, please provide a tor address."
raise ClanError(msg) raise ClanError(msg)
@@ -98,15 +99,9 @@ def ssh_command(args: argparse.Namespace) -> None:
if not deploy_info: if not deploy_info:
msg = "No --json or --png data provided" msg = "No --json or --png data provided"
raise ClanError(msg) raise ClanError(msg)
try:
ssh_shell_from_deploy(deploy_info) with AsyncRuntime() as runtime:
except TorSocksError as ex: ssh_shell_from_deploy(deploy_info, runtime)
log.error(ex)
tor_cmd = nix_shell(["nixpkgs#tor"], ["tor"])
log.error("Is Tor running? If not, you can start it by running:")
log.error(f"{' '.join(shlex.quote(arg) for arg in tor_cmd)}")
except TorConnectionError:
log.error("The onion address is not reachable via Tor.")
def register_parser(parser: argparse.ArgumentParser) -> None: def register_parser(parser: argparse.ArgumentParser) -> None:

View File

@@ -185,6 +185,7 @@ class Host:
ssh_opts.extend(["-i", self.key]) ssh_opts.extend(["-i", self.key])
if tor_socks: if tor_socks:
packages.append("nixpkgs#netcat")
ssh_opts.append("-o") ssh_opts.append("-o")
ssh_opts.append("ProxyCommand=nc -x 127.0.0.1:9050 -X 5 %h %p") ssh_opts.append("ProxyCommand=nc -x 127.0.0.1:9050 -X 5 %h %p")

View File

@@ -4,9 +4,11 @@ import argparse
import logging import logging
import socket import socket
import struct import struct
import time
from dataclasses import dataclass from dataclasses import dataclass
from clan_cli.cmd import run from clan_cli.async_run import AsyncRuntime
from clan_cli.cmd import Log, RunOpts, run
from clan_cli.errors import TorConnectionError, TorSocksError from clan_cli.errors import TorConnectionError, TorSocksError
from clan_cli.nix import nix_shell from clan_cli.nix import nix_shell
@@ -96,14 +98,42 @@ def fetch_onion_content(target: TorTarget) -> str:
return response.decode("utf-8", errors="replace") return response.decode("utf-8", errors="replace")
def spawn_tor() -> None: def is_tor_running() -> bool:
"""Checks if Tor is online."""
try:
tor_online_test()
except TorSocksError:
return False
else:
return True
def spawn_tor(runtime: AsyncRuntime) -> None:
""" """
Spawns a Tor process using `nix-shell`. Spawns a Tor process using `nix-shell` if Tor is not already running.
""" """
cmd_args = ["tor"]
def start_tor() -> None:
"""Starts Tor process using nix-shell."""
cmd_args = ["tor", "--HardwareAccel", "1"]
packages = ["nixpkgs#tor"] packages = ["nixpkgs#tor"]
cmd = nix_shell(packages, cmd_args) cmd = nix_shell(packages, cmd_args)
run(cmd) runtime.async_run(None, run, cmd, RunOpts(log=Log.BOTH))
log.debug("Attempting to start Tor")
# Check if Tor is already running
if is_tor_running():
log.info("Tor is running")
return
# Attempt to start Tor
start_tor()
# Continuously check if Tor has started
while not is_tor_running():
log.debug("Waiting for Tor to start...")
time.sleep(0.2)
log.info("Tor is now running")
def tor_online_test() -> bool: def tor_online_test() -> bool: