import CS aide-0.19.2-7.el9
This commit is contained in:
parent
a1c5816ac1
commit
cadece6330
@ -20,21 +20,28 @@
|
||||
compared with the AIDE database. Prior to running a check manually,
|
||||
ensure that the AIDE binary and database have not been modified
|
||||
without your knowledge.
|
||||
|
||||
Caution!
|
||||
|
||||
With the default setup, an AIDE check is not run periodically as a
|
||||
cron job. It cannot be guaranteed that the AIDE binaries, config
|
||||
file and database are intact. It is not recommended that you run
|
||||
automated AIDE checks without verifying AIDE yourself frequently.
|
||||
In addition to that, AIDE does not implement any password or
|
||||
encryption protection for its own files.
|
||||
|
||||
It is up to you how to put a file integrity checker to good effect
|
||||
and how to set up automated checks if you think it adds a level of
|
||||
safety (e.g. detecting failed/incomplete compromises or unauthorized
|
||||
modification of special files). On a compromised system, the
|
||||
intruder could disable the automated check. Or he could replace the
|
||||
AIDE binary, config file and database easily when they are not
|
||||
located on read-only media.
|
||||
|
||||
6) To schedule daily integrity checks, enable the systemd timer:
|
||||
|
||||
systemctl enable --now aide-check.timer
|
||||
|
||||
View results with: journalctl -u aide-check
|
||||
Check timer status with: systemctl status aide-check.timer
|
||||
|
||||
The timer runs daily with low CPU/IO priority to minimize impact
|
||||
on production workloads. It is disabled by default — only enable
|
||||
it after initializing the database (steps 2-4).
|
||||
|
||||
Caution!
|
||||
|
||||
It cannot be guaranteed that the AIDE binaries, config file and
|
||||
database are intact. It is not recommended that you run automated
|
||||
AIDE checks without verifying AIDE yourself frequently. In addition
|
||||
to that, AIDE does not implement any password or encryption
|
||||
protection for its own files.
|
||||
|
||||
It is up to you how to put a file integrity checker to good effect.
|
||||
On a compromised system, the intruder could disable the automated
|
||||
check. Or he could replace the AIDE binary, config file and database
|
||||
easily when they are not located on read-only media.
|
||||
|
||||
|
||||
657
SOURCES/aide-0.19.2-syslog-format.patch
Normal file
657
SOURCES/aide-0.19.2-syslog-format.patch
Normal file
@ -0,0 +1,657 @@
|
||||
From f3e62eb87e0a0e9c6fd43c933670447c8ab0517a Mon Sep 17 00:00:00 2001
|
||||
From: Cropi <alakatos@redhat.com>
|
||||
Date: Thu, 28 May 2026 14:50:34 +0200
|
||||
Subject: [PATCH] conf, report: add syslog_format config option
|
||||
|
||||
Re-implement the syslog_format option that existed as a Red Hat downstream
|
||||
patch against aide 0.16 but was dropped during the rebase to 0.19.2.
|
||||
|
||||
Customers upgrading from RHEL 9.7 (aide 0.16) to RHEL 9.8 (aide 0.19.2)
|
||||
received a fatal parse error on startup if their aide.conf contained
|
||||
'syslog_format = true' (RHEL-178317).
|
||||
|
||||
The option is implemented as a new REPORT_FORMAT_SYSLOG value in the
|
||||
existing report format module system, rather than as a standalone boolean,
|
||||
which fits the 0.19.2 architecture cleanly.
|
||||
|
||||
syslog_format = yes/true is equivalent to report_format = syslog
|
||||
Both spellings are accepted; last-write wins.
|
||||
|
||||
When active, the standard multi-line report is replaced with a compact
|
||||
semicolon-delimited format where every file event is one line:
|
||||
|
||||
AIDE found differences between database and filesystem!!
|
||||
summary;total_number_of_files=N;added_files=N;removed_files=N;changed_files=N
|
||||
file=/usr/sbin/sshd;Mtime_old=...;Mtime_new=...;SHA256_old=...;SHA256_new=...
|
||||
dir=/etc/cron.d; added
|
||||
file=/usr/bin/old; removed
|
||||
|
||||
Each line is emitted with a single report_printf() call so that when used
|
||||
with report_url=syslog:<FACILITY> exactly one syslog message is produced
|
||||
per file event.
|
||||
|
||||
The module implements its own unconditional tree walker (not gated on
|
||||
report_level) so added and removed entries are always included, matching
|
||||
the original patch behaviour. ACL and xattr values are formatted directly
|
||||
from db_line fields rather than through get_attribute_values() to avoid
|
||||
embedded newlines breaking the single-line invariant. The original patch's
|
||||
uninitialized 'char *A' in the ACL path is fixed.
|
||||
|
||||
Signed-off-by: Cropi <alakatos@redhat.com>
|
||||
---
|
||||
Makefile.am | 1 +
|
||||
doc/aide.conf.5 | 28 ++++
|
||||
include/conf_ast.h | 1 +
|
||||
include/db_config.h | 1 +
|
||||
include/report.h | 3 +
|
||||
include/report_syslog.h | 28 ++++
|
||||
src/aide.c | 1 +
|
||||
src/conf_ast.c | 1 +
|
||||
src/conf_eval.c | 8 +
|
||||
src/conf_lex.l | 7 +
|
||||
src/report.c | 16 +-
|
||||
src/report_syslog.c | 338 ++++++++++++++++++++++++++++++++++++++++
|
||||
12 files changed, 432 insertions(+), 1 deletion(-)
|
||||
create mode 100644 include/report_syslog.h
|
||||
create mode 100644 src/report_syslog.c
|
||||
|
||||
diff --git a/Makefile.am b/Makefile.am
|
||||
index f78a96c..356b983 100644
|
||||
--- a/Makefile.am
|
||||
+++ b/Makefile.am
|
||||
@@ -31,6 +31,7 @@ aide_SOURCES = src/aide.c include/aide.h \
|
||||
include/report.h src/report.c \
|
||||
include/report_plain.h src/report_plain.c \
|
||||
include/report_json.h src/report_json.c \
|
||||
+ include/report_syslog.h src/report_syslog.c \
|
||||
include/conf_ast.h src/conf_ast.c \
|
||||
include/conf_eval.h src/conf_eval.c \
|
||||
include/conf_lex.h src/conf_lex.l \
|
||||
diff --git a/doc/aide.conf.5 b/doc/aide.conf.5
|
||||
index 4ad9f3e..5366be4 100644
|
||||
--- a/doc/aide.conf.5
|
||||
+++ b/doc/aide.conf.5
|
||||
@@ -266,6 +266,34 @@ The report format to use. The available report formats are as follows:
|
||||
|
||||
\fBjson\fP: Print report in json machine-readable format.
|
||||
.RE
|
||||
+.IP "syslog_format (type: bool, default: \fBno\fR)"
|
||||
+Valid values are \fByes\fR, \fBtrue\fR, \fBno\fR and \fBfalse\fR.
|
||||
+
|
||||
+When enabled, the standard multi-line report is replaced with a compact
|
||||
+semicolon-delimited format where every file event is emitted as a single line.
|
||||
+This ensures that when used with \fBreport_url=syslog:\fILOG_FACILITY\fR, exactly
|
||||
+one syslog message is produced per changed, added, or removed file.
|
||||
+
|
||||
+Output starts with a header line when differences are found:
|
||||
+.nf
|
||||
+AIDE found differences between database and filesystem!!
|
||||
+.fi
|
||||
+Followed by a summary line:
|
||||
+.nf
|
||||
+summary;total_number_of_files=\fIN\fP;added_files=\fIN\fP;removed_files=\fIN\fP;changed_files=\fIN\fP
|
||||
+.fi
|
||||
+Then one line per changed, added, or removed entry:
|
||||
+.nf
|
||||
+file=/usr/sbin/sshd;Mtime_old=...;Mtime_new=...;SHA256_old=...;SHA256_new=...
|
||||
+dir=/etc/cron.d; added
|
||||
+file=/usr/bin/old; removed
|
||||
+.fi
|
||||
+
|
||||
+The maximum size of a single syslog message depends on the syslog daemon
|
||||
+(typically 1\(en8\ KB). Lines exceeding this limit will be silently truncated
|
||||
+by the syslog daemon. This is not controlled by AIDE.
|
||||
+
|
||||
+The \fBreport_summarize_changes\fR option has no effect in this format.
|
||||
|
||||
.IP "report_base16 (type: bool, default: \fBfalse\fR, added in AIDE v0.17)"
|
||||
Base16 encode the checksums in the report. The default is to
|
||||
diff --git a/include/conf_ast.h b/include/conf_ast.h
|
||||
index 8892a05..424f734 100644
|
||||
--- a/include/conf_ast.h
|
||||
+++ b/include/conf_ast.h
|
||||
@@ -53,6 +53,7 @@ typedef enum config_option {
|
||||
REPORT_FORMAT_OPTION,
|
||||
LIMIT_CMDLINE_OPTION,
|
||||
NUM_WORKERS,
|
||||
+ SYSLOG_FORMAT_OPTION,
|
||||
} config_option;
|
||||
|
||||
typedef struct {
|
||||
diff --git a/include/db_config.h b/include/db_config.h
|
||||
index 4173a4b..363631e 100644
|
||||
--- a/include/db_config.h
|
||||
+++ b/include/db_config.h
|
||||
@@ -133,6 +133,7 @@ typedef struct db_config {
|
||||
int report_detailed_init;
|
||||
int report_base16;
|
||||
int report_quiet;
|
||||
+ int syslog_format;
|
||||
bool report_append;
|
||||
|
||||
DB_ATTR_TYPE report_ignore_added_attrs;
|
||||
diff --git a/include/report.h b/include/report.h
|
||||
index 2ec3539..b2efa55 100644
|
||||
--- a/include/report.h
|
||||
+++ b/include/report.h
|
||||
@@ -48,6 +48,7 @@ typedef enum {
|
||||
REPORT_FORMAT_UNKNOWN = 0,
|
||||
REPORT_FORMAT_PLAIN = 1,
|
||||
REPORT_FORMAT_JSON = 2,
|
||||
+ REPORT_FORMAT_SYSLOG = 3,
|
||||
} REPORT_FORMAT;
|
||||
|
||||
extern const ATTRIBUTE report_attrs_order[];
|
||||
@@ -138,6 +139,8 @@ typedef struct report_format_module {
|
||||
void (*print_report_summary)(report_t*);
|
||||
} report_format_module;
|
||||
|
||||
+DB_ATTR_TYPE get_report_attributes(seltree*, report_t*);
|
||||
+
|
||||
char* get_file_type_string(mode_t);
|
||||
char* get_summarize_changes_string(report_t*, seltree*);
|
||||
char* get_summary_string(report_t*);
|
||||
diff --git a/include/report_syslog.h b/include/report_syslog.h
|
||||
new file mode 100644
|
||||
index 0000000..4da9ae4
|
||||
--- /dev/null
|
||||
+++ b/include/report_syslog.h
|
||||
@@ -0,0 +1,28 @@
|
||||
+/*
|
||||
+ * AIDE (Advanced Intrusion Detection Environment)
|
||||
+ *
|
||||
+ * Copyright (C) 2025 Hannes von Haugwitz
|
||||
+ *
|
||||
+ * This program is free software; you can redistribute it and/or
|
||||
+ * modify it under the terms of the GNU General Public License as
|
||||
+ * published by the Free Software Foundation; either version 2 of the
|
||||
+ * License, 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 Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
+ */
|
||||
+
|
||||
+#ifndef _REPORT_SYSLOG_H_INCLUDED
|
||||
+#define _REPORT_SYSLOG_H_INCLUDED
|
||||
+
|
||||
+#include "report.h"
|
||||
+
|
||||
+extern report_format_module report_module_syslog;
|
||||
+
|
||||
+#endif
|
||||
diff --git a/src/aide.c b/src/aide.c
|
||||
index 6f728a9..f9a9cd4 100644
|
||||
--- a/src/aide.c
|
||||
+++ b/src/aide.c
|
||||
@@ -439,6 +439,7 @@ static void setdefaults_before_config(void)
|
||||
conf->report_detailed_init=0;
|
||||
conf->report_base16=0;
|
||||
conf->report_quiet=0;
|
||||
+ conf->syslog_format=0;
|
||||
conf->report_append=false;
|
||||
conf->report_ignore_added_attrs = 0;
|
||||
conf->report_ignore_removed_attrs = 0;
|
||||
diff --git a/src/conf_ast.c b/src/conf_ast.c
|
||||
index 366bc3f..7b66aed 100644
|
||||
--- a/src/conf_ast.c
|
||||
+++ b/src/conf_ast.c
|
||||
@@ -58,6 +58,7 @@ config_option_t config_options[] = {
|
||||
{ REPORT_FORMAT_OPTION, NULL, NULL },
|
||||
{ LIMIT_CMDLINE_OPTION, "limit", "Limit" },
|
||||
{ NUM_WORKERS, NULL, NULL },
|
||||
+ { SYSLOG_FORMAT_OPTION, NULL, NULL },
|
||||
};
|
||||
|
||||
static ast* new_ast_node(void) {
|
||||
diff --git a/src/conf_eval.c b/src/conf_eval.c
|
||||
index 5774ce6..bb39610 100644
|
||||
--- a/src/conf_eval.c
|
||||
+++ b/src/conf_eval.c
|
||||
@@ -264,6 +264,9 @@ static void eval_config_statement(config_option_statement statement, int linenum
|
||||
REPORT_FORMAT report_format = get_report_format(str);
|
||||
if (report_format != REPORT_FORMAT_UNKNOWN) {
|
||||
conf->report_format = report_format;
|
||||
+ for (list *l = conf->report_urls; l; l = l->next) {
|
||||
+ ((report_t *)l->data)->format = report_format;
|
||||
+ }
|
||||
LOG_CONFIG_FORMAT_LINE(LOG_LEVEL_CONFIG, "set 'report_format' option to '%s' (raw: %d)", str, report_format)
|
||||
} else {
|
||||
LOG_CONFIG_FORMAT_LINE(LOG_LEVEL_ERROR, "invalid report format: '%s'", str);
|
||||
@@ -315,6 +318,11 @@ static void eval_config_statement(config_option_statement statement, int linenum
|
||||
LOG_CONFIG_FORMAT_LINE(LOG_LEVEL_NOTICE, "'num_workers' option already set (ignore new value '%s')", str)
|
||||
}
|
||||
break;
|
||||
+ case SYSLOG_FORMAT_OPTION:
|
||||
+ b = string_expression_to_bool(statement.e, linenumber, filename, linebuf);
|
||||
+ conf->syslog_format = b;
|
||||
+ LOG_CONFIG_FORMAT_LINE(LOG_LEVEL_CONFIG, "set 'syslog_format' to '%s'", btoa(b))
|
||||
+ break;
|
||||
}
|
||||
}
|
||||
|
||||
diff --git a/src/conf_lex.l b/src/conf_lex.l
|
||||
index 877a125..1a9654f 100644
|
||||
--- a/src/conf_lex.l
|
||||
+++ b/src/conf_lex.l
|
||||
@@ -362,6 +362,13 @@ LOG_LEVEL lex_log_level = LOG_LEVEL_DEBUG;
|
||||
BEGIN (STRINGEQHUNT);
|
||||
return (CONFIGOPTION);
|
||||
}
|
||||
+<CONFIG>"syslog_format" {
|
||||
+ LOG_LEX_TOKEN(lex_log_level, CONFIGOPTION (SYSLOG_FORMAT_OPTION), conftext)
|
||||
+ conflval.option = SYSLOG_FORMAT_OPTION;
|
||||
+ BEGIN (STRINGEQHUNT);
|
||||
+ return (CONFIGOPTION);
|
||||
+}
|
||||
+
|
||||
|
||||
<CONFIG>"report_level" {
|
||||
LOG_LEX_TOKEN(lex_log_level, CONFIGOPTION (REPORT_LEVEL_OPTION), conftext)
|
||||
diff --git a/src/report.c b/src/report.c
|
||||
index 9e4d0d0..f87754c 100644
|
||||
--- a/src/report.c
|
||||
+++ b/src/report.c
|
||||
@@ -59,6 +59,7 @@
|
||||
#include "report.h"
|
||||
#include "report_plain.h"
|
||||
#include "report_json.h"
|
||||
+#include "report_syslog.h"
|
||||
/*for locale support*/
|
||||
#include "locale-aide.h"
|
||||
/*for locale support*/
|
||||
@@ -146,6 +147,7 @@ struct report_format {
|
||||
static struct report_format report_format_array[] = {
|
||||
{ REPORT_FORMAT_PLAIN, "plain" },
|
||||
{ REPORT_FORMAT_JSON, "json" },
|
||||
+ { REPORT_FORMAT_SYSLOG, "syslog" },
|
||||
{ REPORT_FORMAT_UNKNOWN, NULL }
|
||||
};
|
||||
|
||||
@@ -509,6 +511,15 @@ bool init_report_urls(void) {
|
||||
}
|
||||
}
|
||||
|
||||
+ }
|
||||
+ /* syslog_format is a downstream-only option that must win over report_format
|
||||
+ * regardless of declaration order in the config file. Enforce it here,
|
||||
+ * after the entire config AST has been evaluated, so no subsequent
|
||||
+ * report_format setting can accidentally override the user's intent. */
|
||||
+ if (conf->syslog_format) {
|
||||
+ for (l=conf->report_urls; l; l=l->next) {
|
||||
+ ((report_t *)l->data)->format = REPORT_FORMAT_SYSLOG;
|
||||
+ }
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -677,7 +688,7 @@ char* get_summarize_changes_string(report_t* report, seltree* node) {
|
||||
|
||||
|
||||
|
||||
-static DB_ATTR_TYPE get_report_attributes(seltree* node, report_t *report) {
|
||||
+DB_ATTR_TYPE get_report_attributes(seltree* node, report_t *report) {
|
||||
db_line* oline = node->old_data;
|
||||
db_line* nline = node->new_data;
|
||||
DB_ATTR_TYPE attrs = node->changed_attrs;
|
||||
@@ -966,6 +977,9 @@ int gen_report(seltree* node) {
|
||||
case REPORT_FORMAT_JSON:
|
||||
print_report(report, node, report_module_json);
|
||||
break;
|
||||
+ case REPORT_FORMAT_SYSLOG:
|
||||
+ print_report(report, node, report_module_syslog);
|
||||
+ break;
|
||||
case REPORT_FORMAT_UNKNOWN:
|
||||
/* skip unknown report format */
|
||||
break;
|
||||
diff --git a/src/report_syslog.c b/src/report_syslog.c
|
||||
new file mode 100644
|
||||
index 0000000..920e927
|
||||
--- /dev/null
|
||||
+++ b/src/report_syslog.c
|
||||
@@ -0,0 +1,338 @@
|
||||
+/*
|
||||
+ * AIDE (Advanced Intrusion Detection Environment)
|
||||
+ *
|
||||
+ * Copyright (C) 2025 Hannes von Haugwitz
|
||||
+ *
|
||||
+ * This program is free software; you can redistribute it and/or
|
||||
+ * modify it under the terms of the GNU General Public License as
|
||||
+ * published by the Free Software Foundation; either version 2 of the
|
||||
+ * License, 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 Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
+ */
|
||||
+
|
||||
+#include "config.h"
|
||||
+#include "aide.h"
|
||||
+#include <pthread.h>
|
||||
+#include <stdio.h>
|
||||
+#include <stdlib.h>
|
||||
+#include <string.h>
|
||||
+#include <sys/stat.h>
|
||||
+#include "attributes.h"
|
||||
+#include "base64.h"
|
||||
+#include "db.h"
|
||||
+#include "db_line.h"
|
||||
+#include "hashsum.h"
|
||||
+#include "report.h"
|
||||
+#include "report_syslog.h"
|
||||
+#include "seltree.h"
|
||||
+#include "seltree_struct.h"
|
||||
+#include "tree.h"
|
||||
+#include "util.h"
|
||||
+
|
||||
+/* Returns the compact syslog file-type prefix, or NULL when mode is unknown
|
||||
+ * (mode & S_IFMT == 0), in which case callers omit the "type=" prefix. */
|
||||
+static const char *get_syslog_type_prefix(mode_t mode) {
|
||||
+ switch (mode & S_IFMT) {
|
||||
+ case S_IFREG: return "file";
|
||||
+ case S_IFDIR: return "dir";
|
||||
+ case S_IFLNK: return "link";
|
||||
+ case S_IFBLK: return "blockd";
|
||||
+ case S_IFCHR: return "chard";
|
||||
+#ifdef S_IFIFO
|
||||
+ case S_IFIFO: return "fifo";
|
||||
+#endif
|
||||
+#ifdef S_IFSOCK
|
||||
+ case S_IFSOCK: return "socket";
|
||||
+#endif
|
||||
+ case 0: return NULL;
|
||||
+ default: return "unknown";
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+/* Returns the key name for attribute a. attr_sizeg is the only exception:
|
||||
+ * its details_string is "Size (>)" which contains '>', so we substitute
|
||||
+ * "Size" to match the original patch's explicit details_string[] change. */
|
||||
+static const char *get_attr_key(ATTRIBUTE a) {
|
||||
+ if (a == attr_sizeg) {
|
||||
+ return "Size";
|
||||
+ }
|
||||
+ return attributes[a].details_string;
|
||||
+}
|
||||
+
|
||||
+#ifdef WITH_XATTR
|
||||
+
|
||||
+#define SYSLOG_PRINTABLE_XATTR_VALS \
|
||||
+ "0123456789" \
|
||||
+ "abcdefghijklmnopqrstuvwxyz" \
|
||||
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ" \
|
||||
+ ".-_:;,[]{}<>()!@#$%^&*|\\/?~"
|
||||
+
|
||||
+/* Appends one side's xattr values to stream in compact syslog format.
|
||||
+ * Format: ;XAttrs_<side>=|[1]key=val|[2]key=val| */
|
||||
+static void build_xattrs_compact(FILE *stream, db_line *line, const char *side) {
|
||||
+ xattrs_type *xattrs = line ? line->xattrs : NULL;
|
||||
+
|
||||
+ if (!xattrs || xattrs->num == 0) {
|
||||
+ fprintf(stream, ";XAttrs_%s=|num=0|", side);
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ fprintf(stream, ";XAttrs_%s=|num=%zu|", side, xattrs->num);
|
||||
+
|
||||
+ for (size_t i = 0; i < xattrs->num; i++) {
|
||||
+ const char *key = xattrs->ents[i].key;
|
||||
+ const char *val = (const char *)xattrs->ents[i].val;
|
||||
+ size_t vsz = xattrs->ents[i].vsz;
|
||||
+
|
||||
+ /* Check printability (replicates xstrnspn logic from report.c). */
|
||||
+ size_t plen = 0;
|
||||
+ while (plen < vsz && strchr(SYSLOG_PRINTABLE_XATTR_VALS, val[plen]))
|
||||
+ plen++;
|
||||
+ bool printable = (plen == vsz) || (plen == vsz - 1 && val[plen] == '\0');
|
||||
+
|
||||
+ if (printable) {
|
||||
+ fprintf(stream, "[%zu]%s=%s|", i + 1, key, val);
|
||||
+ } else {
|
||||
+ char *b64 = encode_base64((byte *)xattrs->ents[i].val, vsz);
|
||||
+ fprintf(stream, "[%zu]%s<=>%s|", i + 1, key, b64 ? b64 : "");
|
||||
+ free(b64);
|
||||
+ }
|
||||
+ }
|
||||
+}
|
||||
+#endif /* WITH_XATTR */
|
||||
+
|
||||
+#ifdef WITH_POSIX_ACL
|
||||
+/* Appends one side's ACL to stream in compact syslog format.
|
||||
+ * Format: ;ACL_<side>=A:<acl_a_newlines_as_spaces>|D:<acl_d_newlines_as_spaces>
|
||||
+ *
|
||||
+ * Both A and D are initialized to "<NONE>" before the conditionals, fixing
|
||||
+ * the uninitialized-pointer bug in the original 0.16 patch. */
|
||||
+static void build_acl_compact(FILE *stream, db_line *line, const char *side) {
|
||||
+ acl_type *acl = line ? line->acl : NULL;
|
||||
+
|
||||
+ const char *A = "<NONE>";
|
||||
+ const char *D = "<NONE>";
|
||||
+ if (acl) {
|
||||
+ if (acl->acl_a) { A = acl->acl_a; }
|
||||
+ if (acl->acl_d) { D = acl->acl_d; }
|
||||
+ }
|
||||
+
|
||||
+ /* Write A component, replacing newlines with spaces. */
|
||||
+ fprintf(stream, ";ACL_%s=A:", side);
|
||||
+ for (const char *p = A; *p; p++) {
|
||||
+ fputc(*p == '\n' ? ' ' : *p, stream);
|
||||
+ }
|
||||
+
|
||||
+ /* Write D component, replacing newlines with spaces. */
|
||||
+ fprintf(stream, "|D:");
|
||||
+ for (const char *p = D; *p; p++) {
|
||||
+ fputc(*p == '\n' ? ' ' : *p, stream);
|
||||
+ }
|
||||
+}
|
||||
+#endif /* WITH_POSIX_ACL */
|
||||
+
|
||||
+/* Assembles a complete syslog line for one file event into *out.
|
||||
+ *
|
||||
+ * Invariant: this function never calls report_printf(). The caller emits
|
||||
+ * the result with exactly one report_printf() call to avoid fragmenting
|
||||
+ * syslog messages (each report_printf to url_syslog calls vsyslog() once).
|
||||
+ *
|
||||
+ * Cases:
|
||||
+ * oline != NULL && nline != NULL → changed entry
|
||||
+ * oline == NULL → added entry
|
||||
+ * nline == NULL → removed entry
|
||||
+ *
|
||||
+ * *out is heap-allocated by open_memstream; caller must free() it. */
|
||||
+static void build_syslog_line(report_t *report, db_line *oline, db_line *nline,
|
||||
+ DB_ATTR_TYPE attrs, char **out) {
|
||||
+ db_line *ref = nline ? nline : oline;
|
||||
+ const char *type = get_syslog_type_prefix(ref->perm);
|
||||
+
|
||||
+ char *buf = NULL;
|
||||
+ size_t bufsz = 0;
|
||||
+ FILE *stream = open_memstream(&buf, &bufsz);
|
||||
+
|
||||
+ /* Write "type=path" prefix. */
|
||||
+ if (type) {
|
||||
+ fprintf(stream, "%s=%s", type, ref->filename);
|
||||
+ } else {
|
||||
+ fprintf(stream, "%s", ref->filename);
|
||||
+ }
|
||||
+
|
||||
+ if (oline && nline) {
|
||||
+ /* Changed entry: emit only differing attributes. */
|
||||
+ for (int j = 0; j < report_attrs_order_length; j++) {
|
||||
+ ATTRIBUTE a = report_attrs_order[j];
|
||||
+
|
||||
+ switch (a) {
|
||||
+ case attr_allhashsums:
|
||||
+ /* Expand to each compiled-in hash, mirroring print_dbline_attrs(). */
|
||||
+ for (int i = 0; i < num_hashes; i++) {
|
||||
+ if (!(ATTR(hashsums[i].attribute) & attrs)) { continue; }
|
||||
+ const char *key = get_attr_key(hashsums[i].attribute);
|
||||
+ if (!key) { continue; }
|
||||
+ char **oval = NULL, **nval = NULL;
|
||||
+ get_attribute_values(ATTR(hashsums[i].attribute), oline, &oval, report);
|
||||
+ get_attribute_values(ATTR(hashsums[i].attribute), nline, &nval, report);
|
||||
+ fprintf(stream, ";%s_old=%s;%s_new=%s",
|
||||
+ key, oval ? oval[0] : "", key, nval ? nval[0] : "");
|
||||
+ if (oval) { free(oval[0]); free(oval); }
|
||||
+ if (nval) { free(nval[0]); free(nval); }
|
||||
+ }
|
||||
+ break;
|
||||
+
|
||||
+ case attr_size:
|
||||
+ /* attr_size and attr_sizeg share this slot in report_attrs_order. */
|
||||
+ if (ATTR(attr_size) & attrs) {
|
||||
+ const char *key = get_attr_key(attr_size);
|
||||
+ if (key) {
|
||||
+ char **oval = NULL, **nval = NULL;
|
||||
+ get_attribute_values(ATTR(attr_size), oline, &oval, report);
|
||||
+ get_attribute_values(ATTR(attr_size), nline, &nval, report);
|
||||
+ fprintf(stream, ";%s_old=%s;%s_new=%s",
|
||||
+ key, oval ? oval[0] : "", key, nval ? nval[0] : "");
|
||||
+ if (oval) { free(oval[0]); free(oval); }
|
||||
+ if (nval) { free(nval[0]); free(nval); }
|
||||
+ }
|
||||
+ }
|
||||
+ if (ATTR(attr_sizeg) & attrs) {
|
||||
+ const char *key = get_attr_key(attr_sizeg); /* returns "Size" */
|
||||
+ if (key) {
|
||||
+ char **oval = NULL, **nval = NULL;
|
||||
+ get_attribute_values(ATTR(attr_sizeg), oline, &oval, report);
|
||||
+ get_attribute_values(ATTR(attr_sizeg), nline, &nval, report);
|
||||
+ fprintf(stream, ";%s_old=%s;%s_new=%s",
|
||||
+ key, oval ? oval[0] : "", key, nval ? nval[0] : "");
|
||||
+ if (oval) { free(oval[0]); free(oval); }
|
||||
+ if (nval) { free(nval[0]); free(nval); }
|
||||
+ }
|
||||
+ }
|
||||
+ break;
|
||||
+
|
||||
+ default:
|
||||
+ if (!(ATTR(a) & attrs)) { break; }
|
||||
+#ifdef WITH_XATTR
|
||||
+ if (a == attr_xattrs) {
|
||||
+ build_xattrs_compact(stream, oline, "old");
|
||||
+ build_xattrs_compact(stream, nline, "new");
|
||||
+ break;
|
||||
+ }
|
||||
+#endif
|
||||
+#ifdef WITH_POSIX_ACL
|
||||
+ if (a == attr_acl) {
|
||||
+ build_acl_compact(stream, oline, "old");
|
||||
+ build_acl_compact(stream, nline, "new");
|
||||
+ break;
|
||||
+ }
|
||||
+#endif
|
||||
+ {
|
||||
+ const char *key = get_attr_key(a);
|
||||
+ if (!key) { break; }
|
||||
+ char **oval = NULL, **nval = NULL;
|
||||
+ get_attribute_values(ATTR(a), oline, &oval, report);
|
||||
+ get_attribute_values(ATTR(a), nline, &nval, report);
|
||||
+ fprintf(stream, ";%s_old=%s;%s_new=%s",
|
||||
+ key, oval ? oval[0] : "", key, nval ? nval[0] : "");
|
||||
+ if (oval) { free(oval[0]); free(oval); }
|
||||
+ if (nval) { free(nval[0]); free(nval); }
|
||||
+ }
|
||||
+ break;
|
||||
+ }
|
||||
+ }
|
||||
+ } else if (!oline) {
|
||||
+ fprintf(stream, "; added");
|
||||
+ } else {
|
||||
+ fprintf(stream, "; removed");
|
||||
+ }
|
||||
+
|
||||
+ fclose(stream);
|
||||
+ *out = buf;
|
||||
+}
|
||||
+
|
||||
+/* Emits exactly one syslog line for a file event. */
|
||||
+static void emit_syslog_entry(report_t *report, db_line *oline, db_line *nline,
|
||||
+ DB_ATTR_TYPE attrs) {
|
||||
+ char *line = NULL;
|
||||
+ build_syslog_line(report, oline, nline, attrs, &line);
|
||||
+ report_printf(report, "%s\n", line);
|
||||
+ free(line);
|
||||
+}
|
||||
+
|
||||
+/* Unconditional tree walker — does not gate added/removed on report->level,
|
||||
+ * matching the original patch's print_syslog_format() behavior. */
|
||||
+static void syslog_walk_tree(report_t *report, seltree *node) {
|
||||
+ pthread_rwlock_rdlock(&node->rwlock);
|
||||
+
|
||||
+ if (node->checked & NODE_CHANGED) {
|
||||
+ emit_syslog_entry(report, node->old_data, node->new_data,
|
||||
+ get_report_attributes(node, report));
|
||||
+ }
|
||||
+ if (node->checked & NODE_ADDED) {
|
||||
+ emit_syslog_entry(report, NULL, node->new_data,
|
||||
+ node->new_data->attr & ~report->ignore_added_attrs);
|
||||
+ }
|
||||
+ if (node->checked & NODE_REMOVED) {
|
||||
+ emit_syslog_entry(report, node->old_data, NULL,
|
||||
+ node->old_data->attr & ~report->ignore_removed_attrs);
|
||||
+ }
|
||||
+
|
||||
+ for (tree_node *x = tree_walk_first(node->children); x != NULL; x = tree_walk_next(x)) {
|
||||
+ syslog_walk_tree(report, tree_get_data(x));
|
||||
+ }
|
||||
+
|
||||
+ pthread_rwlock_unlock(&node->rwlock);
|
||||
+}
|
||||
+
|
||||
+/* ── Module callbacks ─────────────────────────────────────────────────── */
|
||||
+
|
||||
+static void noop_header(report_t *report) { (void)report; }
|
||||
+static void noop_footer(report_t *report) { (void)report; }
|
||||
+static void noop_databases(report_t *report) { (void)report; }
|
||||
+static void noop_config_options(report_t *report) { (void)report; }
|
||||
+static void noop_report_options(report_t *report) { (void)report; }
|
||||
+static void noop_starttime_version(report_t *r, const char *t, const char *v) { (void)r; (void)t; (void)v; }
|
||||
+static void noop_endtime_runtime(report_t *r, const char *t, long rt) { (void)r; (void)t; (void)rt; }
|
||||
+static void noop_new_database_written(report_t *report) { (void)report; }
|
||||
+static void noop_entries(report_t *r, seltree *n, const int f) { (void)r; (void)n; (void)f; }
|
||||
+static void noop_diff_attrs(report_t *report) { (void)report; }
|
||||
+static void noop_summary(report_t *report) { (void)report; }
|
||||
+
|
||||
+/* Emits header + summary when there are differences. Two separate
|
||||
+ * report_printf() calls — each becomes one syslog message. */
|
||||
+static void syslog_outline(report_t *report) {
|
||||
+ if (report->nadd || report->nrem || report->nchg) {
|
||||
+ report_printf(report, "AIDE found differences between database and filesystem!!\n");
|
||||
+ report_printf(report,
|
||||
+ "summary;total_number_of_files=%ld;added_files=%ld;"
|
||||
+ "removed_files=%ld;changed_files=%ld\n",
|
||||
+ report->ntotal, report->nadd, report->nrem, report->nchg);
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+static void syslog_details(report_t *report, seltree *node) {
|
||||
+ syslog_walk_tree(report, node);
|
||||
+}
|
||||
+
|
||||
+report_format_module report_module_syslog = {
|
||||
+ .print_report_config_options = noop_config_options,
|
||||
+ .print_report_databases = noop_databases,
|
||||
+ .print_report_details = syslog_details,
|
||||
+ .print_report_diff_attrs_entries = noop_diff_attrs,
|
||||
+ .print_report_endtime_runtime = noop_endtime_runtime,
|
||||
+ .print_report_entries = noop_entries,
|
||||
+ .print_report_footer = noop_footer,
|
||||
+ .print_report_header = noop_header,
|
||||
+ .print_report_new_database_written = noop_new_database_written,
|
||||
+ .print_report_outline = syslog_outline,
|
||||
+ .print_report_report_options = noop_report_options,
|
||||
+ .print_report_starttime_version = noop_starttime_version,
|
||||
+ .print_report_summary = noop_summary,
|
||||
+};
|
||||
--
|
||||
2.54.0
|
||||
|
||||
10
SOURCES/aide-check.service
Normal file
10
SOURCES/aide-check.service
Normal file
@ -0,0 +1,10 @@
|
||||
[Unit]
|
||||
Description=AIDE file integrity check
|
||||
Documentation=man:aide(1) man:aide.conf(5)
|
||||
|
||||
[Service]
|
||||
Type=oneshot
|
||||
ExecStart=/usr/sbin/aide --check
|
||||
SuccessExitStatus=0 1 2 3 4 5 6 7
|
||||
Nice=19
|
||||
IOSchedulingClass=idle
|
||||
11
SOURCES/aide-check.timer
Normal file
11
SOURCES/aide-check.timer
Normal file
@ -0,0 +1,11 @@
|
||||
[Unit]
|
||||
Description=Daily AIDE file integrity check
|
||||
Documentation=man:aide(1) man:aide.conf(5)
|
||||
|
||||
[Timer]
|
||||
OnCalendar=daily
|
||||
AccuracySec=1h
|
||||
Persistent=true
|
||||
|
||||
[Install]
|
||||
WantedBy=timers.target
|
||||
66
SOURCES/aide-include-permission-checks.patch
Normal file
66
SOURCES/aide-include-permission-checks.patch
Normal file
@ -0,0 +1,66 @@
|
||||
diff --git a/src/conf_eval.c b/src/conf_eval.c
|
||||
index 5774ce6..6503709 100644
|
||||
--- a/src/conf_eval.c
|
||||
+++ b/src/conf_eval.c
|
||||
@@ -580,9 +580,9 @@ static void include_file(const char* file, bool execute, int include_depth, char
|
||||
}
|
||||
}
|
||||
|
||||
-void check_permissions(const char* path, struct stat *st, int linenumber, char *filename, char* linebuf) {
|
||||
+static void check_permissions(const char* path, struct stat *st, const char *directive, int linenumber, char *filename, char* linebuf) {
|
||||
if ((st->st_uid != geteuid() && st->st_uid != 0) || (st->st_mode & 002) != 0 || (st->st_mode & 020) != 0) {
|
||||
- LOG_CONFIG_FORMAT_LINE(LOG_LEVEL_ERROR, "'@@x_include': bad ownership or modes for '%s' (please ensure it is neither group- nor world-writable and owned by the current user or root)", path)
|
||||
+ LOG_CONFIG_FORMAT_LINE(LOG_LEVEL_ERROR, "'%s': bad ownership or modes for '%s' (please ensure it is neither group- nor world-writable and owned by the current user or root)", directive, path)
|
||||
exit(INVALID_CONFIGURELINE_ERROR);
|
||||
}
|
||||
}
|
||||
@@ -611,13 +611,13 @@ static void include_directory(const char* dir, const char* rx, bool execute, cha
|
||||
|
||||
struct stat fs;
|
||||
|
||||
- if (execute) {
|
||||
- if (stat(dir,&fs) == -1) {
|
||||
- LOG_CONFIG_FORMAT_LINE(LOG_LEVEL_ERROR, "'@@x_include': stat for '%s' failed: %s", dir, strerror(errno))
|
||||
- exit(INVALID_CONFIGURELINE_ERROR);
|
||||
- }
|
||||
- check_permissions(dir, &fs, linenumber, filename, linebuf);
|
||||
+ /* stat() follows symlinks; we intentionally check the target's ownership
|
||||
+ * and mode rather than the symlink node itself */
|
||||
+ if (stat(dir,&fs) == -1) {
|
||||
+ LOG_CONFIG_FORMAT_LINE(LOG_LEVEL_ERROR, "'%s': stat for '%s' failed: %s", execute?"@@x_include":"@@include", dir, strerror(errno))
|
||||
+ exit(INVALID_CONFIGURELINE_ERROR);
|
||||
}
|
||||
+ check_permissions(dir, &fs, execute?"@@x_include":"@@include", linenumber, filename, linebuf);
|
||||
|
||||
n = scandir(dir, &namelist, dirfilter, alphasort);
|
||||
if (n == -1) {
|
||||
@@ -660,9 +660,8 @@ static void include_directory(const char* dir, const char* rx, bool execute, cha
|
||||
log_msg(LOG_LEVEL_DEBUG,"%s: skip '%s' (reason: file name does not match regex '%s')", dir, namelist[i]->d_name, rx);
|
||||
} else {
|
||||
int exec = execute && S_IXUSR&fs.st_mode;
|
||||
- if (exec) {
|
||||
- check_permissions(filepath, &fs, linenumber, filename, linebuf);
|
||||
- }
|
||||
+ /* pass directive name (not exec flag) so the error names the directive the user wrote */
|
||||
+ check_permissions(filepath, &fs, execute?"@@x_include":"@@include", linenumber, filename, linebuf);
|
||||
log_msg(LOG_LEVEL_CONFIG,"%s: %s '%s'", dir, exec?"execute":"include", namelist[i]->d_name);
|
||||
include_file(filepath, exec, include_depth, nested_rule_prefix);
|
||||
}
|
||||
@@ -701,14 +700,15 @@ static void eval_include_statement(include_statement statement, int include_dept
|
||||
} else {
|
||||
struct stat fs;
|
||||
if (lstat(path,&fs) == -1) {
|
||||
- LOG_CONFIG_FORMAT_LINE(LOG_LEVEL_ERROR, "'@@include': lstat for '%s' failed: %s", path, strerror(errno))
|
||||
+ LOG_CONFIG_FORMAT_LINE(LOG_LEVEL_ERROR, "'%s': lstat for '%s' failed: %s", statement.execute?"@@x_include":"@@include", path, strerror(errno))
|
||||
exit(INVALID_CONFIGURELINE_ERROR);
|
||||
}
|
||||
if (S_ISREG(fs.st_mode)) {
|
||||
+ check_permissions(path, &fs, statement.execute?"@@x_include":"@@include", linenumber, filename, linebuf);
|
||||
LOG_CONFIG_FORMAT_LINE_PREFIX(LOG_LEVEL_CONFIG, "include file '%s' (depth: %d)", path, include_depth)
|
||||
include_file(path, statement.execute && S_IXUSR&fs.st_mode, include_depth, rule_prefix);
|
||||
} else {
|
||||
- LOG_CONFIG_FORMAT_LINE(LOG_LEVEL_ERROR, "'@@include': '%s' is not a regular file", path);
|
||||
+ LOG_CONFIG_FORMAT_LINE(LOG_LEVEL_ERROR, "'%s': '%s' is not a regular file", statement.execute?"@@x_include":"@@include", path);
|
||||
exit(INVALID_CONFIGURELINE_ERROR);
|
||||
}
|
||||
}
|
||||
588
SOURCES/aide-migrate-config
Executable file
588
SOURCES/aide-migrate-config
Executable file
@ -0,0 +1,588 @@
|
||||
#!/bin/bash
|
||||
# aide-migrate-config -- migrate AIDE configuration files from pre-0.19 syntax
|
||||
#
|
||||
# Usage: aide-migrate-config [--dry-run] [--skip-init] <config-file>
|
||||
#
|
||||
# --dry-run Report what would change; do not modify any file.
|
||||
# --skip-init Migrate config files only; do not reinitialise the database.
|
||||
# <config-file> Path to the main AIDE config (e.g. /etc/aide.conf).
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Globals
|
||||
# ---------------------------------------------------------------------------
|
||||
readonly SCRIPT="$(basename "$0")"
|
||||
readonly TIMESTAMP="$(date +%Y%m%d_%H%M%S)"
|
||||
|
||||
DRY_RUN=false
|
||||
SKIP_INIT=false
|
||||
REINIT_NEEDED=false
|
||||
# Parallel arrays: BACKUP_ORIG[i] → BACKUP_COPY[i]
|
||||
BACKUP_ORIG=()
|
||||
BACKUP_COPY=()
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Helpers
|
||||
# ---------------------------------------------------------------------------
|
||||
usage() {
|
||||
echo "Usage: $SCRIPT [--dry-run] [--skip-init] <config-file>" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
die() {
|
||||
echo "$SCRIPT: error: $*" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
info() {
|
||||
echo "$SCRIPT: $*" >&2
|
||||
}
|
||||
|
||||
# log_action MESSAGE
|
||||
# Log a migration action. Prefixes with "would " in dry-run mode.
|
||||
log_action() {
|
||||
if $DRY_RUN; then
|
||||
info " would $*"
|
||||
else
|
||||
info " $*"
|
||||
fi
|
||||
}
|
||||
|
||||
# config_has FILE KEY
|
||||
# Return 0 if FILE contains a KEY= directive.
|
||||
config_has() {
|
||||
local file="$1" key="$2"
|
||||
grep -qE "^[[:space:]]*${key}[[:space:]]*=" "$file"
|
||||
}
|
||||
|
||||
# config_del FILE KEY
|
||||
# Delete all lines matching KEY= from FILE in-place.
|
||||
config_del() {
|
||||
local file="$1" key="$2"
|
||||
sed -E -i "/^[[:space:]]*${key}[[:space:]]*=/d" "$file"
|
||||
}
|
||||
|
||||
# config_rename FILE OLD_KEY NEW_KEY
|
||||
# Rename OLD_KEY= to NEW_KEY= in FILE in-place.
|
||||
config_rename() {
|
||||
local file="$1" old_key="$2" new_key="$3"
|
||||
sed -E -i "s/^([[:space:]]*)${old_key}([[:space:]]*=)/\1${new_key}\2/" "$file"
|
||||
}
|
||||
|
||||
# append_setting FILE CONTENT
|
||||
# Append "CONTENT\n" to FILE, guaranteeing it starts on a fresh line.
|
||||
# No-op if the key extracted from CONTENT already exists in FILE.
|
||||
append_setting() {
|
||||
local file="$1" content="$2"
|
||||
local key="${content%%=*}"
|
||||
config_has "$file" "$key" && return 0
|
||||
if [[ -s "$file" ]] && [[ "$(tail -c1 "$file" | wc -l)" -eq 0 ]]; then
|
||||
printf '\n' >> "$file"
|
||||
fi
|
||||
printf '%s\n' "$content" >> "$file"
|
||||
}
|
||||
|
||||
# backup_file FILE
|
||||
# Create a timestamped backup of FILE. No-op in dry-run mode.
|
||||
backup_file() {
|
||||
local file="$1"
|
||||
local backup="${file}.bak.${TIMESTAMP}"
|
||||
if $DRY_RUN; then
|
||||
return 0
|
||||
fi
|
||||
cp -p -- "$file" "$backup"
|
||||
BACKUP_ORIG+=("$file")
|
||||
BACKUP_COPY+=("$backup")
|
||||
info "backup: $backup"
|
||||
}
|
||||
|
||||
# restore_backups
|
||||
# Called by ERR trap; restores every backed-up file.
|
||||
restore_backups() {
|
||||
local i
|
||||
for i in "${!BACKUP_ORIG[@]}"; do
|
||||
cp -p -- "${BACKUP_COPY[$i]}" "${BACKUP_ORIG[$i]}" && \
|
||||
info "restored: ${BACKUP_ORIG[$i]}" || \
|
||||
info "WARNING: could not restore ${BACKUP_ORIG[$i]} from ${BACKUP_COPY[$i]}"
|
||||
done
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Include-file discovery
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
# extract_includes CONFIG_FILE
|
||||
# Print one absolute path per line for each file pulled in by @@include directives.
|
||||
extract_includes() {
|
||||
local config="$1"
|
||||
local dir
|
||||
dir="$(dirname "$config")"
|
||||
|
||||
grep -E '^[[:space:]]*@@include[[:space:]]' "$config" 2>/dev/null | \
|
||||
while IFS= read -r line; do
|
||||
# Strip the @@include keyword
|
||||
local args
|
||||
args="${line#*@@include}"
|
||||
args="${args#"${args%%[! ]*}"}" # ltrim
|
||||
|
||||
# Count tokens to distinguish FILE vs DIRECTORY REGEX forms
|
||||
local tok1 tok2
|
||||
tok1="${args%% *}"
|
||||
tok2="${args#* }"
|
||||
|
||||
if [[ "$tok1" == "$args" ]]; then
|
||||
# Single token: @@include FILE
|
||||
if [[ "$tok1" = /* ]]; then
|
||||
echo "$tok1"
|
||||
else
|
||||
echo "${dir}/${tok1}"
|
||||
fi
|
||||
else
|
||||
# Two tokens: @@include DIRECTORY REGEX
|
||||
local incdir regex
|
||||
incdir="$tok1"
|
||||
regex="$tok2"
|
||||
if [[ "$incdir" != /* ]]; then
|
||||
incdir="${dir}/${incdir}"
|
||||
fi
|
||||
if [[ -d "$incdir" ]]; then
|
||||
find "$incdir" -maxdepth 1 -type f -regex "$regex" | sort
|
||||
fi
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Config file migration
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
# needs_migration FILE
|
||||
# Return 0 (true) if FILE contains any pattern requiring migration.
|
||||
needs_migration() {
|
||||
local f="$1"
|
||||
# Removed/renamed config options
|
||||
local _key
|
||||
for _key in database grouped summarize_changes ignore_list report_attributes verbose; do
|
||||
config_has "$f" "$_key" && return 0
|
||||
done
|
||||
# sql: value starts with sql: (PostgreSQL backend removed)
|
||||
grep -qE '^[[:space:]]*[a-z_]+[[:space:]]*=[[:space:]]*sql:' "$f" && return 0
|
||||
# any h character in report_ignore_e2fsattrs value
|
||||
grep -qE '^[[:space:]]*report_ignore_e2fsattrs[[:space:]]*=.*h' "$f" && return 0
|
||||
# removed/deprecated hashsums; dash at end of character class to avoid range error in GNU sed
|
||||
grep -v '^[[:space:]]*#' "$f" | \
|
||||
grep -qE '[+[:space:]=-](crc32b|crc32|tiger|haval|whirlpool|md5|sha1|rmd160|gost)([+[:space:]-]|$)' && return 0
|
||||
# standalone S attribute in an expression (skip comment lines)
|
||||
grep -v '^[[:space:]]*#' "$f" | grep -qE '(^|[+=[:space:]-])S([+[:space:]-]|$)' && return 0
|
||||
# deprecated preprocessor macros
|
||||
grep -qE '^[[:space:]]*@@(ifdef|ifndef|ifhost|ifnhost)[[:space:]]' "$f" && return 0
|
||||
# missing trailing newline
|
||||
[[ -s "$f" ]] && [[ "$(tail -c1 "$f" | wc -l)" -eq 0 ]] && return 0
|
||||
return 1
|
||||
}
|
||||
|
||||
# remove_hashsum FILE HASHSUM
|
||||
# Remove a single hashsum token from all group/rule expressions in FILE (in-place).
|
||||
# Handles all positions: +hash, -hash, hash+ (first), and hash$ (last with minus prefix).
|
||||
remove_hashsum() {
|
||||
local file="$1" hash="$2"
|
||||
|
||||
# Pass 1: remove hash when it is preceded by an operator (+ or -)
|
||||
# Keep whatever follows (next operator, space, or end-of-line).
|
||||
sed -E -i \
|
||||
-e "/^[[:space:]]*#/!s/[+-]${hash}([+[:space:]-])/\1/g" \
|
||||
-e "/^[[:space:]]*#/!s/[+-]${hash}$//" \
|
||||
"$file"
|
||||
|
||||
# Pass 2: remove hash at the start of an expression (after = or whitespace),
|
||||
# not preceded by an operator (those were handled in pass 1).
|
||||
sed -E -i \
|
||||
-e "/^[[:space:]]*#/!s/(=[[:space:]]*)${hash}([+[:space:]-])/\1\2/g" \
|
||||
-e "/^[[:space:]]*#/!s/(=[[:space:]]*)${hash}$/\1/" \
|
||||
-e "/^[[:space:]]*#/!s/([[:space:]])${hash}([+[:space:]-])/\1\2/g" \
|
||||
-e "/^[[:space:]]*#/!s/([[:space:]])${hash}$/\1/" \
|
||||
"$file"
|
||||
|
||||
# Clean up artifacts: double operators, dangling operator after '=', trailing operator.
|
||||
sed -E -i \
|
||||
-e '/^[[:space:]]*#/!s/[+][+]/+/g' \
|
||||
-e '/^[[:space:]]*#/!s/[+][-]/+/g' \
|
||||
-e '/^[[:space:]]*#/!s/[-][+]/-/g' \
|
||||
-e '/^[[:space:]]*#/!s/(=[[:space:]]*)[+-]/\1/g' \
|
||||
-e '/^[[:space:]]*#/!s/[+-]$//' \
|
||||
"$file"
|
||||
}
|
||||
|
||||
# migrate_config_file FILE
|
||||
# Apply all config transformations to FILE. Sets global REINIT_NEEDED when needed.
|
||||
migrate_config_file() {
|
||||
local file="$1"
|
||||
|
||||
if [[ ! -f "$file" ]]; then
|
||||
info "WARNING: $file not found, skipping"
|
||||
return 0
|
||||
fi
|
||||
if [[ ! -r "$file" ]]; then
|
||||
info "WARNING: $file is not readable, skipping"
|
||||
return 0
|
||||
fi
|
||||
|
||||
if ! needs_migration "$file"; then
|
||||
info "no migration needed: $file"
|
||||
return 0
|
||||
fi
|
||||
|
||||
info "migrating: $file"
|
||||
backup_file "$file"
|
||||
|
||||
local tmpfile
|
||||
tmpfile="$(mktemp "${file}.XXXXXX")"
|
||||
# Ensure tmpfile is cleaned up if we exit abnormally before the final mv
|
||||
trap "rm -f '$tmpfile'; restore_backups" ERR
|
||||
cp -p -- "$file" "$tmpfile"
|
||||
|
||||
# -----------------------------------------------------------------------
|
||||
# Rename removed config options
|
||||
# The 'database' key anchors to KEY= so it cannot match 'database_in'/'database_out'.
|
||||
# -----------------------------------------------------------------------
|
||||
config_has "$tmpfile" database && log_action "rename: database= → database_in="
|
||||
config_has "$tmpfile" grouped && log_action "rename: grouped= → report_grouped="
|
||||
config_has "$tmpfile" summarize_changes && log_action "rename: summarize_changes= → report_summarize_changes="
|
||||
config_has "$tmpfile" ignore_list && log_action "rename: ignore_list= → report_ignore_changed_attrs="
|
||||
config_has "$tmpfile" report_attributes && log_action "rename: report_attributes= → report_force_attrs="
|
||||
if ! $DRY_RUN; then
|
||||
config_rename "$tmpfile" database database_in
|
||||
config_rename "$tmpfile" grouped report_grouped
|
||||
config_rename "$tmpfile" summarize_changes report_summarize_changes
|
||||
config_rename "$tmpfile" ignore_list report_ignore_changed_attrs
|
||||
config_rename "$tmpfile" report_attributes report_force_attrs
|
||||
fi
|
||||
|
||||
# -----------------------------------------------------------------------
|
||||
# Replace verbose= with the equivalent 0.19 options
|
||||
# -----------------------------------------------------------------------
|
||||
if config_has "$tmpfile" verbose; then
|
||||
log_action "remove verbose="
|
||||
config_has "$tmpfile" log_level || log_action "add log_level=warning"
|
||||
config_has "$tmpfile" report_level || log_action "add report_level=changed_attributes"
|
||||
if ! $DRY_RUN; then
|
||||
config_del "$tmpfile" verbose
|
||||
append_setting "$tmpfile" 'log_level=warning'
|
||||
append_setting "$tmpfile" 'report_level=changed_attributes'
|
||||
fi
|
||||
fi
|
||||
|
||||
# -----------------------------------------------------------------------
|
||||
# Remove sql: database URL lines (PostgreSQL backend removed)
|
||||
# Match lines whose value (after =) begins with sql:.
|
||||
# -----------------------------------------------------------------------
|
||||
if grep -qE '^[[:space:]]*[a-z_]+[[:space:]]*=[[:space:]]*sql:' "$tmpfile"; then
|
||||
log_action "remove sql: database URL lines"
|
||||
if $DRY_RUN; then
|
||||
# Check which keys survive after sql: removal (i.e. have at least one non-sql: value)
|
||||
grep -E '^[[:space:]]*database_out[[:space:]]*=' "$tmpfile" | \
|
||||
grep -qvE '^[[:space:]]*[a-z_]+[[:space:]]*=[[:space:]]*sql:' || \
|
||||
info " would add default database_out=file:/var/lib/aide/aide.db.new.gz"
|
||||
grep -E '^[[:space:]]*database_in[[:space:]]*=' "$tmpfile" | \
|
||||
grep -qvE '^[[:space:]]*[a-z_]+[[:space:]]*=[[:space:]]*sql:' || \
|
||||
info " would add default database_in=file:/var/lib/aide/aide.db.gz"
|
||||
fi
|
||||
if ! $DRY_RUN; then
|
||||
sed -E -i '/^[[:space:]]*[a-z_]+[[:space:]]*=[[:space:]]*sql:/d' "$tmpfile"
|
||||
config_has "$tmpfile" database_out || {
|
||||
append_setting "$tmpfile" 'database_out=file:/var/lib/aide/aide.db.new.gz'
|
||||
info " WARNING: sql: URL removed; default database_out added." \
|
||||
"Verify storage path before running aide --init."
|
||||
}
|
||||
config_has "$tmpfile" database_in || {
|
||||
append_setting "$tmpfile" 'database_in=file:/var/lib/aide/aide.db.gz'
|
||||
info " WARNING: sql: database_in removed; default database_in added." \
|
||||
"Verify path before running aide --init."
|
||||
}
|
||||
fi
|
||||
REINIT_NEEDED=true
|
||||
fi
|
||||
|
||||
# -----------------------------------------------------------------------
|
||||
# Remove 'h' from report_ignore_e2fsattrs
|
||||
# Use the sed address form to remove ALL 'h' characters from the value line.
|
||||
# -----------------------------------------------------------------------
|
||||
if grep -qE '^[[:space:]]*report_ignore_e2fsattrs[[:space:]]*=.*h' "$tmpfile"; then
|
||||
log_action "remove 'h' from report_ignore_e2fsattrs"
|
||||
if ! $DRY_RUN; then
|
||||
sed -E -i '/^[[:space:]]*report_ignore_e2fsattrs[[:space:]]*=/s/h//g' "$tmpfile"
|
||||
sed -E -i '/^[[:space:]]*report_ignore_e2fsattrs[[:space:]]*=[[:space:]]*$/d' "$tmpfile"
|
||||
fi
|
||||
fi
|
||||
|
||||
# -----------------------------------------------------------------------
|
||||
# Remove deprecated and removed hashsums
|
||||
# Process crc32b before crc32 to avoid prefix collision.
|
||||
# Character classes use dash at end to prevent GNU sed range-error.
|
||||
# -----------------------------------------------------------------------
|
||||
local hash changed_hashes=false
|
||||
for hash in crc32b crc32 tiger haval whirlpool md5 sha1 rmd160 gost; do
|
||||
if grep -v '^[[:space:]]*#' "$tmpfile" | grep -qE "(^|[+[:space:]=-])${hash}([+[:space:]-]|\$)"; then
|
||||
log_action "remove hashsum: $hash"
|
||||
if ! $DRY_RUN; then
|
||||
remove_hashsum "$tmpfile" "$hash"
|
||||
fi
|
||||
changed_hashes=true
|
||||
REINIT_NEEDED=true
|
||||
fi
|
||||
done
|
||||
|
||||
# Post-removal: fill any group definition whose RHS became empty with sha256
|
||||
if $changed_hashes; then
|
||||
if ! $DRY_RUN; then
|
||||
while IFS= read -r lineno; do
|
||||
[[ -z "$lineno" ]] && continue
|
||||
sed -i "${lineno}s/=.*/= sha256/" "$tmpfile"
|
||||
info " group on line $lineno became empty after hashsum removal; added sha256"
|
||||
done < <(grep -nE '^[A-Za-z0-9]+[[:space:]]*=[[:space:]]*[+-]?[[:space:]]*$' \
|
||||
"$tmpfile" | cut -d: -f1)
|
||||
else
|
||||
info " note: any group containing only deprecated hashsums will have sha256 substituted"
|
||||
fi
|
||||
fi
|
||||
|
||||
# -----------------------------------------------------------------------
|
||||
# Replace deprecated S attribute with growing+s
|
||||
# Applies since AIDE 0.16 is being replaced; growing+s is unknown to 0.16.
|
||||
# Character classes use dash at end to prevent GNU sed range-error.
|
||||
# -----------------------------------------------------------------------
|
||||
if grep -qE '(^|[+=[:space:]-])S([+[:space:]-]|$)' "$tmpfile"; then
|
||||
log_action "replace S attribute with growing+s"
|
||||
if ! $DRY_RUN; then
|
||||
sed -E -i \
|
||||
-e '/^[[:space:]]*#/!s/([+=[:space:]-])S([+[:space:]-]|$)/\1growing+s\2/g' \
|
||||
-e '/^[[:space:]]*#/!s/^S([+[:space:]-]|$)/growing+s\1/g' \
|
||||
"$tmpfile"
|
||||
fi
|
||||
fi
|
||||
|
||||
# -----------------------------------------------------------------------
|
||||
# Replace deprecated @@ifdef/@@ifndef/@@ifhost/@@ifnhost macros
|
||||
# -----------------------------------------------------------------------
|
||||
if grep -qE '^[[:space:]]*@@(ifdef|ifndef|ifhost|ifnhost)[[:space:]]' "$tmpfile"; then
|
||||
log_action "replace deprecated @@ifdef/@@ifndef/@@ifhost/@@ifnhost macros"
|
||||
if ! $DRY_RUN; then
|
||||
sed -E -i \
|
||||
-e 's/^([[:space:]]*)@@ifdef([[:space:]])/\1@@if defined\2/g' \
|
||||
-e 's/^([[:space:]]*)@@ifndef([[:space:]])/\1@@if not defined\2/g' \
|
||||
-e 's/^([[:space:]]*)@@ifhost([[:space:]])/\1@@if hostname\2/g' \
|
||||
-e 's/^([[:space:]]*)@@ifnhost([[:space:]])/\1@@if not hostname\2/g' \
|
||||
"$tmpfile"
|
||||
fi
|
||||
fi
|
||||
|
||||
# -----------------------------------------------------------------------
|
||||
# Ensure file ends with a newline
|
||||
# -----------------------------------------------------------------------
|
||||
local last_byte
|
||||
last_byte="$(tail -c1 "$tmpfile" | od -An -tx1 | tr -d ' \n')"
|
||||
if [[ -n "$last_byte" && "$last_byte" != '0a' ]]; then
|
||||
log_action "add missing trailing newline"
|
||||
$DRY_RUN || echo "" >> "$tmpfile"
|
||||
fi
|
||||
|
||||
# H group's content changed in 0.19; warn if used without a custom definition.
|
||||
# Only reached when the file had real 0.16-style options, so this never fires on a
|
||||
# clean 0.19 config.
|
||||
if grep -qE '(^|[+[:space:]-])H([+[:space:]-]|$)' "$tmpfile" 2>/dev/null; then
|
||||
if ! grep -qE '^[[:space:]]*H[[:space:]]*=' "$tmpfile"; then
|
||||
info "NOTE: built-in H group in use without custom definition;" \
|
||||
"H content changed in 0.19 — run 'aide --init' to rebuild the database"
|
||||
fi
|
||||
fi
|
||||
|
||||
# -----------------------------------------------------------------------
|
||||
# Commit changes
|
||||
# -----------------------------------------------------------------------
|
||||
if ! $DRY_RUN; then
|
||||
mv -- "$tmpfile" "$file"
|
||||
# Restore original permissions
|
||||
chmod --reference="${BACKUP_COPY[-1]}" "$file" 2>/dev/null || true
|
||||
else
|
||||
rm -f "$tmpfile"
|
||||
fi
|
||||
|
||||
# Disarm the local ERR trap and re-arm the global one
|
||||
trap - ERR
|
||||
trap restore_backups ERR
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Post-migration warnings
|
||||
# ---------------------------------------------------------------------------
|
||||
check_and_warn() {
|
||||
local file="$1"
|
||||
|
||||
# Warn if a rule path starts with a macro variable.
|
||||
# Only flag lines where @@{...} appears at the very start (after optional whitespace);
|
||||
# mid-path macros like /path/@@{VAR}/sub are valid and must not be flagged.
|
||||
local macro_rules
|
||||
macro_rules="$(grep -nE '^[[:space:]]*@@\{[^}]+\}' "$file" 2>/dev/null || true)"
|
||||
if [[ -n "$macro_rules" ]]; then
|
||||
info "WARNING ($file): the following rule paths start with a macro variable." \
|
||||
"Rewrite them so the path begins with a literal '/':"
|
||||
echo "$macro_rules" >&2
|
||||
fi
|
||||
|
||||
# Warn if a group name contains non-alphanumeric characters.
|
||||
# Require an uppercase first letter to avoid false positives on config option names.
|
||||
# Underscores are accepted by AIDE 0.19.2; only '-' and '.' cause parse errors.
|
||||
local bad_groups
|
||||
bad_groups="$(grep -nE '^[A-Z][A-Za-z0-9]*[-.][A-Za-z0-9][^=]*[[:space:]]*=' \
|
||||
"$file" 2>/dev/null || true)"
|
||||
if [[ -n "$bad_groups" ]]; then
|
||||
info "WARNING ($file): the following group names contain non-alphanumeric characters." \
|
||||
"Rename groups and all their references to [A-Za-z0-9] only:"
|
||||
echo "$bad_groups" >&2
|
||||
fi
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Database reinitialisation
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
# expand_aide_macros CONFIG VALUE
|
||||
# Expand @@{NAME} references in VALUE using @@define lines from CONFIG.
|
||||
expand_aide_macros() {
|
||||
local config="$1" value="$2"
|
||||
local key val
|
||||
while IFS= read -r defline; do
|
||||
key="$(echo "$defline" | awk '{print $2}')"
|
||||
val="$(echo "$defline" | awk '{$1=$2=""; print substr($0,3)}')"
|
||||
value="${value//@@\{${key}\}/$val}"
|
||||
done < <(grep -E '^[[:space:]]*@@define[[:space:]]' "$config" || true)
|
||||
echo "$value"
|
||||
}
|
||||
|
||||
# reinit_database CONFIG_FILE
|
||||
# Backup the existing database, run aide --init, move the new DB into place.
|
||||
reinit_database() {
|
||||
local config="$1"
|
||||
|
||||
# Parse database_in path (file: URLs only)
|
||||
local raw_in
|
||||
raw_in="$(grep -E '^[[:space:]]*database_in[[:space:]]*=' "$config" | \
|
||||
head -1 | sed -E 's/^[^=]+=file://')" || true
|
||||
[[ -z "$raw_in" ]] && { info "WARNING: database_in not found in config; skipping reinit"; return 0; }
|
||||
local db_in
|
||||
db_in="$(expand_aide_macros "$config" "$raw_in")"
|
||||
|
||||
# Parse database_out path (file: URLs only)
|
||||
local raw_out
|
||||
raw_out="$(grep -E '^[[:space:]]*database_out[[:space:]]*=' "$config" | \
|
||||
head -1 | sed -E 's/^[^=]+=file://')" || true
|
||||
[[ -z "$raw_out" ]] && { info "WARNING: database_out not found in config; skipping reinit"; return 0; }
|
||||
local db_out
|
||||
db_out="$(expand_aide_macros "$config" "$raw_out")"
|
||||
|
||||
if [[ ! -f "$db_in" ]]; then
|
||||
info "no existing database at $db_in; run 'aide --init -c $config' when ready"
|
||||
return 0
|
||||
fi
|
||||
|
||||
info "reinitialising database (this may take several minutes)..."
|
||||
backup_file "$db_in"
|
||||
aide --init -c "$config"
|
||||
if [[ ! -f "$db_out" ]]; then
|
||||
die "aide --init completed but output database not found at $db_out"
|
||||
fi
|
||||
mv -- "$db_out" "$db_in"
|
||||
chmod 0600 "$db_in"
|
||||
chown root:root "$db_in"
|
||||
info "database reinitialised: $db_in"
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Main
|
||||
# ---------------------------------------------------------------------------
|
||||
main() {
|
||||
# CLI flags are out of scope for this script
|
||||
info "Note: This script migrates aide config files only." \
|
||||
"If you use '--verbose' or '--report' flags in wrapper scripts," \
|
||||
"cron jobs, or systemd units, remove those flags manually."
|
||||
|
||||
# Argument parsing
|
||||
local config_file=""
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--dry-run) DRY_RUN=true; shift ;;
|
||||
--skip-init) SKIP_INIT=true; shift ;;
|
||||
-h|--help) usage ;;
|
||||
--) shift; break ;;
|
||||
-*) die "unknown option: $1" ;;
|
||||
*) config_file="$1"; shift ;;
|
||||
esac
|
||||
done
|
||||
[[ $# -gt 0 ]] && die "unexpected argument: $1"
|
||||
[[ -z "$config_file" ]] && usage
|
||||
[[ -f "$config_file" ]] || die "config file not found: $config_file"
|
||||
[[ -r "$config_file" ]] || die "config file not readable: $config_file"
|
||||
$DRY_RUN || [[ -w "$config_file" ]] || die "config file not writable: $config_file"
|
||||
|
||||
# Verify aide >= 0.19 is installed
|
||||
local aide_ver
|
||||
aide_ver="$(aide --version 2>&1 | grep -oE '[0-9]+\.[0-9]+' | head -1)" || true
|
||||
if [[ -z "$aide_ver" ]]; then
|
||||
info "aide not found; skipping migration"
|
||||
exit 0
|
||||
fi
|
||||
local aide_major aide_minor
|
||||
aide_major="${aide_ver%%.*}"
|
||||
aide_minor="${aide_ver#*.}"
|
||||
if [[ "$aide_major" -lt 1 && "$aide_minor" -lt 19 ]]; then
|
||||
info "aide $aide_ver < 0.19; skipping migration"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
$DRY_RUN && info "DRY-RUN mode: no files will be modified"
|
||||
|
||||
# Global ERR trap for cleanup
|
||||
trap restore_backups ERR
|
||||
|
||||
# Collect all config files to process
|
||||
local -a config_files=("$config_file")
|
||||
while IFS= read -r inc; do
|
||||
[[ -f "$inc" ]] && config_files+=("$inc")
|
||||
done < <(extract_includes "$config_file")
|
||||
|
||||
# Migrate each config file
|
||||
local f
|
||||
for f in "${config_files[@]}"; do
|
||||
migrate_config_file "$f"
|
||||
done
|
||||
|
||||
# Post-migration warnings
|
||||
for f in "${config_files[@]}"; do
|
||||
check_and_warn "$f"
|
||||
done
|
||||
|
||||
# Validate the resulting config
|
||||
if ! $DRY_RUN; then
|
||||
if ! aide --config-check -c "$config_file" >/dev/null 2>&1; then
|
||||
info "ERROR: aide --config-check failed after migration; restoring all backups"
|
||||
restore_backups
|
||||
exit 1
|
||||
fi
|
||||
info "aide --config-check passed"
|
||||
fi
|
||||
|
||||
# Database reinit
|
||||
if $REINIT_NEEDED && ! $SKIP_INIT && ! $DRY_RUN; then
|
||||
reinit_database "$config_file"
|
||||
elif $REINIT_NEEDED && $SKIP_INIT && ! $DRY_RUN; then
|
||||
info "database reinitialisation required but --skip-init set;" \
|
||||
"run 'aide --init -c $config_file' manually"
|
||||
elif $REINIT_NEEDED && $DRY_RUN; then
|
||||
info "would require database reinitialisation after migration"
|
||||
fi
|
||||
|
||||
info "migration complete"
|
||||
}
|
||||
|
||||
main "$@"
|
||||
@ -23,8 +23,8 @@ database_add_metadata=yes
|
||||
# Warn about unrestricted rules during config check (default: false)
|
||||
config_check_warn_unrestricted_rules=false
|
||||
|
||||
# Number of workers for parallel processing (default: 1, can use percentage)
|
||||
num_workers=1
|
||||
# Number of workers for parallel processing (AIDE default: 1, overridden to 4 here)
|
||||
num_workers=4
|
||||
|
||||
# Default.
|
||||
log_level=warning
|
||||
@ -80,20 +80,6 @@ report_url=stdout
|
||||
#stribog256: GOST R 34.11-2012, 256 bit
|
||||
#stribog512: GOST R 34.11-2012, 512 bit
|
||||
|
||||
# DEPRECATED (will be removed in future versions):
|
||||
#md5: md5 checksum (deprecated since v0.19)
|
||||
#sha1: sha1 checksum (deprecated since v0.19)
|
||||
#rmd160: rmd160 checksum (deprecated since v0.19)
|
||||
#gost: gost checksum (deprecated since v0.19)
|
||||
|
||||
# REMOVED in AIDE v0.19:
|
||||
#S: check for growing size (use 'growing+s' instead)
|
||||
#tiger: tiger checksum (removed)
|
||||
#haval: haval checksum (removed)
|
||||
#crc32: crc32 checksum (removed)
|
||||
#crc32b: crc32b checksum (removed)
|
||||
#whirlpool: whirlpool checksum (removed)
|
||||
|
||||
# Special attributes for advanced use cases:
|
||||
#I: ignore changed filename - detects moved files by inode
|
||||
#growing: ignore growing file size/timestamps for logs
|
||||
@ -138,6 +124,10 @@ LOG = p+ftype+u+g+n+ANF+ARF+selinux+xattrs
|
||||
# but we want to know when the data inside them changes - updated with modern hash
|
||||
DATAONLY = ftype+p+l+n+u+g+s+acl+selinux+xattrs+sha256
|
||||
|
||||
# Read /etc/aide.d/*.conf files
|
||||
@@include /etc/aide.d ^[a-zA-Z0-9_-]+\.conf$
|
||||
|
||||
|
||||
# Next decide what directories/files you want in the database.
|
||||
|
||||
/boot NORMAL
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
Summary: Intrusion detection environment
|
||||
Name: aide
|
||||
Version: 0.19.2
|
||||
Release: 5%{?dist}
|
||||
Release: 7%{?dist}
|
||||
URL: https://github.com/aide/aide
|
||||
License: GPLv2+
|
||||
|
||||
@ -15,6 +15,11 @@ Source3: aide.conf
|
||||
Source4: README.quickstart
|
||||
Source5: aide.logrotate
|
||||
Source6: aide-tmpfiles.conf
|
||||
Source7: aide-migrate-config
|
||||
Source8: aide-check.service
|
||||
Source9: aide-check.timer
|
||||
Patch0: aide-0.19.2-syslog-format.patch
|
||||
Patch1: aide-include-permission-checks.patch
|
||||
|
||||
BuildRequires: gcc
|
||||
BuildRequires: make
|
||||
@ -66,21 +71,57 @@ mkdir -p %{buildroot}%{_localstatedir}/log/aide
|
||||
mkdir -p -m0700 %{buildroot}%{_localstatedir}/lib/aide
|
||||
# Install tmpfiles config
|
||||
install -Dpm0644 %{SOURCE6} %{buildroot}%{_tmpfilesdir}/aide.conf
|
||||
install -Dpm0755 %{SOURCE7} %{buildroot}%{_sbindir}/aide-migrate-config
|
||||
# Create /etc/aide.d/
|
||||
mkdir -p -m0700 %{buildroot}%{_sysconfdir}/aide.d
|
||||
# Install systemd timer and service for scheduled integrity checks
|
||||
install -Dpm0644 %{SOURCE8} %{buildroot}%{_unitdir}/aide-check.service
|
||||
install -Dpm0644 %{SOURCE9} %{buildroot}%{_unitdir}/aide-check.timer
|
||||
|
||||
%files
|
||||
%license COPYING
|
||||
%doc AUTHORS ChangeLog NEWS README
|
||||
%doc README.quickstart
|
||||
%{_sbindir}/aide
|
||||
%{_sbindir}/aide-migrate-config
|
||||
%{_mandir}/man1/*.1*
|
||||
%{_mandir}/man5/*.5*
|
||||
%config(noreplace) %attr(0600,root,root) %{_sysconfdir}/aide.conf
|
||||
%config(noreplace) %{_sysconfdir}/logrotate.d/aide
|
||||
%dir %attr(0700,root,root) %{_sysconfdir}/aide.d
|
||||
%dir %attr(0700,root,root) %{_localstatedir}/lib/aide
|
||||
%dir %attr(0700,root,root) %{_localstatedir}/log/aide
|
||||
%{_tmpfilesdir}/aide.conf
|
||||
%{_unitdir}/aide-check.service
|
||||
%{_unitdir}/aide-check.timer
|
||||
|
||||
%post
|
||||
%systemd_post aide-check.timer
|
||||
if [ $1 -ge 2 ]; then
|
||||
/usr/sbin/aide-migrate-config /etc/aide.conf 2>&1 | \
|
||||
tee -a /var/log/aide/aide-migrate.log || :
|
||||
fi
|
||||
|
||||
%preun
|
||||
%systemd_preun aide-check.timer
|
||||
|
||||
%postun
|
||||
%systemd_postun_with_restart aide-check.timer
|
||||
|
||||
%changelog
|
||||
* Tue Jun 03 2026 Patrik Koncity <pkoncity@redhat.com> - 0.19.2-7
|
||||
- Support for included files in /etc/aide.d/
|
||||
Resolves: RHEL-178122
|
||||
- Increase default values for num_workers
|
||||
Resolves: RHEL-178123
|
||||
- Add pre-configured systemd timer for aide check
|
||||
Resolves: RHEL-178121
|
||||
|
||||
* Tue May 26 2026 Attila Lakatos <alakatos@redhat.com> - 0.19.2-6
|
||||
- Add aide-migrate-config to automate config migration from pre-0.19 syntax
|
||||
Resolves: RHEL-178317
|
||||
- Re-add syslog_format config option dropped during rebase to 0.19.2
|
||||
|
||||
* Wed Oct 15 2025 Attila Lakatos <alakatos@redhat.com> - 0.19.2-5
|
||||
- Adjust default config to avoid false positives in /etc
|
||||
Resolves: RHEL-83776
|
||||
|
||||
Loading…
Reference in New Issue
Block a user