def expose_local_services(runner: Runner, ssh: SSH, port_numbers: List[Tuple[int, int]]) -> None: """Create SSH tunnels from remote proxy pod to local host. :param runner: The runner :param ssh: A 'SSH` instance. :param port_numbers: List of pairs of (local port, remote port). """ output = sys.stderr.isatty() if not port_numbers and output: runner.show( "No traffic is being forwarded from the remote Deployment to your" " local machine. You can use the --expose option to specify which" " ports you want to forward.") remote_forward_arguments = [] for local_port, remote_port in port_numbers: if output: runner.show("Forwarding remote port {} to local port {}.".format( remote_port, local_port, )) remote_forward_arguments.extend([ "-R", "*:{}:127.0.0.1:{}".format(remote_port, local_port), ]) if remote_forward_arguments: runner.launch("SSH port forward (exposed ports)", ssh.bg_command(remote_forward_arguments)) if output: runner.show("")
def connect(runner: Runner, remote_info: RemoteInfo, cmdline_args: argparse.Namespace) -> Tuple[int, SSH]: """ Start all the processes that handle remote proxying. Return (local port of SOCKS proxying tunnel, SSH instance). """ span = runner.span() # Keep local copy of pod logs, for debugging purposes: runner.launch( "kubectl logs", runner.kubectl("logs", "-f", remote_info.pod_name, "--container", remote_info.container_name), bufsize=0, ) ssh = SSH(runner, find_free_port()) # forward remote port to here, by tunneling via remote SSH server: runner.launch( "kubectl port-forward", runner.kubectl("port-forward", remote_info.pod_name, "{}:8022".format(ssh.port))) if cmdline_args.method == "container": # kubectl port-forward currently only listens on loopback. So we # portforward from the docker0 interface on Linux, and the lo0 alias we # added on OS X, to loopback (until we can use kubectl port-forward # option to listen on docker0 - # https://github.com/kubernetes/kubernetes/pull/46517, or all our users # have latest version of Docker for Mac, which has nicer solution - # https://github.com/datawire/telepresence/issues/224). if sys.platform == "linux": # If ip addr is available use it if not fall back to ifconfig. if which("ip"): docker_interfaces = re.findall( r"(\d+\.\d+\.\d+\.\d+)", runner.get_output(["ip", "addr", "show", "dev", "docker0"])) elif which("ifconfig"): docker_interfaces = re.findall( r"(\d+\.\d+\.\d+\.\d+)", runner.get_output(["ifconfig", "docker0"])) else: raise runner.fail("'ip addr' nor 'ifconfig' available") if len(docker_interfaces) == 0: raise runner.fail("No interface for docker found") docker_interface = docker_interfaces[0] else: # The way to get routing from container to host is via an alias on # lo0 (https://docs.docker.com/docker-for-mac/networking/). We use # an IP range that is assigned for testing network devices and # therefore shouldn't conflict with real IPs or local private # networks (https://tools.ietf.org/html/rfc6890). runner.check_call( ["sudo", "ifconfig", "lo0", "alias", MAC_LOOPBACK_IP]) runner.add_cleanup( "Mac Loopback", runner.check_call, ["sudo", "ifconfig", "lo0", "-alias", MAC_LOOPBACK_IP]) docker_interface = MAC_LOOPBACK_IP runner.launch("socat for docker", [ "socat", "TCP4-LISTEN:{},bind={},reuseaddr,fork".format( ssh.port, docker_interface, ), "TCP4:127.0.0.1:{}".format(ssh.port) ]) ssh.wait() # In Docker mode this happens inside the local Docker container: if cmdline_args.method != "container": expose_local_services( runner, ssh, cmdline_args.expose.local_to_remote(), ) # Start tunnels for the SOCKS proxy (local -> remote) # and the local server for the proxy to poll (remote -> local). socks_port = find_free_port() local_server_port = find_free_port() runner.track_background( launch_local_server(local_server_port, runner.output)) forward_args = [ "-L127.0.0.1:{}:127.0.0.1:9050".format(socks_port), "-R9055:127.0.0.1:{}".format(local_server_port) ] runner.launch("SSH port forward (socks and proxy poll)", ssh.bg_command(forward_args)) span.end() return socks_port, ssh