Skip to content

Commit

Permalink
Add port forwards for localhost to/from pod
Browse files Browse the repository at this point in the history
  • Loading branch information
Abhay Saxena committed Oct 29, 2019
1 parent 42d4789 commit 659c16b
Show file tree
Hide file tree
Showing 5 changed files with 82 additions and 35 deletions.
4 changes: 3 additions & 1 deletion local-docker/entrypoint.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,8 @@ 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"]

# Launch local sshd so Tel outside can forward 38023 to the cluster
runner = Runner("-", False)
Expand Down Expand Up @@ -105,7 +107,7 @@ def proxy(config: typing.Dict[str, typing.Any]) -> None:
main_process = Popen(sshuttle_cmd, universal_newlines=True)

# Start the SSH tunnels to expose local services:
expose_local_services(runner, ssh, expose_ports)
expose_local_services(runner, ssh, expose_ports, to_pod, from_pod)

# Wait for everything to exit:
runner.wait_for_exit(main_process)
Expand Down
17 changes: 12 additions & 5 deletions telepresence/connect/connect.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,9 @@
# See the License for the specific language governing permissions and
# limitations under the License.

from argparse import Namespace
from subprocess import CalledProcessError
from typing import Tuple
from typing import Callable, List, Tuple

from telepresence.cli import PortMapping
from telepresence.proxy import RemoteInfo
Expand All @@ -26,7 +27,7 @@

def connect(
runner: Runner, remote_info: RemoteInfo, is_container_mode: bool,
expose: PortMapping
expose: PortMapping, to_pod: List[int], from_pod: List[int]
) -> Tuple[int, SSH]:
"""
Start all the processes that handle remote proxying.
Expand Down Expand Up @@ -75,6 +76,8 @@ def connect(
runner,
ssh,
list(expose.local_to_remote()),
to_pod,
from_pod,
show_only=is_container_mode
)

Expand All @@ -97,7 +100,7 @@ def connect(
return socks_port, ssh


def setup(runner: Runner, args):
def setup(runner: Runner, args: Namespace) -> Callable:
# Make sure we can run openssh:
runner.require(["ssh"], "Please install the OpenSSH client")
try:
Expand All @@ -109,7 +112,11 @@ def setup(runner: Runner, args):

is_container_mode = args.method == "container"

def do_connect(runner_: Runner, remote_info: RemoteInfo):
return connect(runner_, remote_info, is_container_mode, args.expose)
def do_connect(runner_: Runner,
remote_info: RemoteInfo) -> Tuple[int, SSH]:
return connect(
runner_, remote_info, is_container_mode, args.expose, args.to_pod,
args.from_pod
)

return do_connect
35 changes: 23 additions & 12 deletions telepresence/connect/expose.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,43 +22,54 @@
def expose_local_services(
runner: Runner,
ssh: SSH,
port_numbers: List[Tuple[int, int]],
exp_port_numbers: List[Tuple[int, int]],
to_pod: List[int],
from_pod: List[int],
show_only: bool = False,
) -> 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).
:param show_only: Don't create the tunnels, just output info for the user
The show_only param is used to show messages for the container method; the
tunnels are created in the network container, where those messages are not
visible to the user.
"""
if not port_numbers and runner.chatty:
if not exp_port_numbers and runner.chatty:
runner.show(
"\nNo 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:
forward_arguments = [] # type: List[str]
for local_port, remote_port in exp_port_numbers:
if runner.chatty:
runner.show(
"Forwarding remote port {} to local port {}.".format(
remote_port,
local_port,
)
)
remote_forward_arguments.extend([
forward_arguments.extend([
"-R",
"*:{}:127.0.0.1:{}".format(remote_port, local_port),
])
if remote_forward_arguments and not show_only:
for port in to_pod:
if runner.chatty:
runner.show("Forwarding localhost:{} to the pod".format(port))
forward_arguments.extend([
"-L",
"127.0.0.1:{}:127.0.0.1:{}".format(port, port),
])
for port in from_pod:
if runner.chatty:
runner.show("Forwarding localhost:{} from the pod".format(port))
forward_arguments.extend([
"-R",
"127.0.0.1:{}:127.0.0.1:{}".format(port, port),
])
if forward_arguments and not show_only:
runner.launch(
"SSH port forward (exposed ports)",
ssh.bg_command(remote_forward_arguments)
ssh.bg_command(forward_arguments)
)
if runner.chatty:
runner.show("\n")
18 changes: 11 additions & 7 deletions telepresence/outbound/container.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
import json
import os
import os.path
from subprocess import CalledProcessError, Popen
import subprocess
from typing import Callable, Dict, List, Optional, Tuple

from telepresence import TELEPRESENCE_LOCAL_IMAGE
Expand All @@ -27,9 +27,9 @@
from telepresence.utilities import find_free_port, random_name


def make_docker_kill(runner: Runner, name: str) -> Callable:
def make_docker_kill(runner: Runner, name: str) -> Callable[[], None]:
"""Return a function that will kill a named docker container."""
def kill():
def kill() -> None:
runner.check_call(runner.docker("stop", "--time=1", name))

return kill
Expand Down Expand Up @@ -76,13 +76,15 @@ def run_docker_command(
remote_info: RemoteInfo,
docker_run: List[str],
expose: PortMapping,
to_pod: List[int],
from_pod: List[int],
container_to_host: PortMapping,
remote_env: Dict[str, str],
ssh: SSH,
mount_dir: Optional[str],
use_docker_mount: Optional[bool],
pod_info: Dict[str, str],
) -> Popen:
) -> "subprocess.Popen[bytes]":
"""
--docker-run support.
Expand Down Expand Up @@ -112,6 +114,8 @@ def run_docker_command(
config = {
"cidrs": ["0/0"],
"expose_ports": list(expose.local_to_remote()),
"to_pod": to_pod,
"from_pod": from_pod,
}
dns_args = []
if "hostname" in pod_info:
Expand Down Expand Up @@ -161,7 +165,7 @@ def run_docker_command(
TELEPRESENCE_LOCAL_IMAGE, "wait"
)
)
except CalledProcessError as e:
except subprocess.CalledProcessError as e:
if e.returncode == 100:
# We're good!
sshuttle_ok = True
Expand Down Expand Up @@ -218,9 +222,9 @@ def run_docker_command(
span.end()

runner.show("Setup complete. Launching your container.")
process = Popen(docker_command, env=docker_env)
process = subprocess.Popen(docker_command, env=docker_env)

def terminate_if_alive():
def terminate_if_alive() -> None:
runner.write("Shutting down containers...\n")
if process.poll() is None:
runner.write("Killing local container...\n")
Expand Down
43 changes: 33 additions & 10 deletions telepresence/outbound/setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,20 +12,34 @@
# See the License for the specific language governing permissions and
# limitations under the License.

from subprocess import CalledProcessError
import subprocess
import typing
from argparse import Namespace

from telepresence.connect import SSH
from telepresence.proxy import RemoteInfo
from telepresence.runner import Runner

from .container import run_docker_command
from .local import launch_inject, launch_vpn

LaunchType = typing.Callable[[
Runner,
RemoteInfo,
typing.Dict[str, str], # env
int,
SSH,
str,
typing.Dict[str, str], # pod info
], "subprocess.Popen[bytes]"]


def check_local_command(runner: Runner, command: str) -> None:
if runner.depend([command]):
raise runner.fail("{}: command not found".format(command))


def setup_inject(runner: Runner, args):
def setup_inject(runner: Runner, args: Namespace) -> LaunchType:
command = ["torsocks"] + (args.run or ["bash", "--norc"])
check_local_command(runner, command[1])
runner.require(["torsocks"], "Please install torsocks (v2.1 or later)")
Expand All @@ -52,7 +66,7 @@ def launch(
return launch


def setup_vpn(runner: Runner, args):
def setup_vpn(runner: Runner, args: Namespace) -> LaunchType:
command = args.run or ["bash", "--norc"]
check_local_command(runner, command[0])
runner.require(["sshuttle-telepresence"],
Expand All @@ -69,7 +83,7 @@ def setup_vpn(runner: Runner, args):
try:
ipt_command = ["sudo", "iptables", "--list"]
runner.get_output(ipt_command, stderr_to_stdout=True)
except CalledProcessError as exc:
except subprocess.CalledProcessError as exc:
runner.show("Quick test of iptables failed:")
print("\n> {}".format(" ".join(ipt_command)))
print("{}\n".format(exc.output))
Expand Down Expand Up @@ -101,7 +115,7 @@ def launch(
return launch


def setup_container(runner: Runner, args):
def setup_container(runner: Runner, args: Namespace) -> LaunchType:
runner.require_docker()
if args.also_proxy:
runner.show(
Expand Down Expand Up @@ -132,7 +146,7 @@ def setup_container(runner: Runner, args):
runner.show("ID mismatch on local Docker check.")
runner.show("\n" + local_docker_message)
raise runner.fail("Error: Local Docker daemon required")
except CalledProcessError as exc:
except subprocess.CalledProcessError as exc:
runner.show("Local Docker check failed: {}".format(exc.output))
runner.show("\n" + local_docker_message)
raise runner.fail("Error: Local Docker daemon required")
Expand All @@ -141,15 +155,24 @@ def launch(
runner_, remote_info, env, _socks_port, ssh, mount_dir, pod_info
):
return run_docker_command(
runner_, remote_info, args.docker_run, args.expose,
args.container_to_host, env, ssh, mount_dir,
args.docker_mount is not None, pod_info
runner_,
remote_info,
args.docker_run,
args.expose,
args.to_pod,
args.from_pod,
args.container_to_host,
env,
ssh,
mount_dir,
args.docker_mount is not None,
pod_info,
)

return launch


def setup(runner: Runner, args):
def setup(runner: Runner, args: Namespace) -> LaunchType:
if args.method == "inject-tcp":
return setup_inject(runner, args)

Expand Down

0 comments on commit 659c16b

Please sign in to comment.