Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add tls support #9

Merged
merged 1 commit into from
Sep 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
4 changes: 4 additions & 0 deletions syslog/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -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
Expand Down
6 changes: 5 additions & 1 deletion syslog/DOCS.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,16 +12,20 @@ Add-on configuration:
syslog_host: syslog.local
syslog_port: 514
syslog_protocol: udp
syslog_ssl: false
syslog_ssl_verfify: false
```

| key | name | description |
| --- | ---- | ----------- |
| `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
6 changes: 5 additions & 1 deletion syslog/config.yaml
Original file line number Diff line number Diff line change
@@ -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"
Expand All @@ -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
88 changes: 85 additions & 3 deletions syslog/journal2syslog.py
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand All @@ -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
Expand Down Expand Up @@ -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",
Expand Down
4 changes: 4 additions & 0 deletions syslog/run.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
6 changes: 6 additions & 0 deletions syslog/translations/de.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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.
6 changes: 6 additions & 0 deletions syslog/translations/en.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Loading