これは、なにをしたくて書いたもの?
時々、TCPでの通信をローカルポートから別の宛先に転送したくなることがあります。いわゆるプロキシです。
個人的に、こういう時にはsocatをよく使っているのですが。
socatでTCPプロキシサーバーを立てる - CLOVER🍀
転送先が増えてくると、これを複数書くのが面倒になってきます。そして、シェルスクリプトだとちょっと苦しい感じがします。
なので、スクリプトでも書こうかなと。
お題
今回は、以下のお題で書いてみようかなと思います。
個人的に、こういうものはコンパイルする言語ではなくてスクリプト系の言語で書きたいと思っているのと、その環境に持っていったら
すぐに使える感じにしたいので、あまりライブラリなどは入れたくありません。
なので、実装する言語もちょっと迷うのですが、今回はPythonにすることにしました。
まあ、socatはインストールしなくてはいけないんじゃないの?という話はありますが。
こういう時に書くスクリプトはPerlかどうかで迷うのですが、今はPythonかな、と…。
環境
今回の環境は、こちら。
$ lsb_release -a No LSB modules are available. Distributor ID: Ubuntu Description: Ubuntu 22.04.1 LTS Release: 22.04 Codename: jammy $ uname -srvmpio Linux 5.15.0-58-generic #64-Ubuntu SMP Thu Jan 5 11:43:13 UTC 2023 x86_64 x86_64 x86_64 GNU/Linux
Pythonとsocat。
$ python3 -V Python 3.10.6 $ socat -v 2023/01/15 15:01:59 socat[2943] E exactly 2 addresses required (there are 0); use option "-h" for help
作成したスクリプト
こんな感じにしました。
start-tcp-proxies.py
#!/usr/bin/python3 from subprocess import Popen target_host = '192.168.0.6' port_mappings: dict = { "8080": f'{target_host}:8080', "3306": f'{target_host}:3306', } def start_proxy(from_port: str, to_address: str): process: Popen = Popen(['socat', f'tcp-listen:{from_port},fork,reuseaddr', f'tcp-connect:{to_address}']) return process def start_proxies(port_mappings: dict): processes:list = [] for from_port, to_address in port_mappings.items(): processes.append(start_proxy(from_port, to_address)) print(f'bind local-port {from_port} -> to {to_address}') return processes def wait_processes(processes: list): for process in processes: process: Popen = process process.wait() def terminate_processes(processes: list): for process in processes: process: Popen = process process.terminate() if __name__ == '__main__': try: proxy_processes: list = start_proxies(port_mappings) wait_processes(proxy_processes) except KeyboardInterrupt as err: pass finally: terminate_processes(proxy_processes)
Popen
を使って、socatを指定した数だけ立ち上げます。
subprocess --- サブプロセス管理 / Popen コンストラクター
def start_proxy(from_port: str, to_address: str): process: Popen = Popen(['socat', f'tcp-listen:{from_port},fork,reuseaddr', f'tcp-connect:{to_address}']) return process
コマンド実行時に完了は待たず、あとでまとめて待ち合わせを行います。
def wait_processes(processes: list): for process in processes: process: Popen = process process.wait()
基本的には、Ctrl-cで止めるかなと思いますが。
あとはローカルポートと転送先をdict
で定義します。
target_host = '192.168.0.6' port_mappings: dict = { "8080": f'{target_host}:8080', "3306": f'{target_host}:3306', }
今回は、転送先にWildFlyとMySQLを起動しておきました。
接続先を変更したい場合は、このdict
を変更します。
確認
起動。
$ python3 /usr/local/bin/start-tcp-proxies.py bind local-port 8080 -> to 192.168.0.6:8080 bind local-port 3306 -> to 192.168.0.6:3306
確認。
$ curl -I localhost:8080 HTTP/1.1 200 OK Connection: keep-alive Last-Modified: Fri, 16 Dec 2022 00:28:16 GMT Content-Length: 1504 Content-Type: text/html Accept-Ranges: bytes Date: Sun, 15 Jan 2023 06:32:05 GMT $ curl telnet://localhost:3306 --output - J 8.0.31
OKですね。
/usr/local/bin
に置いて実行権限を付けて起動してもいいかもしれません。
$ sudo cp start-tcp-proxies.py /usr/local/bin/start-tcp-proxies $ sudo chmod a+x /usr/local/bin/start-tcp-proxies $ start-tcp-proxies bind local-port 8080 -> to 192.168.0.6:8080 bind local-port 3306 -> to 192.168.0.6:3306
こんな感じで。