From d80c607cb629ab437a570629e133f39709b88678 Mon Sep 17 00:00:00 2001 From: josedpedroso Date: Thu, 12 Jul 2018 20:21:08 +0100 Subject: [PATCH 1/2] Port no longer included in token when using --host-token --- websockify/websocketproxy.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/websockify/websocketproxy.py b/websockify/websocketproxy.py index b8e3d99..4ad4918 100644 --- a/websockify/websocketproxy.py +++ b/websockify/websocketproxy.py @@ -146,8 +146,15 @@ Traffic Legend: # in the form of token: host:port if self.host_token: + # Use hostname as token token = self.headers.get('Host') + # Remove port from hostname, as it'll always be the one where + # websockify listens (unless something between the client and + # websockify is redirecting traffic, but that's beside the point) + if token: + token = token.partition(':')[0] + else: # Extract the token parameter from url args = parse_qs(urlparse(self.path)[4]) # 4 is the query from url From 7e29a36f6a026a3599341fb65096f91bcac6e5a0 Mon Sep 17 00:00:00 2001 From: josedpedroso Date: Fri, 24 Aug 2018 03:07:14 +0100 Subject: [PATCH 2/2] Syslog now RFC 5424 compliant and properly identified. --- websockify/sysloghandler.py | 118 +++++++++++++++++++++++++++++++++++ websockify/websocketproxy.py | 59 +++++++++++------- 2 files changed, 155 insertions(+), 22 deletions(-) create mode 100644 websockify/sysloghandler.py diff --git a/websockify/sysloghandler.py b/websockify/sysloghandler.py new file mode 100644 index 0000000..92ca66f --- /dev/null +++ b/websockify/sysloghandler.py @@ -0,0 +1,118 @@ +import logging.handlers as handlers, socket, os, time + + +class WebsockifySysLogHandler(handlers.SysLogHandler): + """ + A handler class that sends proper Syslog-formatted messages, + as defined by RFC 5424. + """ + + _legacy_head_fmt = '<{pri}>{ident}[{pid}]: ' + _rfc5424_head_fmt = '<{pri}>1 {timestamp} {hostname} {ident} {pid} - - ' + _head_fmt = _rfc5424_head_fmt + _legacy = False + _timestamp_fmt = '%Y-%m-%dT%H:%M:%SZ' + _max_hostname = 255 + _max_ident = 24 #safer for old daemons + _send_length = False + _tail = '\n' + + + ident = None + + + def __init__(self, address=('localhost', handlers.SYSLOG_UDP_PORT), + facility=handlers.SysLogHandler.LOG_USER, + socktype=None, ident=None, legacy=False): + """ + Initialize a handler. + + If address is specified as a string, a UNIX socket is used. To log to a + local syslogd, "WebsockifySysLogHandler(address="/dev/log")" can be + used. If facility is not specified, LOG_USER is used. If socktype is + specified as socket.SOCK_DGRAM or socket.SOCK_STREAM, that specific + socket type will be used. For Unix sockets, you can also specify a + socktype of None, in which case socket.SOCK_DGRAM will be used, falling + back to socket.SOCK_STREAM. If ident is specified, this string will be + used as the application name in all messages sent. Set legacy to True + to use the old version of the protocol. + """ + + self.ident = ident + + if legacy: + self._legacy = True + self._head_fmt = self._legacy_head_fmt + + handlers.SysLogHandler.__init__(self, address, facility, socktype) + + + def emit(self, record): + """ + Emit a record. + + The record is formatted, and then sent to the syslog server. If + exception information is present, it is NOT sent to the server. + """ + + try: + # Gather info. + text = self.format(record).replace(self._tail, ' ') + if not text: # nothing to log + return + + pri = self.encodePriority(self.facility, + self.mapPriority(record.levelname)) + + timestamp = time.strftime(self._timestamp_fmt, time.gmtime()); + + hostname = socket.gethostname()[:self._max_hostname] + + if self.ident: + ident = self.ident[:self._max_ident] + else: + ident = '' + + pid = os.getpid() # shouldn't need truncation + + # Format the header. + head = { + 'pri': pri, + 'timestamp': timestamp, + 'hostname': hostname, + 'ident': ident, + 'pid': pid, + } + msg = self._head_fmt.format(**head).encode('ascii', 'ignore') + + # Encode text as plain ASCII if possible, else use UTF-8 with BOM. + try: + msg += text.encode('ascii') + except UnicodeEncodeError: + msg += text.encode('utf-8-sig') + + # Add length or tail character, if necessary. + if self.socktype != socket.SOCK_DGRAM: + if self._send_length: + msg = ('%d ' % len(msg)).encode('ascii') + msg + else: + msg += self._tail.encode('ascii') + + # Send the message. + if self.unixsocket: + try: + self.socket.send(msg) + except socket.error: + self._connect_unixsocket(self.address) + self.socket.send(msg) + + else: + if self.socktype == socket.SOCK_DGRAM: + self.socket.sendto(msg, self.address) + else: + self.socket.sendall(msg) + + except (KeyboardInterrupt, SystemExit): + raise + except: + self.handleError(record) diff --git a/websockify/websocketproxy.py b/websockify/websocketproxy.py index 4ad4918..6e7edbe 100644 --- a/websockify/websocketproxy.py +++ b/websockify/websocketproxy.py @@ -484,9 +484,38 @@ def websockify_init(): 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) @@ -511,44 +540,30 @@ def websockify_init(): # User supplied a local socket file. syslog_dest = os.path.abspath(opts.syslog) - from logging.handlers import SysLogHandler + from websockify.sysloghandler import WebsockifySysLogHandler # Determine syslog facility. if opts.daemon: - syslog_facility = SysLogHandler.LOG_DAEMON + syslog_facility = WebsockifySysLogHandler.LOG_DAEMON else: - syslog_facility = SysLogHandler.LOG_USER + syslog_facility = WebsockifySysLogHandler.LOG_USER # Start logging to syslog. - syslog_handler = SysLogHandler(address=syslog_dest, facility=syslog_facility) + 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) - # 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") - - # Transform to absolute path as daemon may chdir if opts.target_cfg: opts.target_cfg = os.path.abspath(opts.target_cfg)