def main(): """ Top-level function for Telepresence """ ######################################## # Preliminaries: No changes to the machine or the cluster, no cleanup # Capture environment info and the user's intent # Check for a subcommand with crash_reporting(): args = command_parse_args(None, only_for_commands=True) if args is not None: command_main(args) with crash_reporting(): args = parse_args() # tab-completion stuff goes here runner = Runner(Output(args.logfile), None, args.verbose) span = runner.span() runner.add_cleanup("Stop time tracking", span.end) runner.kubectl = KubeInfo(runner, args) start_proxy = proxy.setup(runner, args) do_connect = connect.setup(runner, args) get_remote_env, write_env_files = remote_env.setup(runner, args) launch = outbound.setup(runner, args) mount_remote = mount.setup(runner, args) final_checks(runner, args) # Usage tracking call_scout(runner, args) ######################################## # Now it's okay to change things with runner.cleanup_handling(), crash_reporting(runner): # Set up the proxy pod (operation -> pod name) remote_info = start_proxy(runner) # Connect to the proxy (pod name -> ssh object) socks_port, ssh = do_connect(runner, remote_info) # Capture remote environment information (ssh object -> env info) env = get_remote_env(runner, remote_info) # Handle filesystem stuff mount_dir = mount_remote(runner, env, ssh) # Maybe write environment files write_env_files(runner, env) # Set up outbound networking (pod name, ssh object) # Launch user command with the correct environment (...) user_process = launch( runner, remote_info, env, socks_port, ssh, mount_dir ) wait_for_exit(runner, user_process)
def main(): """ Top-level function for Telepresence """ with crash_reporting(): ######################################## # Preliminaries: No changes to the machine or the cluster, no cleanup # Capture environment info args = parse_args() # tab-completion stuff goes here runner = Runner(args.logfile, args.verbose) span = runner.span() runner.add_cleanup("Stop time tracking", span.end) set_kube_command(runner, args) with runner.cleanup_handling(), crash_reporting(runner): ######################################## # Intent: Fast, user prompts here, cleanup available # Capture the user's intent start_proxy = proxy.setup(runner, args) do_connect = connect.setup(runner, args) get_remote_env, write_env_files = remote_env.setup(runner, args) launch = outbound.setup(runner, args) mount_remote = mount.setup(runner, args) final_checks(runner, args) # Usage tracking call_scout(runner, args) ######################################## # Action: Perform the user's intended operation(s) # Now it's okay to change things # Set up the proxy pod (operation -> pod name) remote_info = start_proxy(runner) # Connect to the proxy (pod name -> ssh object) socks_port, ssh = do_connect(runner, remote_info) # Capture remote environment information (ssh object -> env info) env, pod_info = get_remote_env(runner, ssh, remote_info) # Handle filesystem stuff mount_dir = mount_remote(runner, env, ssh) # Maybe write environment files write_env_files(runner, env) # Set up outbound networking (pod name, ssh object) # Launch user command with the correct environment (...) user_process = launch( runner, remote_info, env, socks_port, ssh, mount_dir, pod_info ) runner.wait_for_exit(user_process)
def proxy(config: typing.Dict[str, typing.Any]) -> None: """Start sshuttle proxy to Kubernetes.""" cidrs = config["cidrs"] expose_ports = config["expose_ports"] to_pod = config["to_pod"] from_pod = config["from_pod"] exclude_proxy = config["exclude_proxy"] # Launch local sshd so Tel outside can forward 38023 to the cluster runner = Runner("-", False) runner.check_call(["/usr/sbin/sshd", "-e"]) # Wait for the cluster to be available ssh = SSH(runner, 38023, "[email protected]") if not ssh.wait(): raise RuntimeError( "SSH from local container to the cluster failed to start.") # Figure out IP addresses to exclude, from the incoming ssh exclusions = [] netstat_output = runner.get_output(["netstat", "-n"]) for line in netstat_output.splitlines(): if not line.startswith("tcp") or "ESTABLISHED" not in line: continue parts = line.split() try: for address in (parts[3], parts[4]): ip, port = address.split(":") exclusions.extend(["-x", ip]) except (IndexError, ValueError): runner.write("Failed on line: " + line) raise assert exclusions, netstat_output if exclude_proxy: for cidr in exclude_proxy: exclusions.extend(["-x", cidr]) # Start the sshuttle VPN-like thing: sshuttle_cmd = get_sshuttle_command(ssh, "nat") + exclusions + cidrs main_process = Popen(sshuttle_cmd, universal_newlines=True) # Start the SSH tunnels to expose local services: expose_local_services(runner, ssh, expose_ports, to_pod, from_pod) # Wait for everything to exit: runner.wait_for_exit(main_process)
def proxy(config: dict): """Start sshuttle proxy to Kubernetes.""" cidrs = config["cidrs"] expose_ports = config["expose_ports"] # Launch local sshd so Tel outside can forward 38023 to the cluster runner = Runner("-", "-", False) runner.check_call(["/usr/sbin/sshd", "-e"]) # Wait for the cluster to be available ssh = SSH(runner, 38023, "[email protected]") ssh.wait() # Figure out IP addresses to exclude, from the incoming ssh exclusions = [] netstat_output = runner.get_output(["netstat", "-n"]) for line in netstat_output.splitlines(): if not line.startswith("tcp") or "ESTABLISHED" not in line: continue parts = line.split() try: for address in (parts[3], parts[4]): ip, port = address.split(":") exclusions.extend(["-x", ip]) except (IndexError, ValueError): runner.write("Failed on line: " + line) raise assert exclusions, netstat_output # Start the sshuttle VPN-like thing: # XXX duplicates code in telepresence, remove duplication main_process = Popen([ "sshuttle-telepresence", "-v", "--dns", "--method", "nat", "-e", ( "ssh -oStrictHostKeyChecking=no -oUserKnownHostsFile=/dev/null " + "-F /dev/null" ), "-r", "[email protected]:38023" ] + exclusions + cidrs) # Start the SSH tunnels to expose local services: expose_local_services(runner, ssh, expose_ports) # Wait for everything to exit: runner.wait_for_exit(main_process)
def command_main(args): """ Top-level function for Telepresence when executing subcommands """ with crash_reporting(): runner = Runner(Output(args.logfile), None, args.verbose) span = runner.span() runner.add_cleanup("Stop time tracking", span.end) runner.kubectl = KubeInfo(runner, args) args.operation = args.command args.method = "teleproxy" call_scout(runner, args) if args.command == "outbound": return outbound.command(runner) raise runner.fail("Not implemented!")
def analyze_args(session): """Construct session info based on user arguments""" args = session.args output = session.output kube_info = KubeInfo(args) session.output.write( "Context: {}, namespace: {}, kubectl_command: {}\n".format( kube_info.context, kube_info.namespace, kube_info.command)) # Figure out if we need capability that allows for ports < 1024: if any([p < 1024 for p in args.expose.remote()]): if kube_info.command == "oc": # OpenShift doesn't support running as root: raise SystemExit("OpenShift does not support ports <1024.") args.needs_root = True else: args.needs_root = False runner = Runner(output, kube_info, args.verbose) # minikube/minishift break DNS because DNS gets captured, sent to # minikube, which sends it back to DNS server set by host, resulting in # loop... we've fixed that for most cases, but not --deployment. def check_if_in_local_vm() -> bool: # Minikube just has 'minikube' as context' if args.context == "minikube": return True # Minishift has complex context name, so check by server: if runner.kubectl.command == "oc" and which("minishift"): ip = runner.get_output(["minishift", "ip"]).strip() if ip and ip in kube_info.server: return True return False args.in_local_vm = check_if_in_local_vm() if args.in_local_vm: output.write("Looks like we're in a local VM, e.g. minikube.\n") if (args.in_local_vm and args.method == "vpn-tcp" and args.new_deployment is None and args.swap_deployment is None): raise runner.fail( "vpn-tcp method doesn't work with minikube/minishift when" " using --deployment. Use --swap-deployment or" " --new-deployment instead.") # Make sure we can access Kubernetes: try: runner.get_output( runner.kubectl("get", "pods", "telepresence-connectivity-check", "--ignore-not-found"), stderr=STDOUT, ) except (CalledProcessError, OSError, IOError) as exc: sys.stderr.write("Error accessing Kubernetes: {}\n".format(exc)) if exc.output: sys.stderr.write("{}\n".format(exc.output.strip())) raise runner.fail("Cluster access failed") # Make sure we can run openssh: try: version = runner.get_output(["ssh", "-V"], stdin=DEVNULL, stderr=STDOUT) if not version.startswith("OpenSSH"): raise runner.fail("'ssh' is not the OpenSSH client, apparently.") except (CalledProcessError, OSError, IOError) as e: raise runner.fail("Error running ssh: {}\n".format(e)) # Other requirements: require_command(runner, "torsocks", "Please install torsocks (v2.1 or later)") if args.mount: require_command(runner, "sshfs") # Need conntrack for sshuttle on Linux: if sys.platform.startswith("linux") and args.method == "vpn-tcp": require_command(runner, "conntrack") return kube_info, runner