From b29917df0b8a57732823d9213c038fea2a2de1fc Mon Sep 17 00:00:00 2001 From: Darragh O'Reilly Date: Fri, 2 May 2025 14:41:52 +0100 Subject: [PATCH 1/4] ClamAV: add artifact for running clamdscan --- .../SUSE/Linux/ClamAV/clamdscan.yaml | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 artifacts/definitions/SUSE/Linux/ClamAV/clamdscan.yaml diff --git a/artifacts/definitions/SUSE/Linux/ClamAV/clamdscan.yaml b/artifacts/definitions/SUSE/Linux/ClamAV/clamdscan.yaml new file mode 100644 index 000000000..eee510b52 --- /dev/null +++ b/artifacts/definitions/SUSE/Linux/ClamAV/clamdscan.yaml @@ -0,0 +1,31 @@ +name: SUSE.Linux.ClamAV.clamdscan + +description: | + Run clamdscan on a file or directory. + +type: CLIENT + +required_permissions: + - EXECVE + +parameters: + - name: path + default: /home + description: file or directory to be scanned + - name: show_summary + type: bool + default: False + description: display scan summary + +sources: + - precondition: + SELECT OS FROM info() WHERE OS = "linux" + + query: | + LET rm_empty_opts(opts) = filter(list=opts, regex='^.+$') + + LET summaryOpt = if(condition=show_summary, then="", else="--no-summary") + + LET argv = rm_empty_opts(opts=["clamdscan", "--fdpass", "--stdout", summaryOpt, path]) + + SELECT * FROM execve(argv=argv, sep='\n') From 7ed68bdd3728a12f530bc2cb748dafd82513400c Mon Sep 17 00:00:00 2001 From: Darragh O'Reilly Date: Fri, 2 May 2025 16:54:08 +0100 Subject: [PATCH 2/4] ClamAV: add artifact to collect clamd logs --- .../SUSE/Linux/Events/ClamAvLogs.yaml | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 artifacts/definitions/SUSE/Linux/Events/ClamAvLogs.yaml diff --git a/artifacts/definitions/SUSE/Linux/Events/ClamAvLogs.yaml b/artifacts/definitions/SUSE/Linux/Events/ClamAvLogs.yaml new file mode 100644 index 000000000..1da1ecebc --- /dev/null +++ b/artifacts/definitions/SUSE/Linux/Events/ClamAvLogs.yaml @@ -0,0 +1,26 @@ +name: SUSE.Linux.Events.ClamAvLogs + +description: | + This artifact watches the systemd journal for messages from + the ClamAV clamd service, and collects any that match the + regex filter parameter. + +type: CLIENT_EVENT + +parameters: + - name: regex_filter + type: regex + default: ".+FOUND$" + description: Filter messages to be collected + +sources: + - precondition: + SELECT OS From info() where OS = 'linux' + + query: | + SELECT + timestamp(epoch=REALTIME_TIMESTAMP) AS Time, + MESSAGE as Message + FROM watch_journal() + WHERE SYSLOG_IDENTIFIER = "clamd" + AND MESSAGE =~ regex_filter From 7d16d46f257be68abaa425b718a5b8f7159fe39a Mon Sep 17 00:00:00 2001 From: Darragh O'Reilly Date: Fri, 9 May 2025 16:17:49 +0100 Subject: [PATCH 3/4] ClamAV: add artifact to check correct configuration This artifact checks clamd.conf at regular intervals and reports any required settings that are not configured properly, and any settings that are configured but are not required. The default required settings are the same as the default /etc/clamd.conf from the clamav package. --- .../SUSE/Linux/Events/ClamdAvCheckConfig.yaml | 100 ++++++++++++++++++ 1 file changed, 100 insertions(+) create mode 100644 artifacts/definitions/SUSE/Linux/Events/ClamdAvCheckConfig.yaml diff --git a/artifacts/definitions/SUSE/Linux/Events/ClamdAvCheckConfig.yaml b/artifacts/definitions/SUSE/Linux/Events/ClamdAvCheckConfig.yaml new file mode 100644 index 000000000..342ccdf4e --- /dev/null +++ b/artifacts/definitions/SUSE/Linux/Events/ClamdAvCheckConfig.yaml @@ -0,0 +1,100 @@ +name: SUSE.Linux.Event.ClamAVStatus + +description: | + This artifact checks the running ClamAV configuration at regular intervals. + It generates a row for each required option that is missing or has the wrong + argument, and each option that is configured but is not required. + +type: CLIENT_EVENT + +parameters: + - name: clamd_conf + description: Path to clamd.conf + type: string + default: /etc/clamd.conf + - name: check_period + description: Time between checks in seconds + type: int + default: 7200 + - name: required_options + description: Options and arguments that are required to be set in clamd.conf + type: csv + default: | + Option,Argument + LogSyslog,yes + LogFacility,LOG_MAIL + PidFile,/run/clamav/clamd.pid + LocalSocket,/run/clamav/clamd.sock + User,vscan + OnAccessIncludePath,/home + OnAccessExcludeUname,vscan + +sources: + - precondition: + SELECT OS From info() WHERE OS = 'linux' + + query: | + SELECT * FROM foreach( + row={ + SELECT * FROM clock(start=0, period=check_period) + }, + query={ + -- The query plugin is used so that the materialized queries + -- get recreated on each iteration of the foreach loop. + SELECT * FROM query(query=''' + LET status <= dict(OK=true) + + LET required_dict <= to_dict(item={ + SELECT Option AS _key, Argument AS _value FROM required_options + }) + + LET conf_exists <= if( + condition={ SELECT * from stat(filename=clamd_conf) }, + then=true, else=false + ) + + LET running_conf <= SELECT Option, Argument + FROM parse_csv(filename=clamd_conf, auto_headers=false, separator=" ", comment="#", columns=["Option", "Argument"]) + WHERE conf_exists + + LET running_dict <= to_dict(item={ + SELECT Option AS _key, Argument AS _value FROM running_conf WHERE conf_exists + }) + + SELECT * FROM chain( + a={ + SELECT + format(format="%s is missing option: \"%s %s\"", args=[clamd_conf, Option, Argument]) AS Message + FROM required_options + WHERE conf_exists + AND get(item=running_dict, field=Option) != Argument + AND set(item=status, field="OK", value=false) + }, + b={ + SELECT + format(format="%s has unrequired option: \"%s %s\"", args=[clamd_conf, Option, Argument]) AS Message + FROM running_conf + WHERE conf_exists + AND NOT get(item=required_dict, field=Option) + AND set(item=status, field="OK", value=false) + }, + c={ + SELECT + format(format="%s does not exist", args=[clamd_conf]) AS Message + FROM scope() + WHERE NOT conf_exists + AND set(item=status, field="OK", value=false) + }, + d={ + SELECT + "Configuration is good" AS Message + FROM scope() + WHERE status.OK + } + ) + + ''', + env=dict(clamd_conf=clamd_conf, required_options=required_options) + ) + } + ) From 13984f6907d0965b7dc6641d1fa82f9e8b23a129 Mon Sep 17 00:00:00 2001 From: Darragh O'Reilly Date: Tue, 13 May 2025 16:30:15 +0100 Subject: [PATCH 4/4] ClamAV: add artifact to report services not running --- .../SUSE/Linux/Events/ClamdAvServices.yaml | 48 +++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 artifacts/definitions/SUSE/Linux/Events/ClamdAvServices.yaml diff --git a/artifacts/definitions/SUSE/Linux/Events/ClamdAvServices.yaml b/artifacts/definitions/SUSE/Linux/Events/ClamdAvServices.yaml new file mode 100644 index 000000000..040f479df --- /dev/null +++ b/artifacts/definitions/SUSE/Linux/Events/ClamdAvServices.yaml @@ -0,0 +1,48 @@ +name: SUSE.Linux.Events.ClamAVServices + +description: | + This artifact checks the ClamAV systemd services every + `check_period` seconds and generate a row for each one + found not to be running. + +type: CLIENT_EVENT + +required_permissions: + - EXECVE + +parameters: + - name: check_period + description: Time between checks in seconds. + type: int + default: 7200 + - name: services + description: List of ClamAV services to check. + type: json_array + default: '["clamd", "clamonacc", "freshclam.timer"]' + +sources: + - precondition: + SELECT OS From info() WHERE OS = 'linux' + + query: | + LET is_active(service) = SELECT * + FROM execve(argv=["systemctl", "is-active", "--quiet", service]) + WHERE ReturnCode = 0 + + SELECT * FROM foreach( + row={ + SELECT * FROM clock(start=0, period=check_period) + }, + query={ + SELECT * FROM foreach( + row={ + SELECT _value AS service FROM items(item=services) + }, + query={ + SELECT format(format="%s is not running", args=[service]) AS Message + FROM scope() + WHERE NOT is_active(service=service) + } + ) + } + )