Compare commits

..

2 Commits

Author SHA1 Message Date
415c16b4ef Formatting. 2024-12-08 10:34:11 +01:00
0dffc6bdd8 Initial commit. 2024-12-08 10:32:43 +01:00
6 changed files with 186 additions and 0 deletions

31
Makefile Normal file
View File

@ -0,0 +1,31 @@
BIN = ./.venv/bin/
PYTHON = ./.venv/bin/python
PIP = ./.venv/bin/pip
APP_NAME=shadowssh
all: install build
ensure_repo:
-@git init
ensure_env: ensure_repo
-@python3 -m venv .venv
install:
$(PIP) install -e .
format:
$(PIP) install shed
. $(BIN)/activate && shed
build: install format
$(PIP) install build
$(PYTHON) -m build
serve:
$(BIN)$(APP_NAME).serve --host=0.0.0.0 --port=443
run: serve

3
pyproject.toml Normal file
View File

@ -0,0 +1,3 @@
[build-system]
requires = ["setuptools", "wheel"]
build-backend = "setuptools.build_meta"

25
setup.cfg Normal file
View File

@ -0,0 +1,25 @@
[metadata]
name = shadowssh
version = 1.0.0
description = Run ssh on other service port!
author = retoor
author_email = retoor@molodetz.nl
license = MIT
long_description = file: README.md
long_description_content_type = text/markdown
[options]
packages = find:
package_dir =
= src
python_requires = >=3.7
install_requires =
app @ git+https://retoor.molodetz.nl/retoor/app.git
mololog @git+https://retoor.molodetz.nl/retoor/mololog.git
[options.packages.find]
where = src
[options.entry_points]
console_scripts =
shadowssh.serve = shadowssh.__main__:serve

View File

@ -0,0 +1,8 @@
import logging
from mololog.client import patch
logging.basicConfig(level=logging.INFO)
log = logging.getLogger()
patch("https://mololog.molodetz.nl/")

31
src/shadowssh/__main__.py Normal file
View File

@ -0,0 +1,31 @@
import argparse
import asyncio
from shadowssh.app import Application
def parse_args():
parser = argparse.ArgumentParser(description="Shadowssh")
parser.add_argument("--host", type=str, required=False, default="0.0.0.0")
parser.add_argument("--port", type=int, required=False, default=443)
parser.add_argument("--host-service", type=str, required=False, default="127.0.0.1")
parser.add_argument("--port-service", type=int, required=False, default=4433)
parser.add_argument("--host-ssh", type=str, required=False, default="127.0.0.1")
parser.add_argument("--port-ssh", type=int, required=False, default=22)
return parser.parse_args()
def serve():
args = parse_args()
app = Application()
asyncio.run(
app.serve(
args.host,
args.port,
args.host_service,
args.port_service,
args.host_ssh,
args.port_ssh,
)
)

88
src/shadowssh/app.py Normal file
View File

@ -0,0 +1,88 @@
import asyncio
from app.app import Application as BaseApplication
from shadowssh import log
class Application(BaseApplication):
def __init__(
self,
host_forward_service=None,
port_forward_service=None,
host_forward_ssh=None,
port_forward_ssh=None,
*args,
**kwargs,
):
self.host_forward_service = host_forward_service
self.port_forward_service = port_forward_service
self.host_forward_ssh = host_forward_ssh
self.port_forward_ssh = port_forward_ssh
super().__init__(*args, **kwargs)
async def connect(self, host, port):
log.info(f"Connected to {host}:{port}.")
reader, writer = await asyncio.open_connection(host, port)
return reader, writer
async def stream(self, reader, writer, port):
while True:
chunk = await reader.read(4096)
if not chunk:
writer.close()
log.info("Forward connection closed.")
return
writer.write(chunk)
await writer.drain()
async def forward(self, reader, writer):
data = await reader.read(10)
if not data:
return
port = None
host = None
if b"SSH-2.0" in data:
port = self.port_forward_ssh
host = self.host_forward_ssh
else:
port = self.port_forward_service
host = self.host_forward_service
log.info(f"Streaming to port: {port}.")
upstream_reader, upstream_writer = await self.connect(host, port)
upstream_writer.write(data)
tasks = []
tasks.append(asyncio.create_task(self.stream(reader, upstream_writer, port)))
tasks.append(asyncio.create_task(self.stream(upstream_reader, writer, port)))
await asyncio.gather(*tasks)
async def route(self, host, port):
async def handle_client(reader, writer):
await self.forward(reader, writer)
server = await asyncio.start_server(handle_client, host, port)
addr = server.sockets[0].getsockname()
log.info(f"Serving on {addr}")
async with server:
await server.serve_forever()
async def serve(
self,
host,
port,
host_forward_service,
port_forward_service,
host_forward_ssh,
port_forward_ssh,
):
self.host_forward_service = host_forward_service
self.port_forward_service = port_forward_service
self.host_forward_ssh = host_forward_ssh
self.port_forward_ssh = port_forward_ssh
tasks = [self.route(host, port)]
await asyncio.gather(*tasks)