Graylog
Siehe auch
- Ansible-Rolle Graylog-Server:
Stand 2023-06 gibt es noch keine offizielle EOL-Regelung. In der Regel werden die letzten drei dot-Releases unterstützt, also z.B. 5.2, 5.1, 5.0. Dies kann sich je nach Versionsnummer mit Major Releases überschneiden (Beispiel 5.1., 5.0, 4.3). Etwa alle 4 bis 6 Monate erscheint ein neues dot-Release.
Es gibt keine Releases, die als besonders stabil oder instabil gelten. Graylog führt daher auch keine LTS-Versionen. Vor einer intensiven Fehleranalyse wird empfohlen, auf eine neuere Version zu aktualisieren.
Graylog Cloud für Europa wird von AWS in Frankfurt gehostet.
Installation
Bemerkung
Unbedingt die zu installierenden Versionen der abhängigen Software-Pakete beachten: https://go2docs.graylog.org/current/downloading_and_installing_graylog/installing_graylog.html
Graylog kann als Linux-Paket (deb, rpm) oder als Docker-Container (docker-compose, Kubernetes) installiert werden.
Minimum:
2x CPUs
6 GB RAM, besser 8 GB
8 GB Platz auf
/
Hier wird die Installation von Graylog 6.0.x beschrieben.
dnf -y install epel-release
dnf -y install dnf-plugin-versionlock
MongoDB 7.x (inkl. Pinning):
Installation siehe MongoDB
Nach der Installation ZWINGEND ausführen:
dnf versionlock add mongodb-org
OpenSearch 2.15.x (inkl. Pinning):
Installation siehe OpenSearch
Nach der Installation ZWINGEND ausführen:
dnf versionlock add opensearch
OpenSearch für Graylog konfigurieren:
# for a minimum unsecured running state, single node cluster.name: graylog node.name: ${HOSTNAME} path.data: /var/lib/opensearch path.logs: /var/log/opensearch discovery.type: single-node network.host: 0.0.0.0 action.auto_create_index: false plugins.security.disabled: true indices.query.bool.max_clause_count: 32768
Die eigentliche Installation der Graylog Community Edition kann beginnen:
rpm --upgrade --verbose --hash https://packages.graylog2.org/repo/packages/graylog-6.0-repository_latest.rpm
dnf -y install graylog-server
dnf versionlock add graylog-server
Konfiguration - es müssen password_secret
und root_password_sha2
generiert und konfiguriert werden.
password_secret
(Passwort für alle Graylog-Nodes im Cluster):< /dev/urandom tr -dc A-Z-a-z-0-9 | head -c${1:-96};echo;
root_password_sha2
:echo -n "Enter Password: " && head -1 </dev/stdin | tr -d '\n' | sha256sum | cut -d" " -f1
Dann:
password_secret = 3JJgCxO...
root_username = graylog-admin
root_password_sha2 = 0621e81...
http_bind_address = 0.0.0.0:9000
elasticsearch_hosts = https://127.0.0.1:9200
elasticsearch_version_probe_attempts = 10
mongodb_version_probe_attempts = 10
SELinux:
setsebool -P httpd_can_network_connect=on
dnf -y install policycoreutils-python-utils
semanage port --add --type=mongod_port_t --proto=tcp 27017
systemctl daemon-reload
systemctl enable --now graylog-server.service
tail -f /var/log/opensearch/opensearch.log
Nach der Installation verfügbare Ports:
9000 (tcp): Graylog-GUI und API-Browser per Swagger UI
9200 (tcp): Elasticsearch/OpenSearch
9300 (tcp): Elasticsearch/OpenSearch Node Communication
27017 (tcp): MongoDB
Port 9000 nach aussen öffnen.
Achtung
Wird Graylog das erste mal gestartet, findet sich der Link zum Login inkl. Initial-Passwort in /var/log/graylog-server/server.log
.
Konfiguration
Neues Index-Set mit alternativen Retention Times / Retention Policies einrichten: System / Indices / Indices > Default Index Set > Edit; als Default Index Set einrichten
Neuen Stream erstellen: Streams > Create stream, Remove matches from ‘Default Stream’, neues Index Set verwenden; Manage Rules > Add stream rule > Type: always match - so erhält der neue Stream alle Nachrichten in das neue Default Index set
Config Ex- und Import:
Export: System / Inputs > Content Packs: Create a content pack
Import: System / Inputs > Content Packs > Select content packs: Import content pack
Update
Vor dem Update sollte man sich die Upgrading und den Changelog anschauen (Achtung: richtige Version im Dropdown oben auswählen).
Generelles Vorgehen:
Graylog stoppen
Repo-File aktualisieren
Graylog Package aktualisieren
Config File prüfen
Graylog starten
Input: Syslog
Soll Graylog Logdaten entgegennehmen, müssen die unter „System > Inputs“ konfiguriert werden.
Für das althergebrachte Syslog Port 1514 (udp) verwenden und lokale Firewall öffnen.
Bemerkung
Graylog kann nicht auf Port 514 hören, das führt zu einem „Permission denied, Failed to bind to: ip-address:514“, da es sich hier um einen privilegierten Port handelt. Daher wie in allen Beispielen Port 1514 verwenden. Um einen Bind auf jede IP-Adresse einzurichten, 0.0.0.0
verwenden, *
funktioniert als Angabe nicht.
LDAP
Eine Gruppen-Synchronisation ist in Graylog Open nicht möglich (muss erworben werden). Eine bestimmte Gruppe kann beispielsweise auf folgende Art und Weise aus dem LDAP bezogen werden: (&(&(uid={0})(objectClass=inetOrgPerson))(memberOf=cn=MYGROUP,cn=groups,cn=accounts,dc=example,dc=org))
(in diesem Beispiel heisst die Gruppe MYGROUP
).
Nachrichtenformat
Welche Felder sind Standard und/oder Pflicht?
message
: das einzige Pflichtfeld, muss gesetzt werden; ohne gesetzte „message“ wird Nachricht nicht angenommen.
Felder, welche automatisch gesetzt werden:
source
(wenn nicht gesetzt, wird die IP des sendenden Hosts eingetragen (nicht die eines möglichen Docker-Containers))timestamp
(wenn nicht gesetzt, wird aktueller Timestamp verwendet; überschreibbar mit UNIX-Timestamp; falls falsch überschrieben, wird die Nachricht verworfen)
Message-IDs sind UUIDs/GUIDs: ee1bdfe2-4c23-11e7-9f76-525400ce74cd
.
Extractors
Unter System > Inputs > Extractors.
GROK-Patterns: Pattern Matching mit GROK orientiert sich an Oracles Java Pattern Class. Ein dazu passender Java Regex Tester findet sich hier.
Elasticsearch Index / Indizes
Ein Elasticsearch-„Index“ hat einen Namen, z.B. „graylog_0“.
Default Settings des „Default index set“:
Rotation strategy: 20000000
Retention strategy: Delete Index
Max number of indices: 20
Wer eigene Streams definiert, sollte darauf achten, den Haken „Remove matches from ‚All messages‘ stream“ zu setzen, damit Nachrichten nicht doppelt gespeichert werden.
Theroetisch kann man mit diesen curl-Aufrufen nach einer Änderung die Indices neu bauen (praktisch hat das bei uns nicht funktioniert - auch nach einem einfachen Reboot des Graylog-Servers waren die alten Indizes noch vorhanden):
# if Elastic is in read-only mode, switch it back to normal
curl --request PUT \
--header "Content-Type: application/json" \
--data '{"index.blocks.read_only_allow_delete": null}'
http://elastic.example.com:9200/_all/_settings
# start retention job
curl --request POST http://graylog.example.com:9000/api/system/indices/ranges/rebuild
Indizes aus Elasticsearch auslesen:
curl 'http://elastic.example.com:9200/_cat/indices?v'
Indizes-Statistiken:
curl 'http://elastic.example.com:9200/_stats'
Indizes leeren:
curl --request DELETE 'http://elastic.example.com:9200/_all'
Logging testen
Wichtig
Taucht in einer JSON-Nachricht an einen Syslog-Input der Key „level“ auf, wird die Nachricht von Graylog verworfen, sobald der dazu gehörende Value nicht kompatibel mit Syslog ist. Beispiel: „level“:“FINE“ wird verworfen, „level“:“5“ dagegen akzeptiert.
Syslog per 1514/udp:
logger --server graylog.example.com --udp --port 1514 'Test Syslog 1514/udp on '$(date)
Um GELF zu testen, wird ncat
benötigt.
dnf -y install nmap-ncat
GELF per 12201/UDP:
# shortest message possible
echo -e '{"message": "Test GELF 12201/udp on '$(date)'"}' | ncat --wait 1 --udp graylog.example.com 12201
# longer message, should split up into fields
echo -e '{
"version": "1.1",
"host": "example.org",
"short_message": "Test GELF 12201/udp on '$(date)'",
"full_message": "Backtrace here\nsome stuff\nmore stuff",
"level": 1,
"_user_id": 9001,
"some_info": "foo",
"some_env_var": "bar"
}' | ncat --wait 1 --udp graylog.example.com 12201
GELF per 12201/TCP:
echo -e '{
"version": "1.1",
"host": "example.org",
"short_message": "Test GELF 12201/tcp on '$(date)'",
"full_message": "Backtrace here\nsome stuff\nmore stuff",
"level": 1,
"_user_id": 9001,
"some_info": "foo",
"some_env_var": "bar"
}' | ncat --wait 1 graylog.example.com 12201
Tipp
Graylog bei der Arbeit zuschauen: tail -f /var/log/graylog-server/server.log /var/log/opensearch/*log
Events und Alerts
„Alerts“ bedeutet: Man definiert einen Event (also einen Match auf bestimmte Log-Einträge), welcher eine Notification auslöst.
Beispiel: Sende eine Nachricht nach Rocket.Chat, sobald das Feld „operator_alert“ in einer Nachricht in einem bestimmten Stream vorkommt.
Graylog GUI > Alerts > Event Definitions > Create event definition
Event Details
Title: Operator Alert
Filter & Aggregation
Search Query:
_exists_:operator_alert
Streams: Stream auswählen
Search within the last: 1 hours
Execute search every: 5 seconds
Notifications
Add notification
Title: operator_alert Notification
Notification Type: Custom HTTP Notification
URL: Rocket.Chat Webhook URL
Body Template:
{ "channel": "my channel", "text": ":service_crit: Graylog ${event_definition_title}: ${foreach backlog message}${message.message}${end}" }
Message Backlog: 1
Suche: Graylog Query Language
Beispiel für eine Suche in zwei Textfeldern und einem Integer-Feld:
NOT requestURI:"/billing/*" AND NOT requestURI:"/calendar/*" AND durationInMs:>500
Hosts ausschliessen:
NOT source:hostA and NOT source:hostB
Das Beispiel
source:host.example.* AND response-time:<30000
liefert auch Ergebnisse mit „response-time“ von 100’000 und mehr? Da drängt sich der Verdacht auf, dass es sich hier um eine Suche nach Strings und nicht nach Integern handelt.
So kann man überprüfen, wie Elasticsearch die Werte speichert - auf dem Log-Server:
curl 'http://localhost:9200/_mapping?pretty'
curl 'http://localhost:9200/_cat/indices?v'
curl 'localhost:9200/_cat/templates?v'
Client-Logging
rsyslog
Soll rsyslog Events an einen Graylog Server weiterleiten, empfiehlt sich das GELF Protokoll, da hier einfach zusätzliche Felder mitgeschickt werden können:
# send server's FQDN instead of its short hostname
$PreserveFQDN on
template(name="gelf" type="list") {
constant(value="{\"version\":\"1.1\",")
constant(value="\"host\":\"")
property(name="hostname")
constant(value="\",\"short_message\":\"")
property(name="msg" format="json")
constant(value="\",\"timestamp\":")
property(name="timegenerated" dateformat="unixtimestamp")
constant(value=",\"level\":\"")
property(name="syslogseverity")
constant(value="\"}")
}
# syslog forwarder via UDP
action(type="omfwd" target="graylog.example.com" port="12201" protocol="udp" template="gelf")
Damit ein Client sinnvolle Daten weiterleitet, kann folgende rsyslog-Konfiguration als Basis verwendet werden. So können die Events schon auf dem Client gefiltert werden, und dann per Syslog oder GELF weitergeleitet werden.
# rsyslog v7 filter conditions:
# contains isequal startswith regex ereregex
# http://www.rsyslog.com/doc/v7-stable/configuration/filters.html
if (
$msg startswith "GSSAPI client step " or
$msg startswith "GSSAPI server step " or
($programname == "kernel" and $msg startswith "RULE ") or
($programname == "systemd" and ($msg startswith "Created slice " or $msg startswith "Removed slice ")) or
($programname == "systemd" and ($msg startswith "Starting user-" or $msg startswith "Stopping user-")) or
($programname == "systemd" and ($msg startswith "Starting Session " or $msg startswith "Started Session ")) or
($programname == "systemd-logind" and ($msg startswith "New Session " or $msg startswith "Removed Session "))
)
then
# ignore, do not foward
continue
else
# Syslog
*.* @graylog.example.com:1514;RSYSLOG_SyslogProtocol23Format
# GELF (requires the template from above)
# action(type="omfwd" target="graylog.example.com" port="12201" protocol="udp" template="gelf")
Beispiel einer Weiterleitung per rsyslog für einen JBoss-Applikationsserver (alternativ WildFly):
module(load="imfile" PollingInterval="10")
input(
type="imfile"
File="/opt/jboss/standalone/log/server.json"
Tag="jboss-eap"
StateFile="statefile-to6nnu8DgDug4ImsaaJCpkAQt0zPKb"
)
# just forward, do not log again in /var/log/messages followed by the rules later on in /etc/rsyslog.conf
if $programname == 'jboss-eap' then {
action(
type="omfwd"
Target="graylog.example.com"
Port="1514"
Protocol="udp"
)
stop
}
Apache
Am besten so: https://www.lisenet.com/2016/send-apache-logs-to-graylog/
LogFormat: direkt GELF-kompatibles JSON schreiben
CustomLog mit
ncat
direkt an Graylog pipen
# GELF Log Format
LogFormat "{\n\
\"version\": \"1.1\",\n\
\"host\": \"%v\",\n\
\"short_message\": \"%r\",\n\
\"timestamp\": %{%s}t,\n\
\"level\": 6,\n\
\"_user_agent\": \"%{User-Agent}i\",\n\
\"_source_ip\": \"%{X-Forwarded-For}i\",\n\
\"_duration_usec\": %D,\n\
\"_duration_sec\": %T,\n\
\"_request_size_byte\": %O,\n\
\"_http_status_orig\": %s,\n\
\"_http_status\": %>s,\n\
\"_http_request_path\": \"%U\",\n\
\"_http_request\": \"%U%q\",\n\
\"_http_method\": \"%m\",\n\
\"_http_referer\": \"%{Referer}i\",\n\
\"_from_apache\": \"true\"\n\
}" gelf
CustomLog "|/usr/bin/ncat --udp graylog.example.com 12201" gelf
Fortinet
Bei Fortinet kann statt Syslog besser ` das Common Event Format (CEF) <https://go2docs.graylog.org/5-0/getting_in_log_data/ingest_cef.html>`_ verwendet werden.
journalctl per GELF
GELF/udp: die halboffizielle Implementierung, uralt - https://github.com/systemd/journal2gelf
GELF/udp - verbessert: https://github.com/nailgun/journal2gelf
GELF/udp oder tcp: https://github.com/oboukili/journald-to-gelf
Hinweis: Nachrichten per TCP müssen mit einem „0“ beendet werden.
Wir empfehlen nailgun/journal2gelf. Zunächst EPEL-Repo aktivieren, dann:
dnf -y install git python-pip gcc python-devel systemd-devel
pip install --user git+https://github.com/systemd/python-systemd.git#egg=systemd
pip install --user gelfclient
git clone https://github.com/nailgun/journal2gelf.git
cd journal2gelf
python setup.py install
journal2gelf --help
Test (geht per GELF an Port 12201/udp):
journal2gelf graylog.example.com:12201
[Unit]
Description=Journald to GELF (graylog) log relay service
[Service]
ExecStart=/usr/bin/journal2gelf graylog.example.com:12201
Restart=on-failure
RestartSec=30
[Install]
WantedBy=multi-user.target
Java Application Server
Alle Log-Daemons arbeiten nach dem Prinzip „Zeilenumbruch = neue Log-Message“, so auch Graylog. Dieses Verhalten lässt sich nicht ändern.
Um Graylog fit für Java Appserver zu machen, die z.B. bei Stacktraces riesige Meldungen mit Zeilenumbrüchen garniert ausgeben, muss im Java Applikationsserver ein GELF-Appender im nativen, von Entwickler eingesetzten Java Logging Framework eingesetzt werden.
GELF-Appender gibt es einige, und sind wie gesagt von dem abhängig, was ein Entwickler an Log-Mechanismen verwendet. Einige GELF-Appender bauen auf dem GELF Java Client auf: https://github.com/Graylog2/gelfclient
Windows
Soll ein Windows-Server Event-Logs senden, wird ein lokal zu installierender Agent benötigt. Graylog empfiehlt die Kombi aus Graylog Sidecar (ein Daemon) und WinLogBeat 7.x (der eigentliche Client). Alternativ kann man Graylog Sidecar und NXlog einsetzen.
Siehe auch https://go2docs.graylog.org/5-2/getting_in_log_data/ingest_windows_eventlog.html
Auf dem Graylog Server:
Es muss im Vorfeld ein API Key erstellt werden:
System
>Users and Teams
Ausserdem muss ein
Beats
-Input erstellt werden
Installation Sidecar auf dem Windows Host:
Sidecar herunterladen: https://github.com/Graylog2/collector-sidecar/releases
Sidecar installieren: Dabei muss direkt die API URL inkl. API Key eingegeben werden. WinLogBeat wird automatisch mit installiert und entsprechend konfiguriert
Wieder auf dem Graylog Server:
Unter
System
>Sidecars
sollte nun das zuvor installiert Sidecar zu sehen seinRechts auf
Show Messages
sollten bereits die ersten Log Messages zu sehen sein
Benutzer
Benutzer benötigt Decorator-Edit-Rechte? Sowas geht nur über das REST-API.
Verwendbare Berechtigungen abfragen:
curl --request GET \
--user admin:linuxfabrik \
'http://graylog.example.com:9000/api/system/permissions?pretty=true'
Ergebnis u.a.:
"decorators" : [ "create", "read", "edit" ]
Neue Rolle erstellen:
curl --request POST \
--user admin:linuxfabrik \
--header 'Content-Type:application/json' \
--data '{"read_only": false,"permissions": ["decorators:create:*", "decorators:read:*", "decorators:edit:*" ],"name": "Full Decorator Permissions","description": "Permission to create, read oder delete decorators."}'
'http://graylog.example.com:9000/api/roles'
Anschliessend kann die neue Rolle im GUI > Authentication Management ganz unten unter „Roles“ den Benutzern zugeteilt werden.
Berechtigungen für den Benutzer prüfen:
curl --request GET \
--user admin:linuxfabrik \
"http://graylog.example.com:9000/api/users/$USERNAME?pretty=true"
Pipelines
Unter System > Configuration > Message Processors Configuration die „Pipeline“ aktivieren und nach der „Message Filter Chain“ setzen, wenn man auf Felder prüfen möchte, die von Extraktoren gesetzt wurden.
Ablauf:
Pipeline anlegen
Stage anlegen
Rule anlegen
Rule zur Stage hinzufügen
Pipeline zu Stream connecten
Sinnfreies Beispiel für eine Rule mit Kommentaren, Abfrage auf einen Input und Wegwerfen einer Nachricht:
rule "unneeded if container_name contains _app_ or _cep_"
when
(
// to_string($message.gl2_source_input) == "5b16a9176e8bf60b6cf8bc6b" and
has_field("container_name") and
(contains(to_string($message.container_name), "_app_") or contains(to_string($message.container_name), "_cep_"))
)
then
set_field("unneeded_message", true);
drop_message();
end
Simulation:
GELF-Message: {
"version": "1.1",
"host": "example.org",
"short_message": "Short message",
"full_message": "Full message",
"level": 1,
"container_name": "my_container"
}
Wert nach Long konvertieren:
rule "Convert RT_FLOW to Numeric"
when
has_field("bytes-from-server" ) && $message.application_name == "RT_FLOW"
then
let serverre = to_long( $message.`bytes-from-server`);
let clientre = to_long( $message.`bytes-from-client`);
set_field("bytes-from-server_conv", serverre );
set_field("bytes-from-client_conv", clientre );
end
API, API-Browser
Alle Methoden auf einen Blick: http://graylog.example.com:9000/api/api-browser
Authentifizierung entweder mittels Username und Passwort, oder per Access Token (letztere ist aus Sicherheitsgründen die bevorzugte Methode).
Access Token erstellen:
Benutzer erstellen, Standard-Rolle „Reader“ notfalls anpassen
Token zuweisen
curl --user user:linuxfabrik \
--header 'Accept: application/json' \
--request GET \
'http://graylog.example.com:9000/api/...' | jq
Cluster-Informationen auslesen - mit Username und Passwort:
curl --user admin:linuxfabrik \
--header 'Accept: application/json' \
'http://graylog.example.com:9000/api/cluster?pretty=true'
Cluster-Informationen auslesen - mit Access Token (Token als Benutzername nutzen; Passwort ist hier immer „token“):
curl --user 1crgamba51dkdn8jsrltpek90m167p1dmteaa0gr7ehscnb3bpce:token \
--header 'Accept: application/json' \
'http://graylog.example.com:9000/api/cluster?pretty=true'
Log-Einträge auf Basis einer Query als CSV exportieren:
# urlencoded query
#q=*
q=syslog_identifier%3Asshd%20AND%20opened%20AND%20NOT%20root
range=300 # in seconds
# comma-separated list of fields (%2C)
curl --user admin:linuxfabrik \
--header 'Accept:text/csv' \
'http://graylog.example.com:9000/api/search/universal/relative/export?query=$q&range=$range&fields=timestamp%2Csource%2Cmessage' | gzip -9 > export.csv
Streams und Stream Rules:
Stream Rule Type 1: match exactly
Stream Rule Type 2: match regular expression
Stream Rule Type 3: greater than
Stream Rule Type 4: smaller than
Stream Rule Type 5: field presence
Stream Rule Type 6: contain
Stream Rule Type 7: always match
Stream Rule Type 8: match input
curl --user user:linuxfabrik \
--header 'Accept: application/json' \
--header 'X-Requested-By: cli' \
--data '{
"title": "All messages",
"description": "All messages are routed here",
"matching_type": "AND",
"rules": [
{
"field": "",
"type": 7,
"inverted": false,
"value": ""
}
],
"content_pack": null
}' \
--request POST \
'http://graylog.example.com:9000/api/streams' | jq
Troubleshooting
- „415 Unsupported Media Type“ bei Verwendung des APIs
The server is refusing to service the request because the entity of the request is in a format not supported by the requested resource for the requested method.
OpenSearchException[OpenSearch exception [type=illegal_argument_exception, reason=mapper [latency] cannot be changed from type [float] to [long]]]
Die Mappings (OpenSearch / Elasticsearch Felder und deren Typ) werden dynamisch anhand der ersten Nachricht im jeweiligen Index bestimmt. Dies geschieht jedes Mal, wenn ein neuer Index begonnen wird, also auch, wenn der Index rotiert wurde. Das heisst, wenn der erste Eintrag
latency: 0
ist, nimmt OpenSearch fälschlicherweise an, dass es sich um ein long handelt. Wenn man nun einen float-Wert schickt, wird dieser zwar im Graylog und in OpenSearch als float angezeigt, intern aber trotzdem als long behandelt (siehe https://xeraa.net/blog/2020_elasticsearch-coerce-float-to-integer-or-long/).Um dieses Problem zu beheben, kann man den Typ für das Feld
latency
manuell setzen:# show current mapping curl 'localhost:9200/lfops-default_4/_mappings?pretty' | grep latency -A1 # "latency" : { # "type" : "float" # create custom mapping, see https://go2docs.graylog.org/5-1/setting_up_graylog/elasticsearch.htm#CustomIndexMappings cat > lfops-latency-float.json << 'EOF' { "template": "lfops-default_*", "order": 10, "mappings": { "properties": { "latency": { "type": "float" } } } } EOF curl -X PUT --data @'lfops-latency-float.json' -H 'Content-Type: application/json' 'http://localhost:9200/_template/lfops-latency-float?pretty' # show current templates curl -X DELETE 'http://localhost:9200/_template/lfops-latency-float?pretty'
Anschliessend muss im Graylog unter Indices der betroffene Index ausgewählt und über „Maintanance > Rotate active write index“ der aktuelle Index neu generiert werden.
Built on 2024-11-18