From 16e1724f3137f65f464fdaa554316700bd33979a Mon Sep 17 00:00:00 2001 From: Daniel afx Date: Sat, 30 Jan 2021 04:24:55 +0200 Subject: [PATCH] split local and discord destinations and add network security --- .gitignore | 1 + Dockerfile | 7 +++ README.md | 15 ++++-- discord.sh | 30 +++++++++++ docker-compose.yml | 22 ++++++++ etc/cont-init.d/10_syslog_ng | 36 ++++++++++--- etc/services.d/logmonitor/run | 6 +-- etc/syslog-ng/conf.d/options.conf | 4 +- etc/syslog-ng/syslog-ng.conf | 10 ++-- etc/syslog-ng/templates/d_discord.template | 13 +++++ etc/syslog-ng/templates/d_local.template | 8 ++- tools/README.md | 10 ++++ tools/syslog_client.py | 61 ++++++++++++++++++++++ 13 files changed, 199 insertions(+), 24 deletions(-) create mode 100644 .gitignore create mode 100644 discord.sh create mode 100644 docker-compose.yml create mode 100644 etc/syslog-ng/templates/d_discord.template create mode 100644 tools/README.md create mode 100644 tools/syslog_client.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..0d20b64 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +*.pyc diff --git a/Dockerfile b/Dockerfile index 97d3298..7410597 100644 --- a/Dockerfile +++ b/Dockerfile @@ -3,6 +3,8 @@ FROM alpine:3 ENV S6_OVERLAY_VERSION 2.2.0.1 ENV S6_OVERLAY_MD5HASH a114568c94d06dc69fdb9d91ed3f7535 +RUN apk add --no-cache curl + RUN apk add --no-cache wget ca-certificates && \ apk --no-cache --update upgrade && \ cd /tmp && \ @@ -22,8 +24,13 @@ RUN apk add --no-cache \ COPY /etc/ /etc/ +COPY discord.sh /bin/discord.sh +RUN chmod +x /bin/discord.sh + EXPOSE 514/udp EXPOSE 601/tcp EXPOSE 6514/tcp +VOLUME /var/log + ENTRYPOINT ["/init"] \ No newline at end of file diff --git a/README.md b/README.md index 4dc72ff..58e2a7e 100644 --- a/README.md +++ b/README.md @@ -7,16 +7,23 @@ A small Alpine container running syslog-ng configured to log to `/var/log/messag Basic usage with the default local destination: +``` +docker build -t logmonitor . +docker run --env WEBHOOK=https://discordapp.com/api/webhooks/... -p 514:514/udp -d --name logmonitor --rm logmonitor:latest ; docker logs logmonitor --follow +docker stop logmonitor ; docker rm logmonitor +``` + ``` docker run -d --name syslog-ng -p 514:514/udp -p 601:601/tcp -p 6514:6514/tcp logmonitor ``` -Destinations can be enabled or disabled with environment variables specified with `-e`. - - ### Environment variables -* `ENABLE_LOCAL` - set `True` to log to `/var/log/messages` in container (default: `False`) +* `ENABLE_LOCAL` - set `True` to log to `/var/log/system.log` in container (default: `False`) +* `ENABLE_DISCORD` - set `True` to log to `Discord` in container (default: `False`) + +* `ALLOWED_SUBNET` - Define allowed network. (default: `10.0.0.0/24`) + * `SQL_HOST` - the IP or domain of the destination SQL server * `SQL_PORT` - the port the destination SQL server runs on (defaults to `3306` if not specified) * `SQL_USER` - the user name used to access the destination SQL server diff --git a/discord.sh b/discord.sh new file mode 100644 index 0000000..0f802c1 --- /dev/null +++ b/discord.sh @@ -0,0 +1,30 @@ +#!/bin/sh + +source /.envvars +SERVICE_URL="https://localhost:8443/" + +# color codes examples: https://gist.github.com/deflax/e4fe4cced12103819c2663e2369911a5 + +while read LINE ; do + PRIO=`echo ${LINE} | cut -d ' ' -f 1` + if [ "${PRIO}" = "27" ] ; then + #test client notice - dark green + COLOR=2067276 + elif [ "${PRIO}" = "134" ] ; then + #hpilo notice - grey + COLOR=9807270 + else + #fallback to dark orange + COLOR=11027200 + fi + SUBJECT=`echo ${LINE} | cut -d ' ' -f 2` + MESSAGE=`echo ${LINE} | cut -d ' ' -f 3-` + + PAYLOAD=`printf "{ \"embeds\": [{\"title\": \"%s\", \"url\": \"%s\", \"description\": \"%s\", \"type\": \"link\", \"color\": %s }] }" "${SUBJECT}" "${SERVICE_URL}" "${MESSAGE}" "${COLOR}"` + echo ${PAYLOAD} > /var/log/discord.payload + HTTP_RESPONSE=`curl --write-out %{http_code} --silent --output /dev/null -X POST -H "Content-Type: application/json" -d @/var/log/discord.payload ${DISCORD_WEBHOOK}` + + if [[ "${HTTP_RESPONSE}" != "204" ]] ; then + echo "Received HTTP-Code: ${HTTP_RESPONSE}" >> /var/log/discord.log + fi +done \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..df0c68c --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,22 @@ +version: "3" + +services: + logmonitor: + image: logmonitor:latest + ports: + - "514:514/udp" + volumes: + - "./data/logmonitor/log:/var/log" + environment: + ENABLE_LOCAL: true + ENABLE_DISCORD: true + DISCORD_WEBHOOK: https://discordapp.com/api/webhooks/CHANGEME + ALLOWED_SUBNET: 10.0.0.0/24 + restart: always + networks: + - internal + labels: + - meta.role=logmonitor + +networks: + internal: {} \ No newline at end of file diff --git a/etc/cont-init.d/10_syslog_ng b/etc/cont-init.d/10_syslog_ng index 5423e66..7ecc594 100644 --- a/etc/cont-init.d/10_syslog_ng +++ b/etc/cont-init.d/10_syslog_ng @@ -3,7 +3,7 @@ CONFD=/etc/syslog-ng/conf.d TEMPLATES=/etc/syslog-ng/templates -# enable and configure the SQL destination if SQL_* environment variables are set +# Setup the SQL destination if SQL_* environment variables are set if $(env | grep -q SQL); then if [ -z ${SQL_PORT+set} ]; then @@ -23,8 +23,8 @@ elif [ -f ${CONFD}/d_sql.conf ]; then rm -f ${CONFD}/d_sql.conf fi -DO_ENABLE_LOCAL=true - +# Setup local template +DO_ENABLE_LOCAL=false if [ ! -z ${ENABLE_LOCAL+set} ]; then case $ENABLE_LOCAL in true|True|TRUE|yes|Yes|YES|1|on|On|ON) @@ -32,12 +32,34 @@ if [ ! -z ${ENABLE_LOCAL+set} ]; then ;; esac fi - -# enable the local destination if the appropriate environment variable is set if ${DO_ENABLE_LOCAL}; then - echo "Logging to /var/log/messages ENABLED." + echo "Logging to Local ENABLED." cp --remove-destination ${TEMPLATES}/d_local.template ${CONFD}/d_local.conf + else # otherwise make sure it's disabled - echo "Logging to /var/log/messages DISABLED." + echo "Logging to Local DISABLED." rm -f ${CONFD}/d_local.conf fi + +# Setup discord template +DO_ENABLE_DISCORD=false +if [ ! -z ${ENABLE_DISCORD+set} ]; then + case $ENABLE_DISCORD in + true|True|TRUE|yes|Yes|YES|1|on|On|ON) + DO_ENABLE_DISCORD=true + ;; + esac +fi +if ${DO_ENABLE_DISCORD}; then + echo "Logging to Discord ENABLED." + cp --remove-destination ${TEMPLATES}/d_discord.template ${CONFD}/d_discord.conf + + if [ -z ${ALLOWED_SUBNET+set} ]; then + ALLOWED_SUBNET="10.0.0.0/24" + fi + echo "Allowed network subnet is ${ALLOWED_SUBNET}" + sed -i "s:ALLOWED_SUBNET:${ALLOWED_SUBNET}:" ${CONFD}/d_discord.conf +else # otherwise make sure it's disabled + echo "Logging to Discord DISABLED." + rm -f ${CONFD}/d_discord.conf +fi \ No newline at end of file diff --git a/etc/services.d/logmonitor/run b/etc/services.d/logmonitor/run index 7b23ce9..ed9e199 100644 --- a/etc/services.d/logmonitor/run +++ b/etc/services.d/logmonitor/run @@ -1,8 +1,8 @@ -#!/bin/sh +#!/usr/bin/with-contenv sh while true do - echo "] ping from logmonitor..." - sleep 10 + echo "DISCORD_WEBHOOK=${DISCORD_WEBHOOK}" > /.envvars + sleep 3600 done diff --git a/etc/syslog-ng/conf.d/options.conf b/etc/syslog-ng/conf.d/options.conf index fdb24da..e063673 100755 --- a/etc/syslog-ng/conf.d/options.conf +++ b/etc/syslog-ng/conf.d/options.conf @@ -4,6 +4,6 @@ options { keep_hostname(yes); create_dirs(yes); ts_format(iso); - time_reopen (10); - chain_hostnames (no); + time_reopen(10); + chain_hostnames(no); }; diff --git a/etc/syslog-ng/syslog-ng.conf b/etc/syslog-ng/syslog-ng.conf index 41eb880..49691bb 100755 --- a/etc/syslog-ng/syslog-ng.conf +++ b/etc/syslog-ng/syslog-ng.conf @@ -18,7 +18,11 @@ source s_local { }; source s_network { - default-network-drivers( + syslog(transport("udp")); +}; + +#source s_network { +# default-network-drivers( # NOTE: TLS support # # the default-network-drivers() source driver opens the TLS @@ -30,8 +34,8 @@ source s_network { # key-file("/etc/syslog-ng/certs/serverkey.pem") # cert-file("/etc/syslog-ng/certs/servercert.pem") # ) - ); -}; +# ); +#}; ### # Include all config files in /etc/syslog-ng/conf.d/ diff --git a/etc/syslog-ng/templates/d_discord.template b/etc/syslog-ng/templates/d_discord.template new file mode 100644 index 0000000..32e77b5 --- /dev/null +++ b/etc/syslog-ng/templates/d_discord.template @@ -0,0 +1,13 @@ +destination d_discord { + program("/bin/discord.sh" mark-mode(none) template("$PRI $HOST $MSGHDR $MSG\n")); +}; + +filter allowed_hosts { + netmask(ALLOWED_SUBNET); +}; + +log { + source(s_network); + filter(allowed_hosts); + destination(d_discord); +}; \ No newline at end of file diff --git a/etc/syslog-ng/templates/d_local.template b/etc/syslog-ng/templates/d_local.template index b0daa38..8c6eabf 100644 --- a/etc/syslog-ng/templates/d_local.template +++ b/etc/syslog-ng/templates/d_local.template @@ -1,11 +1,9 @@ destination d_local { - file("/var/log/messages"); - file("/var/log/messages-kv.log" template("$ISODATE $HOST $(format-welf --scope all-nv-pairs)\n") frac-digits(3)); + file("/var/log/system.log" template("$ISODATE $PRI $HOST $MSGHDR $MSG\n")); + #file("/var/log/messages-kv.log" template("$ISODATE $HOST $(format-welf --scope all-nv-pairs)\n") frac-digits(3)); }; log { - source(s_local); source(s_network); destination(d_local); -}; - +}; \ No newline at end of file diff --git a/tools/README.md b/tools/README.md new file mode 100644 index 0000000..4c5eab2 --- /dev/null +++ b/tools/README.md @@ -0,0 +1,10 @@ +``` +import syslog_client +log = syslog_client.Syslog("127.0.0.1") +log.notice("info msg") +log.warn("warning msg") +log.error("error msg") +``` +``` +log.send("howdy", syslog_client.WARNING) +``` diff --git a/tools/syslog_client.py b/tools/syslog_client.py new file mode 100644 index 0000000..ce8de44 --- /dev/null +++ b/tools/syslog_client.py @@ -0,0 +1,61 @@ +""" +Remote syslog client. + +Works by sending UDP messages to a remote syslog server. The remote server +must be configured to accept logs from the network. + +License: PUBLIC DOMAIN +Author: Christian Stigen Larsen + +For more information, see RFC 3164. +""" + +import socket + +class Facility: + "Syslog facilities" + KERN, USER, MAIL, DAEMON, AUTH, SYSLOG, \ + LPR, NEWS, UUCP, CRON, AUTHPRIV, FTP = range(12) + + LOCAL0, LOCAL1, LOCAL2, LOCAL3, \ + LOCAL4, LOCAL5, LOCAL6, LOCAL7 = range(16, 24) + +class Level: + "Syslog levels" + EMERG, ALERT, CRIT, ERR, \ + WARNING, NOTICE, INFO, DEBUG = range(8) + +class Syslog: + """A syslog client that logs to a remote server. + + Example: + >>> log = Syslog(host="foobar.example") + >>> log.send("hello", Level.WARNING) + """ + def __init__(self, + host="localhost", + port=514, + facility=Facility.DAEMON): + self.host = host + self.port = port + self.facility = facility + self.socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + + def send(self, message, level): + "Send a syslog message to remote host using UDP." + data = "<%d>%s" % (level + self.facility*8, message) + self.socket.sendto(data, (self.host, self.port)) + + def warn(self, message): + "Send a syslog warning message." + self.send(message, Level.WARNING) + + def notice(self, message): + "Send a syslog notice message." + self.send(message, Level.NOTICE) + + def error(self, message): + "Send a syslog error message." + self.send(message, Level.ERR) + + # ... add your own stuff here \ No newline at end of file