Initial import with GitLab CI/CD and registry deploy flow
This commit is contained in:
@@ -0,0 +1,99 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import select
|
||||
import socket
|
||||
import sys
|
||||
import threading
|
||||
import time
|
||||
|
||||
import paramiko
|
||||
|
||||
|
||||
def _bridge_channels(chan: paramiko.Channel, upstream: socket.socket) -> None:
|
||||
try:
|
||||
while True:
|
||||
readers, _, _ = select.select([chan, upstream], [], [], 1.0)
|
||||
if chan in readers:
|
||||
data = chan.recv(4096)
|
||||
if not data:
|
||||
break
|
||||
upstream.sendall(data)
|
||||
if upstream in readers:
|
||||
data = upstream.recv(4096)
|
||||
if not data:
|
||||
break
|
||||
chan.sendall(data)
|
||||
finally:
|
||||
try:
|
||||
chan.close()
|
||||
except Exception:
|
||||
pass
|
||||
try:
|
||||
upstream.close()
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
|
||||
def _accept_channels(transport: paramiko.Transport, local_host: str, local_port: int) -> None:
|
||||
while transport.is_active():
|
||||
chan = transport.accept(1000)
|
||||
if chan is None:
|
||||
continue
|
||||
try:
|
||||
upstream = socket.create_connection((local_host, local_port), timeout=10)
|
||||
except OSError:
|
||||
chan.close()
|
||||
continue
|
||||
threading.Thread(target=_bridge_channels, args=(chan, upstream), daemon=True).start()
|
||||
|
||||
|
||||
def main() -> int:
|
||||
parser = argparse.ArgumentParser(description="Maintain reverse SSH tunnel for Asterisk IVR FastAGI.")
|
||||
parser.add_argument("--ssh-host", required=True)
|
||||
parser.add_argument("--ssh-port", type=int, default=22)
|
||||
parser.add_argument("--ssh-user", required=True)
|
||||
parser.add_argument("--ssh-password", required=True)
|
||||
parser.add_argument("--remote-bind-host", default="127.0.0.1")
|
||||
parser.add_argument("--remote-port", type=int, default=4573)
|
||||
parser.add_argument("--local-host", default="127.0.0.1")
|
||||
parser.add_argument("--local-port", type=int, default=4573)
|
||||
args = parser.parse_args()
|
||||
|
||||
while True:
|
||||
client = paramiko.SSHClient()
|
||||
client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
|
||||
try:
|
||||
client.connect(
|
||||
args.ssh_host,
|
||||
port=args.ssh_port,
|
||||
username=args.ssh_user,
|
||||
password=args.ssh_password,
|
||||
timeout=15,
|
||||
banner_timeout=15,
|
||||
auth_timeout=15,
|
||||
)
|
||||
transport = client.get_transport()
|
||||
if transport is None:
|
||||
raise RuntimeError("SSH transport is unavailable")
|
||||
transport.request_port_forward(args.remote_bind_host, args.remote_port)
|
||||
print(
|
||||
f"Reverse tunnel active: {args.remote_bind_host}:{args.remote_port} -> "
|
||||
f"{args.local_host}:{args.local_port}",
|
||||
flush=True,
|
||||
)
|
||||
_accept_channels(transport, args.local_host, args.local_port)
|
||||
except KeyboardInterrupt:
|
||||
return 0
|
||||
except Exception as exc:
|
||||
print(f"Reverse tunnel error: {exc}", file=sys.stderr, flush=True)
|
||||
time.sleep(3)
|
||||
finally:
|
||||
try:
|
||||
client.close()
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
Reference in New Issue
Block a user