Skip to content

Commit

Permalink
Merge pull request #6 from airbus-cyber/single_message
Browse files Browse the repository at this point in the history
Add Single message notification
  • Loading branch information
tomasnk authored Nov 20, 2019
2 parents b59d787 + 2e02e69 commit fc82b95
Show file tree
Hide file tree
Showing 7 changed files with 148 additions and 26 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,5 @@ node_modules/
node/
build/
cache/
.idea/
/graylog-plugin-logging-alert.iml
7 changes: 6 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,9 +55,14 @@ The parameter **Split Fields** allow you to split the alert based on message fie

The parameter **Aggregation Time Range** allow you to aggregate alerts received in the given number of minutes. Thus, the alerts are logged with the same alert id during the time range.

![](https://raw.githubusercontent.com/airbus-cyber/graylog-plugin-logging-alert/master/images/edit_notification.png)

The parameter **Single message** allow you to sent only one notification by alert

![](https://raw.githubusercontent.com/airbus-cyber/graylog-plugin-logging-alert/master/images/edit_notification2.png)

You can optionally add any **Comment** about the configuration of the notification.

![](https://raw.githubusercontent.com/airbus-cyber/graylog-plugin-logging-alert/master/images/edit_notification.png)

Make sure you also configured alert conditions for the stream so that the alerts are actually triggered.

Expand Down
Binary file modified images/edit_notification.png
100755 → 100644
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added images/edit_notification2.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@

<groupId>com.airbus-cyber-security.graylog</groupId>
<artifactId>graylog-plugin-logging-alert</artifactId>
<version>1.2.0</version>
<version>1.3.0-beta</version>
<packaging>jar</packaging>

<name>${project.artifactId}</name>
Expand Down
69 changes: 57 additions & 12 deletions src/main/java/com/airbus_cyber_security/graylog/LoggingAlert.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,7 @@
import org.graylog2.plugin.configuration.Configuration;
import org.graylog2.plugin.configuration.ConfigurationException;
import org.graylog2.plugin.configuration.ConfigurationRequest;
import org.graylog2.plugin.configuration.fields.ConfigurationField;
import org.graylog2.plugin.configuration.fields.DropdownField;
import org.graylog2.plugin.configuration.fields.ListField;
import org.graylog2.plugin.configuration.fields.NumberField;
import org.graylog2.plugin.configuration.fields.TextField;
import org.graylog2.plugin.configuration.fields.*;
import org.graylog2.plugin.indexer.searches.timeranges.AbsoluteRange;
import org.graylog2.plugin.indexer.searches.timeranges.InvalidRangeParametersException;
import org.graylog2.plugin.indexer.searches.timeranges.RelativeRange;
Expand All @@ -42,6 +38,7 @@
import com.floreysoft.jmte.Engine;
import com.google.common.collect.Maps;
import com.google.inject.Inject;
import scala.xml.Null;

/**
* This is the plugin. Your class should implement one of the existing plugin
Expand All @@ -57,6 +54,8 @@ public class LoggingAlert implements AlarmCallback{
private static final String FIELD_SPLIT = "split_fields";
private static final String FIELD_COMMENT = "comment";
private static final String FIELD_AGGREGATION_TIME = "aggregation_time";
private static final String FIELD_SINGLE_MESSAGE = "single_notification";
private static final String FIELD_TAG = "alert_tag";

private static final String SEPARATOR_TEMPLATE = "\n";

Expand Down Expand Up @@ -110,11 +109,28 @@ private Map<String, Object> getModel(Stream stream, AlertCondition.CheckResult c

return model;
}

private Map<String, Object> getModel(Stream stream, AlertCondition.CheckResult checkResult,
ArrayList <Message> messages, LoggingAlertFields loggingAlertFields) {
Map<String, Object> model = new HashMap<>();
model.put("messages", messages);
model.put("stream", stream);
model.put("check_result", checkResult);
model.put("alertCondition", checkResult.getTriggeredCondition());
model.put("logging_alert", loggingAlertFields);

return model;
}

private String buildBody(Stream stream, AlertCondition.CheckResult checkResult, Message message, LoggingAlertFields loggingAlertFields) {
Map<String, Object> model = getModel(stream, checkResult, message, loggingAlertFields);
return this.templateEngine.transform(configs.getString(FIELD_BODY).replace(SEPARATOR_TEMPLATE, separator), model);
}

private String buildBody(Stream stream, AlertCondition.CheckResult checkResult, ArrayList<Message> messages, LoggingAlertFields loggingAlertFields) {
Map<String, Object> model = getModel(stream, checkResult, messages, loggingAlertFields);
return this.templateEngine.transform(configs.getString(FIELD_BODY).replace(SEPARATOR_TEMPLATE, separator), model);
}


private String getAggregationAlertID(Stream stream, DateTime date, String sufixID) {
Expand Down Expand Up @@ -358,20 +374,38 @@ public void call(Stream stream, CheckResult result)
String messageToLog=buildBody(stream, result, new Message("Empty message", "LoggingAlert", date), loggingAlertFields);
listMessagesToLog.add(messageToLog);
}else {
Map<String, LoggingAlertFields> listOfloggingAlertField = getListOfloggingAlertField(stream, result, date);
for (MessageSummary messageSummary : listMsgSummary) {
String valuesAggregationField = getValuesAggregationField(messageSummary);
String messageToLog=buildBody(stream, result, messageSummary.getRawMessage(),
listOfloggingAlertField.get(valuesAggregationField));
if(configs.getBoolean(FIELD_SINGLE_MESSAGE)){
LoggingAlertFields loggingAlertFields= new LoggingAlertFields( getAlertID(stream, result, ""),
getGraylogID(stream, result), configs.getString(FIELD_SEVERITY), date, getAlertUrl(stream, result), getStreamSearchUrl(stream, result, date));
ArrayList <Message> listMessages = new ArrayList<>();
for (MessageSummary messageSummary : listMsgSummary) {
listMessages.add(messageSummary.getRawMessage());
}
String messageToLog = buildBody(stream, result, listMessages, loggingAlertFields);
listMessagesToLog.add(messageToLog);
}else {
Map<String, LoggingAlertFields> listOfloggingAlertField = getListOfloggingAlertField(stream, result, date);
for (MessageSummary messageSummary : listMsgSummary) {
String valuesAggregationField = getValuesAggregationField(messageSummary);
String messageToLog = buildBody(stream, result, messageSummary.getRawMessage(),
listOfloggingAlertField.get(valuesAggregationField));
listMessagesToLog.add(messageToLog);
}
}
}

Logger localLogger;
if(configs.getString(FIELD_TAG) != null && !configs.getString(FIELD_TAG).isEmpty()){
localLogger = LoggerFactory.getLogger(configs.getString(FIELD_TAG));
}else{
localLogger = LOGGER;
}

/* Log each messages */
int iter = 0;
for (String message : listMessagesToLog) {
if(limitOverflow <= 0 || iter < limitOverflow) {
LOGGER.info(message);
localLogger.info(message);
} else {
LOGGER_OVERFLOW.info(message);
}
Expand Down Expand Up @@ -438,7 +472,18 @@ public ConfigurationRequest getRequestedConfiguration() {
"Aggregate alerts received in the given number of minutes by logging alerts with the same alert id",
ConfigurationField.Optional.OPTIONAL,
NumberField.Attribute.ONLY_POSITIVE));


configurationRequest.addField(new TextField(FIELD_TAG,
"Alert Tag",
configGeneral.accessAlertTag(),
"The tag of the generated logs",
ConfigurationField.Optional.OPTIONAL));

configurationRequest.addField(new BooleanField(FIELD_SINGLE_MESSAGE,
"Single message",
false,
"Check this box to send only one message by alert"));

configurationRequest.addField(new TextField(FIELD_COMMENT,
"Comment",
"",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -149,13 +149,15 @@ public void setUp() throws Exception {
}

private Map<String, Object> getConfigMap(String severity, String body,
List<String> aggregationField, int aggregationTime, int limitOverflow) {
List<String> aggregationField, int aggregationTime, int limitOverflow, String alertTag, boolean singleNotification) {
Map<String, Object> parameters = Maps.newHashMap();
parameters.put("severity", severity);
parameters.put("content", body);
parameters.put("split_fields", aggregationField);
parameters.put("aggregation_time", aggregationTime);
parameters.put("limit_overflow", limitOverflow);
parameters.put("alert_tag", alertTag);
parameters.put("single_notification", singleNotification);
return parameters;
}

Expand All @@ -172,7 +174,7 @@ private void initializeConfiguration(Map<String, Object> configMap) throws Alarm
}

private void initializeSimpleConfiguration() throws AlarmCallbackConfigurationException {
initializeConfiguration(getConfigMap("info", BODY_TEMPLATE, Collections.emptyList(), 0, 0));
initializeConfiguration(getConfigMap("info", BODY_TEMPLATE, Collections.emptyList(), 0, 0, null, false));
}

@Test
Expand Down Expand Up @@ -318,7 +320,7 @@ public void callWithAdditionalField() throws AlarmCallbackException, AlarmCallba
public void testAggregationWith1Field() throws AlarmCallbackException, AlarmCallbackConfigurationException {
List<String> listAggegationFields = Collections.singletonList(USER);
initializeConfiguration(getConfigMap("info", "alert_id: ${logging_alert.id}" + SEPARATOR_TEMPLATE +
"user: ${message.fields.user}"+SEPARATOR_TEMPLATE+"ip_src: ${message.fields.ip_src}", listAggegationFields, 0, 0));
"user: ${message.fields.user}"+SEPARATOR_TEMPLATE+"ip_src: ${message.fields.ip_src}", listAggegationFields, 0, 0,null, false));

Stream stream = mock(Stream.class);

Expand Down Expand Up @@ -376,7 +378,7 @@ public void testAggregationWith1Field() throws AlarmCallbackException, AlarmCall
public void testDeduped() throws AlarmCallbackException, AlarmCallbackConfigurationException {
List<String> listAggegationFields = Collections.singletonList(USER);
initializeConfiguration(getConfigMap("info", "alert_id: ${logging_alert.id}" + SEPARATOR_TEMPLATE +
"user: ${message.fields.user}", listAggegationFields, 0, 0));
"user: ${message.fields.user}", listAggegationFields, 0, 0,null, false));

Stream stream = mock(Stream.class);

Expand Down Expand Up @@ -430,7 +432,7 @@ public void testDeduped() throws AlarmCallbackException, AlarmCallbackConfigura
public void testAggregationWithMultipleField() throws AlarmCallbackException, AlarmCallbackConfigurationException {
List<String> listAggegationFields = Arrays.asList(USER,"ip_src");
initializeConfiguration(getConfigMap("info", "alert_id: ${logging_alert.id}" + SEPARATOR_TEMPLATE +
"user: ${message.fields.user}" +SEPARATOR_TEMPLATE+"ip_src: ${message.fields.ip_src}", listAggegationFields, 0, 0));
"user: ${message.fields.user}" +SEPARATOR_TEMPLATE+"ip_src: ${message.fields.ip_src}", listAggegationFields, 0, 0,null, false));

Stream stream = mock(Stream.class);

Expand Down Expand Up @@ -490,7 +492,7 @@ public void testAggregationWithMultipleField() throws AlarmCallbackException, Al
public void testWithAggregationTime() throws AlarmCallbackException, AlarmCallbackConfigurationException {
List<String> listAggegationFields = Collections.singletonList(USER);
initializeConfiguration(getConfigMap("info", "alert_id: ${logging_alert.id}" + SEPARATOR_TEMPLATE +
"user: ${message.fields.user}"+SEPARATOR_TEMPLATE+"ip_src: ${message.fields.ip_src}", listAggegationFields, 15, 0));
"user: ${message.fields.user}"+SEPARATOR_TEMPLATE+"ip_src: ${message.fields.ip_src}", listAggegationFields, 15, 0,null, false));

Stream stream = mock(Stream.class);

Expand Down Expand Up @@ -558,7 +560,7 @@ public void testWithAggregationTime() throws AlarmCallbackException, AlarmCallba
public void testWithFieldAlerId() throws AlarmCallbackException, AlarmCallbackConfigurationException {
List<String> listAggegationFields = Collections.singletonList("user");
initializeConfiguration(getConfigMap("info", "alert_id: ${logging_alert.id}" + SEPARATOR_TEMPLATE +
"user: ${message.fields.user}"+SEPARATOR_TEMPLATE+"ip_src: ${message.fields.ip_src}",listAggegationFields, 0, 0));
"user: ${message.fields.user}"+SEPARATOR_TEMPLATE+"ip_src: ${message.fields.ip_src}",listAggegationFields, 0, 0,null, false));

Stream stream = mock(Stream.class);

Expand Down Expand Up @@ -732,7 +734,7 @@ public void testLimitOverflow() throws AlarmCallbackException, AlarmCallbackConf

List<String> listAggegationFields = Collections.singletonList(USER);
initializeConfiguration(getConfigMap("info", "alert_id: ${logging_alert.id}" + SEPARATOR_TEMPLATE +
"user: ${message.fields.user}"+SEPARATOR_TEMPLATE+"ip_src: ${message.fields.ip_src}", listAggegationFields, 0, 2));
"user: ${message.fields.user}"+SEPARATOR_TEMPLATE+"ip_src: ${message.fields.ip_src}", listAggegationFields, 0, 2,null, false));

Stream stream = mock(Stream.class);

Expand Down Expand Up @@ -790,7 +792,7 @@ public void testLimitOverflow() throws AlarmCallbackException, AlarmCallbackConf
@Test
public void callWithSplitFieldTestMessageUrl() throws AlarmCallbackException, AlarmCallbackConfigurationException {
List<String> listAggegationFields = Collections.singletonList(USER);
initializeConfiguration(getConfigMap("info", BODY_TEMPLATE, listAggegationFields, 0, 0));
initializeConfiguration(getConfigMap("info", BODY_TEMPLATE, listAggegationFields, 0, 0,null, false));

Stream stream = mock(Stream.class);

Expand Down Expand Up @@ -887,7 +889,7 @@ public void callWithSplitFieldTestMessageUrl() throws AlarmCallbackException, Al
@Test
public void testMsgURLWithPreviousMsgsURL() throws AlarmCallbackException, AlarmCallbackConfigurationException {
List<String> listAggegationFields = Collections.singletonList(USER);
initializeConfiguration(getConfigMap("info", BODY_TEMPLATE_MSG_URL, listAggegationFields, 15, 0));
initializeConfiguration(getConfigMap("info", BODY_TEMPLATE_MSG_URL, listAggegationFields, 15, 0,null, false));

Stream stream = mock(Stream.class);

Expand Down Expand Up @@ -983,7 +985,7 @@ public void testMsgURLWithPreviousMsgsURL() throws AlarmCallbackException, Alarm
@Test
public void callWithSplitFieldNotPresent() throws AlarmCallbackException, AlarmCallbackConfigurationException {
List<String> listAggegationFields = Collections.singletonList(USER);
initializeConfiguration(getConfigMap("info", BODY_TEMPLATE, listAggegationFields, 0, 0));
initializeConfiguration(getConfigMap("info", BODY_TEMPLATE, listAggegationFields, 0, 0,null, false));

Stream stream = mock(Stream.class);

Expand Down Expand Up @@ -1129,11 +1131,79 @@ public void callWithListMsgEmpty() throws AlarmCallbackException, AlarmCallbackC
+ "/search?rangetype=absolute&from="
+ "2018-04-19T13%3A59%3A00.000Z&to=2018-04-19T14%3A01%3A00.000Z&q=streams%3A001"));
}

@Test
public void callWithSpecificTagAndSingleNotification() throws AlarmCallbackConfigurationException, AlarmCallbackException {
String template = "type: alert\n" +
"id: ${logging_alert.id}\n" +
"severity: ${logging_alert.severity}\n" +
"app: graylog\n" +
"subject: ${alertCondition.title}\n" +
"body: ${check_result.resultDescription}\n" +
"ip_srcs: [${foreach messages message}${message.fields.ip_src},${end}]";

initializeConfiguration(getConfigMap("info", template, Collections.emptyList(), 0, 0,"SpecificTag", true));

Stream stream = mock(Stream.class);

final AlertCondition alertCondition = new DummyAlertCondition(
stream,
CONDITION_ID,
new DateTime(2017, 9, 6, 17, 0, DateTimeZone.UTC),
USER,
ImmutableMap.of(),
CONDITION_TITLE
);

Message message1 = new Message("Test message 1", "source1", new DateTime(2018, 4, 19, 14, 0, DateTimeZone.UTC));
message1.addField(USER, "admin");
message1.addField("ip_src", "127.0.0.1");
Message message2 = new Message("Test message 2", "source2", new DateTime(2018, 4, 19, 14, 1, DateTimeZone.UTC));
message2.addField("ip_src", "127.0.0.2");
Message message3 = new Message("Test message 3", "source3", new DateTime(2018, 4, 19, 14, 2, DateTimeZone.UTC));
message3.addField(USER, "user1");
message3.addField("ip_src", "127.0.0.3");

final List<MessageSummary> messageSummaries = ImmutableList.of(
new MessageSummary("graylog_1", message1),
new MessageSummary("graylog_2", message2),
new MessageSummary("graylog_3", message3)
);

final AlertCondition.CheckResult checkResult = new AbstractAlertCondition.CheckResult(
true,
alertCondition,
"Result Description",
new DateTime(2018, 4, 19, 14, 0, DateTimeZone.UTC),
messageSummaries
);

UUID uuid = UUID.randomUUID();
mockStatic(UUID.class);
when(UUID.randomUUID()).thenReturn(uuid);

Alert alert = mock(Alert.class);
when(alert.getId()).thenReturn("002");
when(alert.isInterval()).thenReturn(true);
when(alert.getTriggeredAt()).thenReturn(new DateTime(2018, 04, 19, 14, 01, 27, DateTimeZone.UTC));
when(alert.getResolvedAt()).thenReturn(new DateTime(2018, 04, 19, 14, 02, 27, DateTimeZone.UTC));
Optional<Alert> optAlert = Optional.of(alert);

when(stream.getId()).thenReturn("001");
when(alertService.getLastTriggeredAlert(anyString(), anyString())).thenReturn(optAlert);

alarmCallback.call(stream,checkResult);

final TestLogger testLogger = TestLoggerFactory.getTestLogger("SpecificTag");
assertThat(testLogger.getLoggingEvents()).extracting("level", "message").containsExactlyInAnyOrder(
tuple(INFO, "type: alert | id: 002 | severity: info | app: graylog | subject: Alert Condition Title " +
"| body: Result Description | ip_srcs: [127.0.0.1,127.0.0.2,127.0.0.3,]"));

}

@After
public void clearLoggers() {
TestLoggerFactory.clear();
}

}

0 comments on commit fc82b95

Please sign in to comment.