From 8e39a0ef101b634e54b48ba2d34cb53be2e8c142 Mon Sep 17 00:00:00 2001 From: LoveIsGrief Date: Mon, 21 Dec 2020 23:52:54 +0100 Subject: [PATCH 1/5] Allow creating a server tunnel This is untested for now until we can figure out how to send requests from another I2P instance #5 - Create SAM server tunnels in trans-proxy --- contained/bin/exec.sh | 6 ++ contained/bin/trans_proxy/cli.py | 36 ++++++++- contained/bin/trans_proxy/servers/__init__.py | 18 +++++ .../{servers.py => servers/client.py} | 0 contained/bin/trans_proxy/servers/server.py | 73 +++++++++++++++++++ contained/bin/trans_proxy/types.py | 18 +++++ 6 files changed, 150 insertions(+), 1 deletion(-) create mode 100644 contained/bin/trans_proxy/servers/__init__.py rename contained/bin/trans_proxy/{servers.py => servers/client.py} (100%) create mode 100644 contained/bin/trans_proxy/servers/server.py create mode 100644 contained/bin/trans_proxy/types.py diff --git a/contained/bin/exec.sh b/contained/bin/exec.sh index e1c60ba..14ca19d 100644 --- a/contained/bin/exec.sh +++ b/contained/bin/exec.sh @@ -19,6 +19,12 @@ iptables -t nat -A OUTPUT -o eth0 \ -p tcp --dport $PROXY_SAM_PORT \ -j ACCEPT +# Allow existing connections e.g for servers +iptables -t nat -A OUTPUT -o eth0 \ + -p tcp \ + --state RELATED,ESTABLISHED \ + -j ACCEPT + # Redirect all other traffic on eth0 to trans-proxy iptables -t nat -A OUTPUT -o eth0 \ -p tcp \ diff --git a/contained/bin/trans_proxy/cli.py b/contained/bin/trans_proxy/cli.py index d508b0e..67062ca 100644 --- a/contained/bin/trans_proxy/cli.py +++ b/contained/bin/trans_proxy/cli.py @@ -24,17 +24,20 @@ from i2plib import sam from trans_proxy import fake_dns from trans_proxy.process import AsyncProcess -from trans_proxy.servers import start_client_tcp_tunnel +from trans_proxy.servers.client import start_client_tcp_tunnel +from trans_proxy.servers.server import start_server_tcp_tunnel ENV_PORT = "PROXY_PORT" ENV_SAM_HOST = "PROXY_SAM_HOST" ENV_SAM_PORT = "PROXY_SAM_PORT" ENV_DNS_PORT = "PROXY_DNS_PORT" +ENV_PRIV_KEY = "PROXY_PRIV_KEY" logger = logging.getLogger("trans_proxy.cli") def main(): + # TODO: Write helper formatter that prints default values and env var names parser = argparse.ArgumentParser( description="Forwards packets to an I2P instance and handles DNS requests" ) @@ -54,6 +57,14 @@ def main(): "--sam-port", type=int, default=os.environ.get(ENV_SAM_PORT, sam.DEFAULT_ADDRESS[1])) + # TODO: provide option to read this from a file (useful for docker secrets) + parser.add_argument( + "--priv-key", + help="Your private key in base64." + " It is generated by SAM automatically if none is provided." + " Reuse the generated one to have a stable address on the I2P network", + default=os.environ.get(ENV_PRIV_KEY) + ) dns_group = parser.add_argument_group('dns') dns_group.add_argument( @@ -61,6 +72,17 @@ def main(): type=int, default=os.environ.get(ENV_DNS_PORT, 1053)) + server_group = parser.add_argument_group('server') + server_group.add_argument( + "--server", + action="store_true", + ) + server_group.add_argument( + "--server-port", + type=int, + help="The port your app is listening to", + default=os.environ.get(ENV_DNS_PORT, 80)) + args = parser.parse_args() logging.basicConfig(level=logging.DEBUG if args.verbose else logging.INFO) @@ -85,6 +107,18 @@ def main(): } ) ] + + if args.server: + processes.append(AsyncProcess( + target=start_server_tcp_tunnel, + kwargs={ + "sam_host": args.sam_host, + "sam_port": args.sam_port, + "target_port": args.server_port, + "priv_key": args.priv_key, + } + )) + exec_processes(processes) diff --git a/contained/bin/trans_proxy/servers/__init__.py b/contained/bin/trans_proxy/servers/__init__.py new file mode 100644 index 0000000..5726897 --- /dev/null +++ b/contained/bin/trans_proxy/servers/__init__.py @@ -0,0 +1,18 @@ +# i2p-docker-proxy +# Copyright (C) 2019 LoveIsGrief +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + + +SESSION_NAME = "trans-proxy-sessions" diff --git a/contained/bin/trans_proxy/servers.py b/contained/bin/trans_proxy/servers/client.py similarity index 100% rename from contained/bin/trans_proxy/servers.py rename to contained/bin/trans_proxy/servers/client.py diff --git a/contained/bin/trans_proxy/servers/server.py b/contained/bin/trans_proxy/servers/server.py new file mode 100644 index 0000000..8c25267 --- /dev/null +++ b/contained/bin/trans_proxy/servers/server.py @@ -0,0 +1,73 @@ +# i2p-docker-proxy +# Copyright (C) 2019 LoveIsGrief +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +import asyncio +import logging + +import i2plib +from i2plib import Destination + +from trans_proxy.servers import SESSION_NAME +from trans_proxy.types import Base64 + + +async def start_server_tcp_tunnel( + sam_host: str, + sam_port: int, + target_port: int, + priv_key: Base64 = None, +): + """ + Starts a server tunnel that accepts requests from I2P + and forwards them to a given local port + + :param sam_host: + :param sam_port: + :param target_port: Where to forward the I2P traffic to + :param priv_key: Private key of a destination as printed in the logs + """ + loop = asyncio.get_event_loop() + + destination = None + if priv_key: + destination = Destination(priv_key, has_private_key=True) + + logger = logging.getLogger("servers.server_tcp_tunnel") + logger.info("Starting a server tunnel for %s", target_port) + tunnel = i2plib.ServerTunnel( + ("127.0.0.1", target_port), + destination=destination, + session_name=SESSION_NAME, + sam_address=(sam_host, sam_port), + loop=loop + ) + await tunnel.run() + + log_destination = destination + if not log_destination: + log_destination = tunnel.destination + logger.info("Started server with destination: %s", log_destination.base32) + + # Log private key for later reuse + # TODO: Probably this isn't safe and we would provide another option + if not destination: + logger.info("Your private key is %s", log_destination.private_key.base64) + try: + await tunnel.server_loop + except KeyboardInterrupt: + pass + finally: + tunnel.stop() + logger.info("Server tunnel closed") diff --git a/contained/bin/trans_proxy/types.py b/contained/bin/trans_proxy/types.py new file mode 100644 index 0000000..b0c554b --- /dev/null +++ b/contained/bin/trans_proxy/types.py @@ -0,0 +1,18 @@ +# i2p-docker-proxy +# Copyright (C) 2019 LoveIsGrief +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +import typing + +Base64 = typing.NewType('Base64', str) From 4122b08e4532f83be43f5a3f49238681f376ff66 Mon Sep 17 00:00:00 2001 From: LoveIsGrief Date: Wed, 6 Jan 2021 21:51:30 +0100 Subject: [PATCH 2/5] SAM: Use different sessions for the client and server #5 - Create SAM server tunnels in trans-proxy --- contained/bin/trans_proxy/servers/client.py | 2 +- contained/bin/trans_proxy/servers/server.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/contained/bin/trans_proxy/servers/client.py b/contained/bin/trans_proxy/servers/client.py index dccd940..1ddb7aa 100644 --- a/contained/bin/trans_proxy/servers/client.py +++ b/contained/bin/trans_proxy/servers/client.py @@ -22,7 +22,7 @@ import i2plib from trans_proxy.utils import get_original_ip -SESSION_NAME = "trans-proxy-sessions" +SESSION_NAME = "trans-proxy-session_client" async def start_client_tcp_tunnel( sam_host, diff --git a/contained/bin/trans_proxy/servers/server.py b/contained/bin/trans_proxy/servers/server.py index 8c25267..b398a24 100644 --- a/contained/bin/trans_proxy/servers/server.py +++ b/contained/bin/trans_proxy/servers/server.py @@ -19,9 +19,9 @@ import logging import i2plib from i2plib import Destination -from trans_proxy.servers import SESSION_NAME from trans_proxy.types import Base64 +SESSION_NAME = "trans-proxy-session_server" async def start_server_tcp_tunnel( sam_host: str, From 578ddfcecfc60b56ef523440f594b91b952a220e Mon Sep 17 00:00:00 2001 From: LoveIsGrief Date: Wed, 6 Jan 2021 21:52:12 +0100 Subject: [PATCH 3/5] trans-proxy: Use the correct env-vars for the server opts #5 - Create SAM server tunnels in trans-proxy --- contained/bin/trans_proxy/cli.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/contained/bin/trans_proxy/cli.py b/contained/bin/trans_proxy/cli.py index 67062ca..e0f09ea 100644 --- a/contained/bin/trans_proxy/cli.py +++ b/contained/bin/trans_proxy/cli.py @@ -31,6 +31,8 @@ ENV_PORT = "PROXY_PORT" ENV_SAM_HOST = "PROXY_SAM_HOST" ENV_SAM_PORT = "PROXY_SAM_PORT" ENV_DNS_PORT = "PROXY_DNS_PORT" +ENV_SERVER_ACTIVE = "PROXY_SERVER_ACTIVE" +ENV_SERVER_PORT = "PROXY_SERVER_PORT" ENV_PRIV_KEY = "PROXY_PRIV_KEY" logger = logging.getLogger("trans_proxy.cli") @@ -81,7 +83,7 @@ def main(): "--server-port", type=int, help="The port your app is listening to", - default=os.environ.get(ENV_DNS_PORT, 80)) + default=os.environ.get(ENV_SERVER_PORT, 80)) args = parser.parse_args() @@ -108,7 +110,7 @@ def main(): ) ] - if args.server: + if args.server or os.getenv(ENV_SERVER_ACTIVE): processes.append(AsyncProcess( target=start_server_tcp_tunnel, kwargs={ From 19860d625e81c4386a98fffd91ac08a527ca572a Mon Sep 17 00:00:00 2001 From: LoveIsGrief Date: Wed, 6 Jan 2021 21:57:21 +0100 Subject: [PATCH 4/5] docker: fix iptables rule We want to match by state, not by protocol. Establish connections shouldn't be redirected #5 - Create SAM server tunnels in trans-proxy --- contained/bin/exec.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/contained/bin/exec.sh b/contained/bin/exec.sh index 14ca19d..fbf3385 100644 --- a/contained/bin/exec.sh +++ b/contained/bin/exec.sh @@ -21,7 +21,7 @@ iptables -t nat -A OUTPUT -o eth0 \ # Allow existing connections e.g for servers iptables -t nat -A OUTPUT -o eth0 \ - -p tcp \ + -m state \ --state RELATED,ESTABLISHED \ -j ACCEPT @@ -43,5 +43,5 @@ ulogd -d tcpdump -i any -w /mount/tcp.dmp & export PYTHONPATH=/opt/bin -python3 /opt/bin/trans_proxy/cli.py \ +python3 -m trans_proxy.cli \ --verbose From 6f185ad576e57508c35541eeea75149e9108d022 Mon Sep 17 00:00:00 2001 From: LoveIsGrief Date: Wed, 6 Jan 2021 21:57:52 +0100 Subject: [PATCH 5/5] docker: Activate server tunnel and point to correct i2pd host #5 - Create SAM server tunnels in trans-proxy --- docker-compose.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docker-compose.yml b/docker-compose.yml index c5dc047..cb88137 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -27,7 +27,9 @@ services: depends_on: - i2pd environment: - PROXY_SAM_HOST: 172.16.200.3 # First floodfill + PROXY_SAM_HOST: 172.16.200.4 # First non-floodfill + PROXY_SERVER_ACTIVE: 1 + PROXY_SERVER_PORT: 8080 volumes: - /tmp/contained:/mount - ./contained/bin:/opt/bin