Example #1
0
def websockify_init():
    # Setup basic logging to stderr.
    logger = logging.getLogger(WebSocketProxy.log_prefix)
    logger.propagate = False
    logger.setLevel(logging.INFO)
    stderr_handler = logging.StreamHandler()
    stderr_handler.setLevel(logging.DEBUG)
    log_formatter = logging.Formatter("%(message)s")
    stderr_handler.setFormatter(log_formatter)
    logger.addHandler(stderr_handler)

    # Setup optparse.
    usage = "\n    %prog [options]"
    usage += " [source_addr:]source_port [target_addr:target_port]"
    usage += "\n    %prog [options]"
    usage += " [source_addr:]source_port -- WRAP_COMMAND_LINE"
    parser = optparse.OptionParser(usage=usage)
    parser.add_option("--verbose",
                      "-v",
                      action="store_true",
                      help="verbose messages")
    parser.add_option("--traffic",
                      action="store_true",
                      help="per frame traffic")
    parser.add_option("--record",
                      help="record sessions to FILE.[session_number]",
                      metavar="FILE")
    parser.add_option("--daemon",
                      "-D",
                      dest="daemon",
                      action="store_true",
                      help="become a daemon (background process)")
    parser.add_option("--run-once",
                      action="store_true",
                      help="handle a single WebSocket connection and exit")
    parser.add_option("--timeout",
                      type=int,
                      default=0,
                      help="after TIMEOUT seconds exit when not connected")
    parser.add_option(
        "--idle-timeout",
        type=int,
        default=0,
        help="server exits after TIMEOUT seconds if there are no "
        "active connections")
    parser.add_option("--cert",
                      default="self.pem",
                      help="SSL certificate file")
    parser.add_option("--key",
                      default=None,
                      help="SSL key file (if separate from cert)")
    parser.add_option("--key-password", default=None, help="SSL key password")
    parser.add_option("--ssl-only",
                      action="store_true",
                      help="disallow non-encrypted client connections")
    parser.add_option("--ssl-target",
                      action="store_true",
                      help="connect to SSL target as SSL client")
    parser.add_option(
        "--verify-client",
        action="store_true",
        help="require encrypted client to present a valid certificate "
        "(needs Python 2.7.9 or newer or Python 3.4 or newer)")
    parser.add_option(
        "--cafile",
        metavar="FILE",
        help="file of concatenated certificates of authorities trusted "
        "for validating clients (only effective with --verify-client). "
        "If omitted, system default list of CAs is used.")
    parser.add_option(
        "--ssl-version",
        type="choice",
        default="default",
        choices=["default", "tlsv1_1", "tlsv1_2", "tlsv1_3"],
        action="store",
        help="minimum TLS version to use (default, tlsv1_1, tlsv1_2, tlsv1_3)")
    parser.add_option(
        "--ssl-ciphers",
        action="store",
        help="list of ciphers allowed for connection. For a list of "
        "supported ciphers run `openssl ciphers`")
    parser.add_option("--unix-target",
                      help="connect to unix socket target",
                      metavar="FILE")
    parser.add_option("--inetd",
                      help="inetd mode, receive listening socket from stdin",
                      action="store_true")
    parser.add_option("--web",
                      default=None,
                      metavar="DIR",
                      help="run webserver on same port. Serve files from DIR.")
    parser.add_option("--web-auth",
                      action="store_true",
                      help="require authentication to access webserver.")
    parser.add_option("--wrap-mode",
                      default="exit",
                      metavar="MODE",
                      choices=["exit", "ignore", "respawn"],
                      help="action to take when the wrapped program exits "
                      "or daemonizes: exit (default), ignore, respawn")
    parser.add_option("--prefer-ipv6",
                      "-6",
                      action="store_true",
                      dest="source_is_ipv6",
                      help="prefer IPv6 when resolving source_addr")
    parser.add_option("--libserver",
                      action="store_true",
                      help="use Python library SocketServer engine")
    parser.add_option(
        "--target-config",
        metavar="FILE",
        dest="target_cfg",
        help="Configuration file containing valid targets "
        "in the form 'token: host:port' or, alternatively, a "
        "directory containing configuration files of this form "
        "(DEPRECATED: use `--token-plugin TokenFile --token-source "
        " path/to/token/file` instead)")
    parser.add_option(
        "--token-plugin",
        default=None,
        metavar="CLASS",
        help="use a Python class, usually one from websockify.token_plugins, "
        "such as TokenFile, to process tokens into host:port pairs")
    parser.add_option("--token-source",
                      default=None,
                      metavar="ARG",
                      help="an argument to be passed to the token plugin "
                      "on instantiation")
    parser.add_option("--host-token",
                      action="store_true",
                      help="use the host HTTP header as token instead of the "
                      "token URL query parameter")
    parser.add_option(
        "--auth-plugin",
        default=None,
        metavar="CLASS",
        help="use a Python class, usually one from websockify.auth_plugins, "
        "such as BasicHTTPAuth, to determine if a connection is allowed")
    parser.add_option("--auth-source",
                      default=None,
                      metavar="ARG",
                      help="an argument to be passed to the auth plugin "
                      "on instantiation")
    parser.add_option("--heartbeat",
                      type=int,
                      default=0,
                      metavar="INTERVAL",
                      help="send a ping to the client every INTERVAL seconds")
    parser.add_option("--log-file",
                      metavar="FILE",
                      dest="log_file",
                      help="File where logs will be saved")
    parser.add_option("--syslog",
                      default=None,
                      metavar="SERVER",
                      help="Log to syslog server. SERVER can be local socket, "
                      "such as /dev/log, or a UDP host:port pair.")
    parser.add_option(
        "--legacy-syslog",
        action="store_true",
        help="Use the old syslog protocol instead of RFC 5424. "
        "Use this if the messages produced by websockify seem abnormal.")

    (opts, args) = parser.parse_args()

    # Validate options.

    if opts.token_source and not opts.token_plugin:
        parser.error("You must use --token-plugin to use --token-source")

    if opts.host_token and not opts.token_plugin:
        parser.error("You must use --token-plugin to use --host-token")

    if opts.auth_source and not opts.auth_plugin:
        parser.error("You must use --auth-plugin to use --auth-source")

    if opts.web_auth and not opts.auth_plugin:
        parser.error("You must use --auth-plugin to use --web-auth")

    if opts.web_auth and not opts.web:
        parser.error("You must use --web to use --web-auth")

    if opts.legacy_syslog and not opts.syslog:
        parser.error("You must use --syslog to use --legacy-syslog")

    opts.ssl_options = select_ssl_version(opts.ssl_version)
    del opts.ssl_version

    if opts.log_file:
        # Setup logging to user-specified file.
        opts.log_file = os.path.abspath(opts.log_file)
        log_file_handler = logging.FileHandler(opts.log_file)
        log_file_handler.setLevel(logging.DEBUG)
        log_file_handler.setFormatter(log_formatter)
        logger.addHandler(log_file_handler)

    del opts.log_file

    if opts.syslog:
        # Determine how to connect to syslog...
        if opts.syslog.count(':'):
            # User supplied a host:port pair.
            syslog_host, syslog_port = opts.syslog.rsplit(':', 1)
            try:
                syslog_port = int(syslog_port)
            except ValueError:
                parser.error("Error parsing syslog port")
            syslog_dest = (syslog_host, syslog_port)
        else:
            # User supplied a local socket file.
            syslog_dest = os.path.abspath(opts.syslog)

        from websockify.sysloghandler import WebsockifySysLogHandler

        # Determine syslog facility.
        if opts.daemon:
            syslog_facility = WebsockifySysLogHandler.LOG_DAEMON
        else:
            syslog_facility = WebsockifySysLogHandler.LOG_USER

        # Start logging to syslog.
        syslog_handler = WebsockifySysLogHandler(address=syslog_dest,
                                                 facility=syslog_facility,
                                                 ident='websockify',
                                                 legacy=opts.legacy_syslog)
        syslog_handler.setLevel(logging.DEBUG)
        syslog_handler.setFormatter(log_formatter)
        logger.addHandler(syslog_handler)

    del opts.syslog
    del opts.legacy_syslog

    if opts.verbose:
        logger.setLevel(logging.DEBUG)

    # Transform to absolute path as daemon may chdir
    if opts.target_cfg:
        opts.target_cfg = os.path.abspath(opts.target_cfg)

    if opts.target_cfg:
        opts.token_plugin = 'TokenFile'
        opts.token_source = opts.target_cfg

    del opts.target_cfg

    if sys.argv.count('--'):
        opts.wrap_cmd = args[1:]
    else:
        opts.wrap_cmd = None

    if not websockifyserver.ssl and opts.ssl_target:
        parser.error("SSL target requested and Python SSL module not loaded.")

    if opts.ssl_only and not os.path.exists(opts.cert):
        parser.error("SSL only and %s not found" % opts.cert)

    if opts.inetd:
        opts.listen_fd = sys.stdin.fileno()
    else:
        if len(args) < 1:
            parser.error("Too few arguments")
        arg = args.pop(0)
        # Parse host:port and convert ports to numbers
        if arg.count(':') > 0:
            opts.listen_host, opts.listen_port = arg.rsplit(':', 1)
            opts.listen_host = opts.listen_host.strip('[]')
        else:
            opts.listen_host, opts.listen_port = '', arg

        try:
            opts.listen_port = int(opts.listen_port)
        except ValueError:
            parser.error("Error parsing listen port")

    del opts.inetd

    if opts.wrap_cmd or opts.unix_target or opts.token_plugin:
        opts.target_host = None
        opts.target_port = None
    else:
        if len(args) < 1:
            parser.error("Too few arguments")
        arg = args.pop(0)
        if arg.count(':') > 0:
            opts.target_host, opts.target_port = arg.rsplit(':', 1)
            opts.target_host = opts.target_host.strip('[]')
        else:
            parser.error("Error parsing target")

        try:
            opts.target_port = int(opts.target_port)
        except ValueError:
            parser.error("Error parsing target port")

    if len(args) > 0 and opts.wrap_cmd == None:
        parser.error("Too many arguments")

    if opts.token_plugin is not None:
        if '.' not in opts.token_plugin:
            opts.token_plugin = ('websockify.token_plugins.%s' %
                                 opts.token_plugin)

        token_plugin_module, token_plugin_cls = opts.token_plugin.rsplit(
            '.', 1)

        __import__(token_plugin_module)
        token_plugin_cls = getattr(sys.modules[token_plugin_module],
                                   token_plugin_cls)

        opts.token_plugin = token_plugin_cls(opts.token_source)

    del opts.token_source

    if opts.auth_plugin is not None:
        if '.' not in opts.auth_plugin:
            opts.auth_plugin = 'websockify.auth_plugins.%s' % opts.auth_plugin

        auth_plugin_module, auth_plugin_cls = opts.auth_plugin.rsplit('.', 1)

        __import__(auth_plugin_module)
        auth_plugin_cls = getattr(sys.modules[auth_plugin_module],
                                  auth_plugin_cls)

        opts.auth_plugin = auth_plugin_cls(opts.auth_source)

    del opts.auth_source

    # Create and start the WebSockets proxy
    libserver = opts.libserver
    del opts.libserver
    if libserver:
        # Use standard Python SocketServer framework
        server = LibProxyServer(**opts.__dict__)
        server.serve_forever()
    else:
        # Use internal service framework
        server = WebSocketProxy(**opts.__dict__)
        server.start_server()
Example #2
0
def websockify_init():
    # Setup basic logging to stderr.
    logger = logging.getLogger(WebSocketProxy.log_prefix)
    logger.propagate = False
    logger.setLevel(logging.INFO)
    stderr_handler = logging.StreamHandler()
    stderr_handler.setLevel(logging.DEBUG)
    log_formatter = logging.Formatter("%(message)s")
    stderr_handler.setFormatter(log_formatter)
    logger.addHandler(stderr_handler)

    # Setup optparse.
    usage = "\n    %prog [options]"
    usage += " [source_addr:]source_port [target_addr:target_port]"
    usage += "\n    %prog [options]"
    usage += " [source_addr:]source_port -- WRAP_COMMAND_LINE"
    parser = optparse.OptionParser(usage=usage)
    parser.add_option("--verbose", "-v", action="store_true",
            help="verbose messages")
    parser.add_option("--traffic", action="store_true",
            help="per frame traffic")
    parser.add_option("--record",
            help="record sessions to FILE.[session_number]", metavar="FILE")
    parser.add_option("--daemon", "-D",
            dest="daemon", action="store_true",
            help="become a daemon (background process)")
    parser.add_option("--run-once", action="store_true",
            help="handle a single WebSocket connection and exit")
    parser.add_option("--timeout", type=int, default=0,
            help="after TIMEOUT seconds exit when not connected")
    parser.add_option("--idle-timeout", type=int, default=0,
            help="server exits after TIMEOUT seconds if there are no "
                 "active connections")
    parser.add_option("--cert", default="self.pem",
            help="SSL certificate file")
    parser.add_option("--key", default=None,
            help="SSL key file (if separate from cert)")
    parser.add_option("--key-password", default=None,
            help="SSL key password")
    parser.add_option("--ssl-only", action="store_true",
            help="disallow non-encrypted client connections")
    parser.add_option("--ssl-target", action="store_true",
            help="connect to SSL target as SSL client")
    parser.add_option("--verify-client", action="store_true",
            help="require encrypted client to present a valid certificate "
            "(needs Python 2.7.9 or newer or Python 3.4 or newer)")
    parser.add_option("--cafile", metavar="FILE",
            help="file of concatenated certificates of authorities trusted "
            "for validating clients (only effective with --verify-client). "
            "If omitted, system default list of CAs is used.")
    parser.add_option("--ssl-version", type="choice", default="default",
            choices=["default", "tlsv1_1", "tlsv1_2", "tlsv1_3"], action="store",
            help="minimum TLS version to use (default, tlsv1_1, tlsv1_2, tlsv1_3)")
    parser.add_option("--ssl-ciphers", action="store",
            help="list of ciphers allowed for connection. For a list of "
            "supported ciphers run `openssl ciphers`")
    parser.add_option("--unix-target",
            help="connect to unix socket target", metavar="FILE")
    parser.add_option("--inetd",
            help="inetd mode, receive listening socket from stdin", action="store_true")
    parser.add_option("--web", default=None, metavar="DIR",
            help="run webserver on same port. Serve files from DIR.")
    parser.add_option("--web-auth", action="store_true",
            help="require authentication to access webserver.")
    parser.add_option("--wrap-mode", default="exit", metavar="MODE",
            choices=["exit", "ignore", "respawn"],
            help="action to take when the wrapped program exits "
            "or daemonizes: exit (default), ignore, respawn")
    parser.add_option("--prefer-ipv6", "-6",
            action="store_true", dest="source_is_ipv6",
            help="prefer IPv6 when resolving source_addr")
    parser.add_option("--libserver", action="store_true",
            help="use Python library SocketServer engine")
    parser.add_option("--target-config", metavar="FILE",
            dest="target_cfg",
            help="Configuration file containing valid targets "
            "in the form 'token: host:port' or, alternatively, a "
            "directory containing configuration files of this form "
            "(DEPRECATED: use `--token-plugin TokenFile --token-source "
            " path/to/token/file` instead)")
    parser.add_option("--token-plugin", default=None, metavar="CLASS",
                      help="use a Python class, usually one from websockify.token_plugins, "
                           "such as TokenFile, to process tokens into host:port pairs")
    parser.add_option("--token-source", default=None, metavar="ARG",
                      help="an argument to be passed to the token plugin "
                           "on instantiation")
    parser.add_option("--host-token", action="store_true",
                      help="use the host HTTP header as token instead of the "
                           "token URL query parameter")
    parser.add_option("--auth-plugin", default=None, metavar="CLASS",
                      help="use a Python class, usually one from websockify.auth_plugins, "
                           "such as BasicHTTPAuth, to determine if a connection is allowed")
    parser.add_option("--auth-source", default=None, metavar="ARG",
                      help="an argument to be passed to the auth plugin "
                           "on instantiation")
    parser.add_option("--heartbeat", type=int, default=0, metavar="INTERVAL",
            help="send a ping to the client every INTERVAL seconds")
    parser.add_option("--log-file", metavar="FILE",
            dest="log_file",
            help="File where logs will be saved")
    parser.add_option("--syslog", default=None, metavar="SERVER",
            help="Log to syslog server. SERVER can be local socket, "
                 "such as /dev/log, or a UDP host:port pair.")
    parser.add_option("--legacy-syslog", action="store_true",
                      help="Use the old syslog protocol instead of RFC 5424. "
                           "Use this if the messages produced by websockify seem abnormal.")

    (opts, args) = parser.parse_args()


    # Validate options.

    if opts.token_source and not opts.token_plugin:
        parser.error("You must use --token-plugin to use --token-source")

    if opts.host_token and not opts.token_plugin:
        parser.error("You must use --token-plugin to use --host-token")

    if opts.auth_source and not opts.auth_plugin:
        parser.error("You must use --auth-plugin to use --auth-source")

    if opts.web_auth and not opts.auth_plugin:
        parser.error("You must use --auth-plugin to use --web-auth")

    if opts.web_auth and not opts.web:
        parser.error("You must use --web to use --web-auth")

    if opts.legacy_syslog and not opts.syslog:
        parser.error("You must use --syslog to use --legacy-syslog")


    opts.ssl_options = select_ssl_version(opts.ssl_version)
    del opts.ssl_version


    if opts.log_file:
        # Setup logging to user-specified file.
        opts.log_file = os.path.abspath(opts.log_file)
        log_file_handler = logging.FileHandler(opts.log_file)
        log_file_handler.setLevel(logging.DEBUG)
        log_file_handler.setFormatter(log_formatter)
        logger.addHandler(log_file_handler)

    del opts.log_file

    if opts.syslog:
        # Determine how to connect to syslog...
        if opts.syslog.count(':'):
            # User supplied a host:port pair.
            syslog_host, syslog_port = opts.syslog.rsplit(':', 1)
            try:
                syslog_port = int(syslog_port)
            except ValueError:
                parser.error("Error parsing syslog port")
            syslog_dest = (syslog_host, syslog_port)
        else:
            # User supplied a local socket file.
            syslog_dest = os.path.abspath(opts.syslog)

        from websockify.sysloghandler import WebsockifySysLogHandler

        # Determine syslog facility.
        if opts.daemon:
            syslog_facility = WebsockifySysLogHandler.LOG_DAEMON
        else:
            syslog_facility = WebsockifySysLogHandler.LOG_USER

        # Start logging to syslog.
        syslog_handler = WebsockifySysLogHandler(address=syslog_dest,
                                                 facility=syslog_facility,
                                                 ident='websockify',
                                                 legacy=opts.legacy_syslog)
        syslog_handler.setLevel(logging.DEBUG)
        syslog_handler.setFormatter(log_formatter)
        logger.addHandler(syslog_handler)

    del opts.syslog
    del opts.legacy_syslog

    if opts.verbose:
        logger.setLevel(logging.DEBUG)


    # Transform to absolute path as daemon may chdir
    if opts.target_cfg:
        opts.target_cfg = os.path.abspath(opts.target_cfg)

    if opts.target_cfg:
        opts.token_plugin = 'TokenFile'
        opts.token_source = opts.target_cfg

    del opts.target_cfg

    if sys.argv.count('--'):
        opts.wrap_cmd = args[1:]
    else:
        opts.wrap_cmd = None

    if not websockifyserver.ssl and opts.ssl_target:
        parser.error("SSL target requested and Python SSL module not loaded.");

    if opts.ssl_only and not os.path.exists(opts.cert):
        parser.error("SSL only and %s not found" % opts.cert)

    if opts.inetd:
        opts.listen_fd = sys.stdin.fileno()
    else:
        if len(args) < 1:
            parser.error("Too few arguments")
        arg = args.pop(0)
        # Parse host:port and convert ports to numbers
        if arg.count(':') > 0:
            opts.listen_host, opts.listen_port = arg.rsplit(':', 1)
            opts.listen_host = opts.listen_host.strip('[]')
        else:
            opts.listen_host, opts.listen_port = '', arg

        try:
            opts.listen_port = int(opts.listen_port)
        except ValueError:
            parser.error("Error parsing listen port")

    del opts.inetd

    if opts.wrap_cmd or opts.unix_target or opts.token_plugin:
        opts.target_host = None
        opts.target_port = None
    else:
        if len(args) < 1:
            parser.error("Too few arguments")
        arg = args.pop(0)
        if arg.count(':') > 0:
            opts.target_host, opts.target_port = arg.rsplit(':', 1)
            opts.target_host = opts.target_host.strip('[]')
        else:
            parser.error("Error parsing target")

        try:
            opts.target_port = int(opts.target_port)
        except ValueError:
            parser.error("Error parsing target port")

    if len(args) > 0 and opts.wrap_cmd == None:
        parser.error("Too many arguments")

    if opts.token_plugin is not None:
        if '.' not in opts.token_plugin:
            opts.token_plugin = (
                'websockify.token_plugins.%s' % opts.token_plugin)

        token_plugin_module, token_plugin_cls = opts.token_plugin.rsplit('.', 1)

        __import__(token_plugin_module)
        token_plugin_cls = getattr(sys.modules[token_plugin_module], token_plugin_cls)

        opts.token_plugin = token_plugin_cls(opts.token_source)

    del opts.token_source

    if opts.auth_plugin is not None:
        if '.' not in opts.auth_plugin:
            opts.auth_plugin = 'websockify.auth_plugins.%s' % opts.auth_plugin

        auth_plugin_module, auth_plugin_cls = opts.auth_plugin.rsplit('.', 1)

        __import__(auth_plugin_module)
        auth_plugin_cls = getattr(sys.modules[auth_plugin_module], auth_plugin_cls)

        opts.auth_plugin = auth_plugin_cls(opts.auth_source)

    del opts.auth_source

    # Create and start the WebSockets proxy
    libserver = opts.libserver
    del opts.libserver
    if libserver:
        # Use standard Python SocketServer framework
        server = LibProxyServer(**opts.__dict__)
        server.serve_forever()
    else:
        # Use internal service framework
        server = WebSocketProxy(**opts.__dict__)
        server.start_server()