Skip to content

Commit

Permalink
syslog-ng: add webhook() and webhook-json() sources
Browse files Browse the repository at this point in the history
Signed-off-by: László Várady <laszlo.varady@axoflow.com>
  • Loading branch information
MrAnno committed Apr 27, 2024
1 parent 80c963b commit 42c85fd
Show file tree
Hide file tree
Showing 9 changed files with 278 additions and 4 deletions.
9 changes: 9 additions & 0 deletions .github/workflows/syslog-ng-docker.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ on:
tarball-artifact:
required: false
type: string
axosyslog-modules-artifact:
required: true
type: string
snapshot-version:
required: false
type: string
Expand All @@ -34,6 +37,12 @@ jobs:
name: ${{ inputs.tarball-artifact }}
path: syslog-ng/apkbuild/axoflow/syslog-ng

- name: Download axosyslog-modules tarball artifact
uses: actions/download-artifact@v4
with:
name: ${{ inputs.axosyslog-modules-artifact }}
path: syslog-ng/apkbuild/axoflow/syslog-ng

- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
Expand Down
31 changes: 29 additions & 2 deletions .github/workflows/syslog-ng-snapshot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -59,9 +59,29 @@ jobs:
name: source-tarball
path: dbld/build/*.tar.*

axosyslog-modules:
name: axosyslog-modules
runs-on: ubuntu-latest
steps:
- name: Checkout source
uses: actions/checkout@v4

- name: Create AxoSyslog modules tarball
working-directory: syslog-ng
run: |
tar -czvf python-modules.tar.gz python-modules/
- name: Store axosyslog-modules tarball as artifact
uses: actions/upload-artifact@v4
with:
name: axosyslog-modules-tarball
path: syslog-ng/python-modules.tar.gz

build-and-test:
runs-on: ubuntu-latest
needs: tarball
needs:
- tarball
- axosyslog-modules
steps:
- name: Checkout source
uses: actions/checkout@v4
Expand All @@ -72,6 +92,12 @@ jobs:
name: source-tarball
path: syslog-ng/apkbuild/axoflow/syslog-ng

- name: Download axosyslog-modules tarball artifact
uses: actions/download-artifact@v4
with:
name: ${{ inputs.axosyslog-modules-artifact }}
path: syslog-ng/apkbuild/axoflow/syslog-ng

- name: Build and export Docker image
uses: docker/build-push-action@v5
with:
Expand All @@ -93,10 +119,11 @@ jobs:
publish-image:
if: github.ref == 'refs/heads/main'
uses: ./.github/workflows/syslog-ng-docker.yml
needs: [tarball, build-and-test]
needs: [tarball, build-and-test, axosyslog-modules]
with:
pkg-type: nightly
tarball-artifact: source-tarball
axosyslog-modules-artifact: axosyslog-modules-tarball
snapshot-version: ${{ needs.tarball.outputs.snapshot-version }}

# https://github.com/actions/delete-package-versions/issues/90
Expand Down
22 changes: 22 additions & 0 deletions .github/workflows/syslog-ng-stable.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,24 @@ jobs:
outputs:
version: ${{ steps.unpack_tag.outputs.group1 }}

axosyslog-modules:
name: axosyslog-modules
runs-on: ubuntu-latest
steps:
- name: Checkout source
uses: actions/checkout@v4

- name: Create AxoSyslog modules tarball
working-directory: syslog-ng
run: |
tar -czvf python-modules.tar.gz python-modules/
- name: Store axosyslog-modules tarball as artifact
uses: actions/upload-artifact@v4
with:
name: axosyslog-modules-tarball
path: syslog-ng/python-modules.tar.gz

publish-packages:
uses: ./.github/workflows/syslog-ng-packages.yml
needs:
Expand All @@ -33,5 +51,9 @@ jobs:

publish-image:
uses: ./.github/workflows/syslog-ng-docker.yml
needs:
- prepare
- axosyslog-modules
with:
pkg-type: stable
axosyslog-modules-artifact: axosyslog-modules-tarball
2 changes: 1 addition & 1 deletion syslog-ng/alpine.dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ RUN mkdir packages \
&& printf 'export JOBS=$(nproc)\nexport MAKEFLAGS=-j$JOBS\n' >> .abuild/abuild.conf \
&& cd axoflow/syslog-ng \
&& if [ "$PKG_TYPE" = "nightly" ]; then \
tarball_filename="$(ls *.tar.*)"; \
tarball_filename="$(ls syslog-ng-*.tar.*)"; \
[ -z "$tarball_filename" ] && echo "Tarball for nightly can not be found" && exit 1; \
tarball_name="${tarball_filename/\.tar.*}"; \
sed -i -e "s|^pkgver=.*|pkgver=$SNAPSHOT_VERSION|" -e "s|^builddir=.*|builddir=\"\$srcdir/$tarball_name\"|" APKBUILD; \
Expand Down
8 changes: 7 additions & 1 deletion syslog-ng/apkbuild/axoflow/syslog-ng/APKBUILD
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,9 @@ subpackages="
$pkgname-python3:_python3
$pkgname-grpc:_grpc
"
source="https://github.com/syslog-ng/syslog-ng/releases/download/syslog-ng-$pkgver/syslog-ng-$pkgver.tar.gz"
source="https://github.com/syslog-ng/syslog-ng/releases/download/syslog-ng-$pkgver/syslog-ng-$pkgver.tar.gz
python-modules.tar.gz
"
builddir="$srcdir/$pkgname-$pkgver"

_modules="
Expand Down Expand Up @@ -145,6 +147,9 @@ _python3() {
depends="$pkgname=$pkgver-r$pkgrel python3"
install="$subpkgname.post-install"

cat $srcdir/python-modules/requirements.txt >> usr/lib/syslog-ng/python/requirements.txt
rm $srcdir/python-modules/requirements.txt
mv $srcdir/python-modules/* usr/lib/syslog-ng/python/syslogng/modules
_submv usr/lib/syslog-ng/libmod-python.so usr/lib/syslog-ng/python usr/bin/syslog-ng-update-virtualenv
}

Expand Down Expand Up @@ -174,4 +179,5 @@ _submv() {

sha512sums="
2f1e0dea4c0ecfc3c77df7e6ac231ee8436c9c78fcb4df8ccdc417fea7d56791fdeb0844ac35f0342ce7c2bea5618d8723b6b54319c556120099eb809873082e syslog-ng-4.7.1.tar.gz
SKIP python-modules.tar.gz
"
1 change: 1 addition & 0 deletions syslog-ng/python-modules/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
tornado==6.4
5 changes: 5 additions & 0 deletions syslog-ng/python-modules/webhook/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
from .source import HTTPSource

__all__ = [
"HTTPSource"
]
65 changes: 65 additions & 0 deletions syslog-ng/python-modules/webhook/scl/webhook.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
#############################################################################
# Copyright (c) 2024 László Várady
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License version 2 as published
# by the Free Software Foundation, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
#
# As an additional exemption you are allowed to compile & link against the
# OpenSSL libraries as published by the OpenSSL project. See the file
# COPYING for details.
#
#############################################################################

block source webhook(
port("")
auth_token("")
tls_key_file("")
tls_cert_file("")
...
)
{
python(
class("syslogng.modules.http.HTTPSource")
options(
"port" => "`port`"
"auth_token" => "`auth_token`"
"tls_key_file" => "`tls_key_file`"
"tls_cert_file" => "`tls_cert_file`"
)
`__VARARGS__`
);
};

block source webhook-json(
port("")
auth_token("")
tls_key_file("")
tls_cert_file("")
prefix("")
...
)
{
channel {
source {
webhook(
port("`port`") auth_token("`auth_token`")
tls_key_file("`tls_key_file`") tls_cert_file("`tls_cert_file`")
`__VARARGS__`
);
};

parser {
json-parser(prefix("`prefix`"));
};
};
};
139 changes: 139 additions & 0 deletions syslog-ng/python-modules/webhook/source.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
#############################################################################
# Copyright (c) 2024 László Várady
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License version 2 as published
# by the Free Software Foundation, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
#
# As an additional exemption you are allowed to compile & link against the
# OpenSSL libraries as published by the OpenSSL project. See the file
# COPYING for details.
#
#############################################################################

from syslogng import LogSource, LogMessage

import logging
import asyncio
import threading
import tornado
import ssl
import signal
from typing import Any

signal.signal(signal.SIGINT, signal.SIG_IGN)
signal.signal(signal.SIGTERM, signal.SIG_IGN)

class Handler(tornado.web.RequestHandler):
def initialize(self, source) -> None:
self.source = source

@tornado.web.authenticated
async def post(self) -> None:
# racy, but the client should retry
if self.source.suspended.is_set():
self.set_status(503)
await self.finish({"status": "flow-controlled"})
return

self.source.post_message(LogMessage(self.request.body))
await self.finish({"status": "received"})

def set_default_headers(self) -> None:
self.set_header("Server", "syslog-ng");

def get_current_user(self):
if not self.source.auth_token:
return True

token = self.request.headers.get("Authorization", "").split(" ")
if len(token) != 2:
return False

token = token[1]
return self.source.auth_token == token

def write_error(self, status_code: int, **kwargs: Any) -> None:
self.set_status(status_code)

class HTTPSource(LogSource):
def init(self, options: dict[str, Any]) -> bool:
self.logger = logging.getLogger("http")
self.set_transport_name("http")
if not self.init_options(options):
return False

self.ssl_ctx = None
if self.tls_key_file:
self.ssl_ctx = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH)
self.ssl_ctx.load_cert_chain(self.tls_cert_file, self.tls_key_file)

if not self.port:
self.port = 443 if self.tls_key_file else 80

self.port = int(self.port)

self.suspended = threading.Event()
self.event_loop = asyncio.new_event_loop()
self.request_exit = asyncio.Event()
self.app = tornado.web.Application(
[
(r"/.*", Handler, {"source": self}),
],
log_function=self.log_access,
autoreload=False,
compress_response=False,
)
self.server = None

return True

async def runServer(self) -> None:
self.logger.info(f"HTTP(S) server started on port {self.port}")
self.server = self.app.listen(self.port, decompress_request=True, ssl_options=self.ssl_ctx)
await self.request_exit.wait()

self.server.stop()
await self.server.close_all_connections()

async def stopServer(self) -> None:
self.request_exit.set()

def deinit(self) -> None:
pass

def run(self) -> None:
self.event_loop.run_until_complete(self.runServer())

def suspend(self) -> None:
self.suspended.set()

def wakeup(self) -> None:
self.suspended.clear()

def request_exit(self) -> None :
asyncio.run_coroutine_threadsafe(self.stopServer(), self.event_loop)
pass

def log_access(self, req: tornado.web.RequestHandler):
self.logger.debug(f"{req.get_status()} {req._request_summary()}")

def init_options(self, options: dict[str, Any]) -> bool:
try:
self.port = options.get("port")
self.auth_token = options.get("auth_token")
self.tls_key_file = options.get("tls_key_file")
self.tls_cert_file = options.get("tls_cert_file")
return True
except KeyError as e:
self.logger.error(f"Missing option '{e.args[0]}'")
return False

0 comments on commit 42c85fd

Please sign in to comment.