From 69806c0dcae72ef51485bccd8d8d4185ac4eecaa Mon Sep 17 00:00:00 2001 From: mib1185 Date: Sat, 14 Sep 2024 11:40:40 +0000 Subject: [PATCH] add tls support --- README.md | 2 +- syslog/CHANGELOG.md | 4 ++ syslog/DOCS.md | 6 ++- syslog/config.yaml | 6 ++- syslog/journal2syslog.py | 88 +++++++++++++++++++++++++++++++++++-- syslog/run.sh | 4 ++ syslog/translations/de.yaml | 6 +++ syslog/translations/en.yaml | 6 +++ 8 files changed, 116 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index fdcf808..414df01 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ ![Supports aarch64 Architecture][aarch64-shield] ![Supports amd64 Architecture][amd64-shield] ![Supports armhf Architecture][armhf-shield] ![Supports armv7 Architecture][armv7-shield] ![Supports i386 Architecture][i386-shield] - ... to send your HAOS logs to a remote syslog server + ... to send your HAOS logs to a remote syslog server. UDP and TCP transport is support, as also TLS encrypted transport. ## How to use this repository diff --git a/syslog/CHANGELOG.md b/syslog/CHANGELOG.md index f0eb645..5cadc91 100644 --- a/syslog/CHANGELOG.md +++ b/syslog/CHANGELOG.md @@ -1,5 +1,9 @@ # Changelog +## 0.3.0 + +- Add tls support + ## 0.2.0 - Determine correct syslog level for ha core, supervisor and haos host messages diff --git a/syslog/DOCS.md b/syslog/DOCS.md index 51e4543..ab3d0b8 100644 --- a/syslog/DOCS.md +++ b/syslog/DOCS.md @@ -12,6 +12,8 @@ Add-on configuration: syslog_host: syslog.local syslog_port: 514 syslog_protocol: udp +syslog_ssl: false +syslog_ssl_verfify: false ``` | key | name | description | @@ -19,9 +21,11 @@ syslog_protocol: udp | `syslog_host` | Syslog host | The hostname or IP address of the remote syslog server to send HAOS logs to. | | `syslog_port` | Syslog port | The port of the remote syslog server to send HAOS logs to. | | `syslog_protocol` | Transfer protocol | The protocol to be used to send HAOS logs. | +| `syslog_ssl` | SSL encryption | Whether or not to to use ssl encryption (only supported with tcp). | +| `syslog_ssl_verfify` | SSL verify | Whether or not to verify ssl certificate. | ## Support -In case you've found a bug, please [open an issue on our GitHub][issue]. +In case you've found a bug, please [open an issue on GitHub][issue]. [issue]: https://github.com/mib1185/ha-addon-syslog/issues diff --git a/syslog/config.yaml b/syslog/config.yaml index e23f377..58372ee 100644 --- a/syslog/config.yaml +++ b/syslog/config.yaml @@ -1,5 +1,5 @@ name: "Syslog" -version: "0.2.0" +version: "0.3.0" slug: "syslog" description: "Send your HAOS logs to a remote syslog server" url: "https://github.com/mib1185/ha-addon-syslog" @@ -17,7 +17,11 @@ options: syslog_host: syslog.local syslog_port: 514 syslog_protocol: udp + syslog_ssl: false + syslog_ssl_verify: true schema: syslog_host: str syslog_port: int syslog_protocol: list(udp|tcp) + syslog_ssl: bool + syslog_ssl_verify: bool diff --git a/syslog/journal2syslog.py b/syslog/journal2syslog.py index 67f1ab6..db97c03 100644 --- a/syslog/journal2syslog.py +++ b/syslog/journal2syslog.py @@ -2,14 +2,18 @@ import logging import logging.handlers -from os import environ import re import socket +import ssl +from os import environ + from systemd import journal SYSLOG_HOST = str(environ["SYSLOG_HOST"]) SYSLOG_PORT = int(environ["SYSLOG_PORT"]) SYSLOG_PROTO = str(environ["SYSLOG_PROTO"]) +SYSLOG_SSL = True if environ["SYSLOG_SSL"] == "true" else False +SYSLOG_SSL_VERIFY = True if environ["SYSLOG_SSL_VERIFY"] == "true" else False HAOS_HOSTNAME = str(environ["HAOS_HOSTNAME"]) LOGGING_NAME_TO_LEVEL_MAPPING = logging.getLevelNamesMapping() @@ -33,6 +37,78 @@ } +class TlsSysLogHandler(logging.handlers.SysLogHandler): + def __init__( + self, + address: tuple[str, int] + | str = ("localhost", logging.handlers.SYSLOG_UDP_PORT), + facility: str | int = logging.handlers.SysLogHandler.LOG_USER, + socktype: logging.handlers.SocketKind | None = None, + ssl: bool | ssl.SSLContext = False, + ) -> None: + self.ssl = ssl + if ssl and socktype != socket.SOCK_STREAM: + raise RuntimeError("TLS is only support for TCP connections") + super().__init__(address, facility, socktype) + + def _wrap_sock_ssl(self, sock: socket.socket, host: str): + """Wrap a tcp socket into a ssl context.""" + if isinstance(self.ssl, ssl.SSLContext): + context = self.ssl + else: + context = ssl.create_default_context() + + return context.wrap_socket(sock, server_hostname=host) + + def createSocket(self): + """ + Try to create a socket and, if it's not a datagram socket, connect it + to the other end. This method is called during handler initialization, + but it's not regarded as an error if the other end isn't listening yet + --- the method will be called again when emitting an event, + if there is no socket at that point. + """ + address = self.address + socktype = self.socktype + + if isinstance(address, str): + self.unixsocket = True + # Syslog server may be unavailable during handler initialisation. + # C's openlog() function also ignores connection errors. + # Moreover, we ignore these errors while logging, so it's not worse + # to ignore it also here. + try: + self._connect_unixsocket(address) + except OSError: + pass + else: + self.unixsocket = False + if socktype is None: + socktype = socket.SOCK_DGRAM + host, port = address + ress = socket.getaddrinfo(host, port, 0, socktype) + if not ress: + raise OSError("getaddrinfo returns an empty list") + for res in ress: + af, socktype, proto, _, sa = res + err = sock = None + try: + sock = socket.socket(af, socktype, proto) + if self.ssl: + sock = self._wrap_sock_ssl(sock, host) + if socktype == socket.SOCK_STREAM: + sock.connect(sa) + break + except (OSError, ssl.SSLError) as exc: + err = exc + if sock is not None: + sock.close() + if err is not None: + raise err + self.socket = sock + self.socktype = socktype + + def parse_log_level(message: str, container_name: str) -> int: """ Try to determine logging level from message @@ -64,8 +140,14 @@ def parse_log_level(message: str, container_name: str) -> int: else: socktype = socket.SOCK_STREAM -syslog_handler = logging.handlers.SysLogHandler( - address=(SYSLOG_HOST, SYSLOG_PORT), socktype=socktype +use_ssl = SYSLOG_SSL +if SYSLOG_SSL and not SYSLOG_SSL_VERIFY: + use_ssl = ssl.create_default_context() + use_ssl.check_hostname = False + use_ssl.verify_mode = ssl.CERT_NONE + +syslog_handler = TlsSysLogHandler( + address=(SYSLOG_HOST, SYSLOG_PORT), socktype=socktype, ssl=use_ssl ) formatter = logging.Formatter( f"%(asctime)s %(ip)s %(prog)s: %(message)s", diff --git a/syslog/run.sh b/syslog/run.sh index bf30721..d31f4b7 100755 --- a/syslog/run.sh +++ b/syslog/run.sh @@ -8,6 +8,10 @@ SYSLOG_PORT=$(bashio::config 'syslog_port') export SYSLOG_PORT SYSLOG_PROTO=$(bashio::config 'syslog_protocol') export SYSLOG_PROTO +SYSLOG_SSL=$(bashio::config 'syslog_ssl') +export SYSLOG_SSL +SYSLOG_SSL_VERIFY=$(bashio::config 'syslog_ssl_verify') +export SYSLOG_SSL_VERIFY HAOS_HOSTNAME=$(bashio::info.hostname) export HAOS_HOSTNAME diff --git a/syslog/translations/de.yaml b/syslog/translations/de.yaml index 6c3b417..77b6674 100644 --- a/syslog/translations/de.yaml +++ b/syslog/translations/de.yaml @@ -8,3 +8,9 @@ configuration: syslog_protocol: name: Übertragungsprotokol description: Das Übertragungsprotokol welches zum Senden der HAOS-Logs verwendet werden soll. + syslog_ssl: + name: SSL Verschlüsselung + description: Aktivieren oder deaktivieren der SSL Verschlüsselung (wird nur bei tcp unterstützt). + syslog_ssl_verify: + name: SSL-Zertifikat überprüfen + description: Prüfung des SSL-Zertifikate aktivieren oder dekativieren. diff --git a/syslog/translations/en.yaml b/syslog/translations/en.yaml index 2652b94..7db1df7 100644 --- a/syslog/translations/en.yaml +++ b/syslog/translations/en.yaml @@ -8,3 +8,9 @@ configuration: syslog_protocol: name: Transfer protocol description: The protocol to be used to send HAOS logs. + syslog_ssl: + name: SSL encryption + description: Whether or not to to use ssl encryption (only supported with tcp). + syslog_ssl_verify: + name: SSL verify + description: Whether or not to verify ssl certificate.