import struct import asyncio import ipaddress from contextlib import AsyncExitStack from tornado import ioloop, tcpserver, tcpclient, iostream, gen from wsproxy import util from wsproxy.core import WsContext from wsproxy.routes import info from wsproxy.protocol.json import ( Route, RouteType, setup_subscription, SubscriptionComplete ) from wsproxy.protocol import proxy # Create the default logger for SOCKS 5. logger = util.get_child_logger('socks5') SOCKS5_VERSION = 0x05 ATYP_IPV4 = 0x01 ATYP_DOMAIN = 0x03 ATYP_IPV6 = 0x04 class Socks5Server(util.LocalTcpServer): """Base TcpServer that handles SOCKSv5 proxies. Subclasses should override to support custom proxying. """
"""proxy.py. Module to implement the parsing logic for "raw" proxy message types on the websocket connection. """ import uuid import weakref import asyncio from contextlib import AsyncExitStack from tornado import websocket, netutil, iostream, tcpclient from wsproxy import util from wsproxy.protocol.json import (Route, RouteType, setup_subscription, SubscriptionComplete, SubscriptionError) logger = util.get_child_logger('raw_proxy') DEFAULT_BUFFSIZE = (2**16) class RawProxyParser(object): """Basic Proxy Parser for raw message tunneling.""" # Single-byte character (cast to an int) to identify messages of this type. opcode = ord('r') async def process_message(self, state, message): """Process a message for the given proxy. This parser should receive a message of: 'r', <16 bytes of socket_id>, .... It will parse out the socket_id and check if any handlers are regisered
def initialize(self, resolver, unix_socket, *args, **kwargs): self.resolver = resolver self.unix_socket = unix_socket def close(self): self.resolver.close() async def resolve(self, host, port, *args, **kwargs): if host == 'unix_localhost': return [(socket.AF_UNIX, self.unix_socket)] result = await self.resolver.resolve(host, port, *args, **kwargs) return result WSPROXY_VERSION = '0.1.0' logger = util.get_child_logger('main') def create_root_cli(): @click.group(context_settings=dict(help_option_names=['-h', '--help'])) @click.version_option(WSPROXY_VERSION) def _cli(): pass return _cli main_cli = create_root_cli() @main_cli.command(
"""ssh_keys.py. Authentication module to support using SSH keys. """ import os from cryptography.hazmat.primitives.serialization import (load_ssh_public_key, load_ssh_private_key) from wsproxy.util import get_child_logger logger = get_child_logger('ssh') _DEFAULT = object() class SSHAuthManager(object): def __init__(self, local_private_key, authorized_public_keys): self.__private_key = local_private_key self.__public_keys = authorized_public_keys def private_key(self): return self.__private_key def public_keys(self): return self.__public_keys def load_ssh_auth_manager(private_key_path=_DEFAULT, authorized_key_path=_DEFAULT): user_dir = os.path.expanduser('~') authorized_keys = []
"""parser.py. Module to parse configuration options into the wsproxy service. """ import os import re from tornado import web, ioloop, httpserver, httpclient, netutil from wsproxy import auth, core, util from wsproxy.routes import registry as route_registry from wsproxy.service import misc logger = util.get_child_logger('parser') SOCKS5_INIT_REGEX = re.compile(r'socks5\:(?P<port>[0-9]+)') TUNNEL_INIT_REGEX = re.compile(r'tunnel\:(?P<port>[0-9]+)') def parse_user_config_section(options): """Parse the user settings (authentication) portion of the config.""" auth_context = auth.AuthContext() user_configs = options.get('user_config', []) # Parse each config as an auth manager. for config in user_configs: set_main = config.get('set_as_main', False) manager = parse_auth_manager(config) # Add this AuthManager to the current context. auth_context.add_auth_manager(manager) if set_main:
"""misc.py. Miscellany for running certain operations with wsproxy. """ from wsproxy.protocol.json import setup_subscription from wsproxy.util import get_child_logger logger = get_child_logger('misc') async def run_socks_proxy(port, state): """Run a socks proxy on the given port and connection. This proxies all traffic from this socks proxy over the given connection (as defined by the passed in WebsocketState). """ try: async with setup_subscription(state, 'socks5_proxy', dict(port=port)) as sub: async for msg in sub.result_generator(): logger.info("%s socks5 handler: %s", state.cxn_id, msg) except Exception: logger.exception("Error starting SOCSKv5 proxy!") async def run_tunneled_server(port, state): """Run a server on the given port and tunnel it to the client. This emulates the -L flag for SSH. """ pass
authentication pieces. Authentication works in this proxy as follows: - AuthManager reads credentials and returns AuthContext - AuthContext is queried to permit a request. There is one AuthContext per (websocket) connection, as well as for HTTP requests. """ import datetime import jwt from wsproxy.util import get_child_logger from wsproxy.authentication.context import (AllAccessAuthContext, NoAccessAuthContext) logger = get_child_logger('auth') def _empty_auth_callback(*args, **kwargs): """Default callback that returns an empty JWT token.""" return '' class AuthManager(object): """Class that is responsible for assigning AuthContexts. This class receives input credentials and returns an applicable AuthContext for that connection. """ @staticmethod def get_all_access_context(*args, **kwargs):