From 2256b37dc039b6b09f1a98c665e640e3e030cdc0 Mon Sep 17 00:00:00 2001 From: JuanGarriuz Date: Wed, 24 Apr 2024 18:16:14 +0200 Subject: [PATCH] Implement journald log collection feature (#6572) * Added tab * Add journald tab * Added changes to imposter * journald values filters table * Update changelog and redesign filters table * Fixed styles * Fixed hardcode bug * Added filters groups * Change to an accordion render * Added helps-link * Added changelog * Fix changelog and message popover improve * Update configuration-setting imports * Fix macOS log title * resolve comments * Fixed info euitext render and header no render in journald tab * Add verification to mac and journald agents and add condition to journald --------- Co-authored-by: Federico Rodriguez --- CHANGELOG.md | 1 + .../configuration/logcollector-localfile.json | 132 +++++++++-------- .../log-collection/log-collection-journald.js | 138 ++++++++++++++++++ .../log-collection-macosevents.js | 14 +- .../log-collection/log-collection.js | 21 ++- .../configuration/log-collection/types.js | 1 + .../util-components/configuration-setting.js | 76 ++++++++-- .../configuration-settings-group.js | 28 +--- .../configuration-settings-header.js | 31 ++-- .../util-components/help-button-popover.js | 39 +++-- 10 files changed, 345 insertions(+), 136 deletions(-) create mode 100644 plugins/main/public/controllers/management/components/management/configuration/log-collection/log-collection-journald.js diff --git a/CHANGELOG.md b/CHANGELOG.md index 10b54e5a60..b769600104 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ All notable changes to the Wazuh app project will be documented in this file. - Added propagation of updates from the table to dashboard visualizations in Endpoints summary [#6460](https://github.com/wazuh/wazuh-dashboard-plugins/pull/6460) - Handle index pattern selector on new discover [#6499](https://github.com/wazuh/wazuh-dashboard-plugins/pull/6499) - Added macOS log collector tab [#6545](https://github.com/wazuh/wazuh-dashboard-plugins/pull/6545) +- Added journald log collector tab [#6572](https://github.com/wazuh/wazuh-dashboard-plugins/pull/6572) ### Changed diff --git a/docker/imposter/agents/configuration/logcollector-localfile.json b/docker/imposter/agents/configuration/logcollector-localfile.json index c61d46743d..a9f45d284f 100644 --- a/docker/imposter/agents/configuration/logcollector-localfile.json +++ b/docker/imposter/agents/configuration/logcollector-localfile.json @@ -1,31 +1,71 @@ { "data": { "localfile": [ + { + "logformat": "journald", + "ignore_binaries": "no", + "only-future-events": "no", + "target": ["agent1"], + "filters": [ + [ + { + "field": "_KERNEL_DEVICE", + "expression": ".kernel1", + "ignore_if_missing": false + } + ], + [ + { + "field": "_SYSTEMD_UNIT", + "expression": "^cron.service$", + "ignore_if_missing": false + }, + { + "field": "CUSTOM", + "expression": "0|1|2", + "ignore_if_missing": true + } + ] + ], + "filters_disabled": false + }, + { + "logformat": "journald", + "ignore_binaries": "no", + "only-future-events": "yes", + "target": ["agent2"] + }, + { + "logformat": "journald", + "ignore_binaries": "no", + "only-future-events": "yes", + "target": ["agent3"], + "filters": [ + { + "field": "_KERNEL_DEVICE", + "expression": ".", + "ignore_if_missing": false + } + ], + "filters_disabled": false + }, { "logformat": "macos", "query": { "value": "(process == \"sudo\") or (process == \"sessionlogoutd\" and message contains \"logout is complete.\") or (process == \"sshd\") or (process == \"tccd\" and message contains \"Update Access Record\") or (message contains \"SessionAgentNotificationCenter\") or (process == \"screensharingd\" and message contains \"Authentication\") or (process == \"securityd\" and eventMessage contains \"Session\" and subsystem == \"com.apple.securityd\")", "level": "info", - "type": [ - "log", - "activity", - "trace" - ] + "type": ["log", "activity", "trace"] }, "ignore_binaries": "no", "only-future-events": "yes", - "target": [ - "agent" - ] + "target": ["agent"] }, { "logformat": "command", "command": "df -P", "alias": "df -P", "ignore_binaries": "no", - "target": [ - "agent" - ], + "target": ["agent"], "frequency": 360 }, { @@ -33,9 +73,7 @@ "command": "netstat -tulpn | sed 's/\\([[:alnum:]]\\+\\)\\ \\+[[:digit:]]\\+\\ \\+[[:digit:]]\\+\\ \\+\\(.*\\):\\([[:digit:]]*\\)\\ \\+\\([0-9\\.\\:\\*]\\+\\).\\+\\ \\([[:digit:]]*\\/[[:alnum:]\\-]*\\).*/\\1 \\2 == \\3 == \\4 \\5/' | sort -k 4 -g | sed 's/ == \\(.*\\) ==/:\\1/' | sed 1,2d", "alias": "netstat listening ports", "ignore_binaries": "no", - "target": [ - "agent" - ], + "target": ["agent"], "frequency": 360 }, { @@ -43,9 +81,7 @@ "command": "last -n 20", "alias": "last -n 20", "ignore_binaries": "no", - "target": [ - "agent" - ], + "target": ["agent"], "frequency": 360 }, { @@ -53,80 +89,62 @@ "logformat": "syslog", "ignore_binaries": "no", "only-future-events": "yes", - "target": [ - "agent" - ] + "target": ["agent"] }, { "file": "/var/log/nginx/access.log", "logformat": "apache", "ignore_binaries": "no", "only-future-events": "yes", - "target": [ - "agent" - ] + "target": ["agent"] }, { "file": "/var/log/nginx/error.log", "logformat": "apache", "ignore_binaries": "no", "only-future-events": "yes", - "target": [ - "agent" - ] + "target": ["agent"] }, { "file": "/var/ossec/logs/active-responses.log", "logformat": "syslog", "ignore_binaries": "no", "only-future-events": "yes", - "target": [ - "agent" - ] + "target": ["agent"] }, { "file": "/var/log/auth.log", "logformat": "syslog", "ignore_binaries": "no", "only-future-events": "yes", - "target": [ - "agent" - ] + "target": ["agent"] }, { "file": "/var/log/syslog", "logformat": "syslog", "ignore_binaries": "no", "only-future-events": "yes", - "target": [ - "agent" - ] + "target": ["agent"] }, { "file": "/var/log/dpkg.log", "logformat": "syslog", "ignore_binaries": "no", "only-future-events": "yes", - "target": [ - "agent" - ] + "target": ["agent"] }, { "file": "/var/log/kern.log", "logformat": "syslog", "ignore_binaries": "no", "only-future-events": "yes", - "target": [ - "agent" - ] + "target": ["agent"] }, { "channel": "Application", "logformat": "eventlog", "ignore_binaries": "no", - "target": [ - "agent" - ] + "target": ["agent"] }, { "channel": "Security", @@ -136,36 +154,28 @@ }, "ignore_binaries": "no", "only-future-events": "yes", - "target": [ - "agent" - ], + "target": ["agent"], "reconnect_time": 5 }, { "channel": "System", "logformat": "eventlog", "ignore_binaries": "no", - "target": [ - "agent" - ] + "target": ["agent"] }, { "file": "active-response\\active-responses.log", "logformat": "syslog", "ignore_binaries": "no", "only-future-events": "yes", - "target": [ - "agent" - ] + "target": ["agent"] }, { "channel": "Microsoft-Windows-Sysmon/Operational", "logformat": "eventchannel", "ignore_binaries": "no", "only-future-events": "yes", - "target": [ - "agent" - ], + "target": ["agent"], "reconnect_time": 5 }, { @@ -173,9 +183,7 @@ "logformat": "eventchannel", "ignore_binaries": "no", "only-future-events": "yes", - "target": [ - "agent" - ], + "target": ["agent"], "reconnect_time": 5 }, { @@ -183,11 +191,9 @@ "logformat": "iis", "ignore_binaries": "no", "only-future-events": "yes", - "target": [ - "agent" - ] + "target": ["agent"] } ] }, "error": 0 -} \ No newline at end of file +} diff --git a/plugins/main/public/controllers/management/components/management/configuration/log-collection/log-collection-journald.js b/plugins/main/public/controllers/management/components/management/configuration/log-collection/log-collection-journald.js new file mode 100644 index 0000000000..400bceb45c --- /dev/null +++ b/plugins/main/public/controllers/management/components/management/configuration/log-collection/log-collection-journald.js @@ -0,0 +1,138 @@ +/* + * Wazuh app - React component for show configuration of log collection - commands tab. + * Copyright (C) 2015-2022 Wazuh, Inc. + * + * 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. + * + * Find more information about this on the LICENSE file. + */ + +import React, { Component, Fragment } from 'react'; + +import WzNoConfig from '../util-components/no-config'; +import WzConfigurationSettingsHeader from '../util-components/configuration-settings-header'; +import WzConfigurationListSelector from '../util-components/configuration-settings-list-selector'; +import WzConfigurationSettingsGroup from '../util-components/configuration-settings-group'; +import { + renderValueOrNoValue, + isString, + renderValueOrDefault, +} from '../utils/utils'; +import { settingsListBuilder } from '../utils/builders'; +import helpLinks from './help-links'; +import { LOGCOLLECTOR_LOCALFILE_PROP, LOCALFILE_JOURNALDT_PROP } from './types'; + +/** + * Returns targets array parsed in one string + * @param {*} item + * @returns string => target + */ +const renderTargetField = item => + Array.isArray(item) ? item.join(', ') : 'agent'; + +/** + * Return panels title + * @param {*} item => log data + * @returns + */ +const panelsLabel = item => + `${item.logformat} - ${renderTargetField(item.target)}`; + +const mainSettings = [ + { field: 'logformat', label: 'Log format' }, + { + field: 'only-future-events', + label: 'Only future events', + render: renderValueOrNoValue, + }, + { + field: 'filters_disabled', + label: 'Filters Disabled', + render: renderValueOrDefault('true'), + }, + { + field: 'filters', + label: 'Filters', + columns: [ + { + field: 'field', + name: 'Field', + }, + { + field: 'expression', + name: 'Expression', + }, + { + field: 'ignore_if_missing', + name: 'Ignore If Missing', + }, + ], + info: 'The configuration filters within the same group are processed with an AND logic operator. Whereas the different filter groups are processed with an OR like logic operator.', + }, +]; + +class WzConfigurationLogCollectionJournald extends Component { + constructor(props) { + super(props); + } + render() { + const { currentConfig } = this.props; + const items = currentConfig?.[LOGCOLLECTOR_LOCALFILE_PROP]?.[ + LOCALFILE_JOURNALDT_PROP + ] + ? settingsListBuilder( + currentConfig[LOGCOLLECTOR_LOCALFILE_PROP][LOCALFILE_JOURNALDT_PROP], + panelsLabel, + ) + : []; + + return ( + + {isString(currentConfig?.[LOGCOLLECTOR_LOCALFILE_PROP]) && ( + + )} + {!currentConfig?.[LOGCOLLECTOR_LOCALFILE_PROP]?.[ + LOCALFILE_JOURNALDT_PROP + ]?.length ? ( + + ) : null} + {currentConfig?.[LOGCOLLECTOR_LOCALFILE_PROP]?.[ + LOCALFILE_JOURNALDT_PROP + ]?.length > 1 ? ( + + + + ) : null} + {currentConfig?.[LOGCOLLECTOR_LOCALFILE_PROP]?.[ + LOCALFILE_JOURNALDT_PROP + ]?.length === 1 ? ( + + + + ) : null} + + ); + } +} + +export default WzConfigurationLogCollectionJournald; diff --git a/plugins/main/public/controllers/management/components/management/configuration/log-collection/log-collection-macosevents.js b/plugins/main/public/controllers/management/components/management/configuration/log-collection/log-collection-macosevents.js index ebb7bb14dd..1304c3fc0a 100644 --- a/plugins/main/public/controllers/management/components/management/configuration/log-collection/log-collection-macosevents.js +++ b/plugins/main/public/controllers/management/components/management/configuration/log-collection/log-collection-macosevents.js @@ -115,10 +115,16 @@ class WzConfigurationLogCollectionMacOSEvents extends Component { {currentConfig?.[LOGCOLLECTOR_LOCALFILE_PROP]?.[ LOCALFILE_MACOSEVENT_PROP ]?.length === 1 ? ( - + + + ) : null} ); diff --git a/plugins/main/public/controllers/management/components/management/configuration/log-collection/log-collection.js b/plugins/main/public/controllers/management/components/management/configuration/log-collection/log-collection.js index 52c0ee029a..2165ca50b8 100644 --- a/plugins/main/public/controllers/management/components/management/configuration/log-collection/log-collection.js +++ b/plugins/main/public/controllers/management/components/management/configuration/log-collection/log-collection.js @@ -20,6 +20,7 @@ import WzConfigurationLogCollectionCommands from './log-collection-commands'; import WzConfigurationLogCollectionWindowsEvents from './log-collection-windowsevents'; import WzConfigurationLogCollectionMacOSEvents from './log-collection-macosevents'; import WzConfigurationLogCollectionSockets from './log-collection-sockets'; +import WzConfigurationLogCollectionJournald from './log-collection-journald'; import withWzConfig from '../util-hocs/wz-config'; import { isString } from '../utils/utils'; import { @@ -28,6 +29,7 @@ import { LOCALFILE_WINDOWSEVENT_PROP, LOGCOLLECTOR_LOCALFILE_PROP, LOCALFILE_MACOSEVENT_PROP, + LOCALFILE_JOURNALDT_PROP, } from './types'; class WzConfigurationLogCollection extends Component { @@ -56,6 +58,9 @@ class WzConfigurationLogCollection extends Component { [LOCALFILE_MACOSEVENT_PROP]: currentConfig[ LOGCOLLECTOR_LOCALFILE_PROP ].localfile.filter(item => item.logformat === 'macos'), + [LOCALFILE_JOURNALDT_PROP]: currentConfig[ + LOGCOLLECTOR_LOCALFILE_PROP + ].localfile.filter(item => item.logformat === 'journald'), [LOCALFILE_COMMANDS_PROP]: currentConfig[ LOGCOLLECTOR_LOCALFILE_PROP ].localfile.filter( @@ -101,7 +106,7 @@ class WzConfigurationLogCollection extends Component { condition: currentConfig[LOGCOLLECTOR_LOCALFILE_PROP] && currentConfig[LOGCOLLECTOR_LOCALFILE_PROP][LOCALFILE_MACOSEVENT_PROP] - .length > 0, + ?.length > 0, component: ( ), }, + { + condition: + currentConfig[LOGCOLLECTOR_LOCALFILE_PROP] && + currentConfig[LOGCOLLECTOR_LOCALFILE_PROP][LOCALFILE_JOURNALDT_PROP] + ?.length > 0, + component: ( + + + + ), + }, { condition: currentConfig[LOGCOLLECTOR_LOCALFILE_PROP] && diff --git a/plugins/main/public/controllers/management/components/management/configuration/log-collection/types.js b/plugins/main/public/controllers/management/components/management/configuration/log-collection/types.js index 57d22b9412..b7a4c7a1ea 100644 --- a/plugins/main/public/controllers/management/components/management/configuration/log-collection/types.js +++ b/plugins/main/public/controllers/management/components/management/configuration/log-collection/types.js @@ -5,3 +5,4 @@ export const LOCALFILE_LOGS_PROP = 'localfile-logs'; export const LOCALFILE_WINDOWSEVENT_PROP = 'localfile-windowsevent'; export const LOCALFILE_COMMANDS_PROP = 'localfile-commands'; export const LOCALFILE_MACOSEVENT_PROP = 'localfile-macosevent'; +export const LOCALFILE_JOURNALDT_PROP = 'localfile-journald'; diff --git a/plugins/main/public/controllers/management/components/management/configuration/util-components/configuration-setting.js b/plugins/main/public/controllers/management/components/management/configuration/util-components/configuration-setting.js index c19d56819f..3714fa232a 100644 --- a/plugins/main/public/controllers/management/components/management/configuration/util-components/configuration-setting.js +++ b/plugins/main/public/controllers/management/components/management/configuration/util-components/configuration-setting.js @@ -10,10 +10,11 @@ * Find more information about this on the LICENSE file. */ -import React, { Component, Fragment } from 'react'; +import React, { Component } from 'react'; import PropTypes from 'prop-types'; - -import { EuiFieldText, EuiSpacer, EuiTextAlign } from '@elastic/eui'; +import { EuiFieldText, EuiSpacer, EuiTextAlign, EuiAccordion, EuiBasicTable } from '@elastic/eui'; +import WzConfigurationSettingsHeader from '../util-components/configuration-settings-header'; +import helpLinks from '../log-collection/help-links'; class WzConfigurationSetting extends Component { constructor(props) { @@ -36,9 +37,9 @@ class WzConfigurationSetting extends Component { } render() { const { isMobile } = this.state; - const { keyItem, label, value } = this.props; + const { keyItem, label, value, columns, info } = this.props; return value || typeof value === 'number' || typeof value === 'boolean' ? ( - + <>
+ {columns ? ( + [] + ) : ( +
+ + {label} + +
+ )} +
- {label} -
-
- {Array.isArray(value) ? ( + {Array.isArray(value) && typeof value[0] === 'string' ? (
    {value.map((v, key) => (
  • @@ -64,17 +76,51 @@ class WzConfigurationSetting extends Component {
  • ))}
+ ) : Array.isArray(value) && columns ? ( + <> + + {value.map((group, groupIndex) => ( + +
+ {Array.isArray(group) ? ( + + ) : ( + + )} +
+
+ ))} + ) : ( )}
- -
+ + ) : null; } } diff --git a/plugins/main/public/controllers/management/components/management/configuration/util-components/configuration-settings-group.js b/plugins/main/public/controllers/management/components/management/configuration/util-components/configuration-settings-group.js index 37fa2ef33b..adbd4ea6a3 100644 --- a/plugins/main/public/controllers/management/components/management/configuration/util-components/configuration-settings-group.js +++ b/plugins/main/public/controllers/management/components/management/configuration/util-components/configuration-settings-group.js @@ -13,13 +13,7 @@ import React, { Component, Fragment } from 'react'; import PropTypes from 'prop-types'; import { get } from 'lodash'; - -import { - EuiFlexGroup, - EuiFlexItem, - EuiSpacer, -} from '@elastic/eui'; - +import { EuiFlexGroup, EuiFlexItem, EuiSpacer } from '@elastic/eui'; import WzConfigurationSetting from './configuration-setting'; import WzConfigurationSettingsHeader from './configuration-settings-header'; @@ -28,13 +22,7 @@ class WzSettingsGroup extends Component { super(props); } render() { - const { - config, - description, - items, - help, - title - } = this.props; + const { config, description, items, help, title } = this.props; return ( - + {items.map((item, key) => { @@ -59,11 +47,9 @@ class WzSettingsGroup extends Component { ? item.renderLabel(value, item, config) : item.label } - value={ - item.render - ? item.render(value) - : value - } + value={item.render ? item.render(value) : value} + columns={item.columns} + info={item.info} /> ); })} @@ -76,7 +62,7 @@ class WzSettingsGroup extends Component { WzSettingsGroup.propTypes = { ...WzConfigurationSettingsHeader.propTypes, - items: PropTypes.array.isRequired + items: PropTypes.array.isRequired, }; export default WzSettingsGroup; diff --git a/plugins/main/public/controllers/management/components/management/configuration/util-components/configuration-settings-header.js b/plugins/main/public/controllers/management/components/management/configuration/util-components/configuration-settings-header.js index a7e58df29c..4e879e3682 100644 --- a/plugins/main/public/controllers/management/components/management/configuration/util-components/configuration-settings-header.js +++ b/plugins/main/public/controllers/management/components/management/configuration/util-components/configuration-settings-header.js @@ -19,7 +19,7 @@ import { EuiHorizontalRule, EuiSpacer, EuiText, - EuiTitle + EuiTitle, } from '@elastic/eui'; import WzHelpButtonPopover from './help-button-popover'; @@ -29,38 +29,33 @@ class WzConfigurationSettingsHeader extends Component { super(props); } render() { - const { - title, - description, - help, - children - } = this.props; + const { title, description, help, children, info } = this.props; return ( - + - +

{title}

- {description && {description}} + {description && {description}}
- {help && ( + {(help || info) && ( - + )}
- + {title && ( - + )} {children}
@@ -70,7 +65,7 @@ class WzConfigurationSettingsHeader extends Component { WzConfigurationSettingsHeader.propTypes = { title: PropTypes.string, - description: PropTypes.string + description: PropTypes.string, }; export default WzConfigurationSettingsHeader; diff --git a/plugins/main/public/controllers/management/components/management/configuration/util-components/help-button-popover.js b/plugins/main/public/controllers/management/components/management/configuration/util-components/help-button-popover.js index ed2451cbd8..3d258bac27 100644 --- a/plugins/main/public/controllers/management/components/management/configuration/util-components/help-button-popover.js +++ b/plugins/main/public/controllers/management/components/management/configuration/util-components/help-button-popover.js @@ -19,7 +19,7 @@ class WzHelpButtonPopover extends Component { constructor(props) { super(props); this.state = { - showHelp: false + showHelp: false, }; } toggleShowHelp() { @@ -27,14 +27,14 @@ class WzHelpButtonPopover extends Component { } render() { const { showHelp } = this.state; - const { children, links } = this.props; + const { children, links, info } = this.props; return ( this.toggleShowHelp()} /> } @@ -42,16 +42,27 @@ class WzHelpButtonPopover extends Component { closePopover={() => this.toggleShowHelp()} >
- + More info about this section - {links.map(link => ( -
- - {link.text} - -
- ))} + <> + {info ? ( + {info} + ) : null} + {Array.isArray(links) + ? links.map(link => ( +
+ + {link.text} + +
+ )) + : null} +
); @@ -59,7 +70,7 @@ class WzHelpButtonPopover extends Component { } WzHelpButtonPopover.propTypes = { - links: PropTypes.array + links: PropTypes.array, }; export default WzHelpButtonPopover;