From 856b180e3014f0e739ca8ea4f469533c1bde4751 Mon Sep 17 00:00:00 2001 From: Loic Dachary Date: Fri, 4 May 2018 23:59:14 +0200 Subject: [PATCH] ossec: never send more than one journalist notification per 24h Relates to: https://github.com/freedomofpress/securedrop/issues/3368 --- .../ossec/files/process_submissions_today.sh | 46 +++++++++++++++++-- testinfra/ossec/test_journalist_mail.py | 29 +++++++++++- 2 files changed, 69 insertions(+), 6 deletions(-) diff --git a/install_files/ansible-base/roles/ossec/files/process_submissions_today.sh b/install_files/ansible-base/roles/ossec/files/process_submissions_today.sh index 899d83df862..342a9e261c3 100755 --- a/install_files/ansible-base/roles/ossec/files/process_submissions_today.sh +++ b/install_files/ansible-base/roles/ossec/files/process_submissions_today.sh @@ -1,10 +1,28 @@ #!/bin/bash +SENT_STAMP=/var/ossec/tmp/journalist_notification_sent.stamp + function send_encrypted_alarm() { /var/ossec/send_encrypted_alarm.sh "$1" } function main() { + if modified_in_the_past_24h "${SENT_STAMP}" ; then + logger "$0 journalist notification suppressed" + else + handle_notification "$@" + fi +} + +function modified_in_the_past_24h() { + local stamp + stamp="$1" + test -f "${stamp}" && \ + find "${stamp}" -mtime -1 | \ + grep --quiet "${stamp}" +} + +function handle_notification() { local sender local stdin sender=${1:-send_encrypted_alarm} @@ -24,6 +42,7 @@ function main() { echo "There has been no submission activity in the past 24 hours." echo "You do not need to login to SecureDrop." fi | $sender journalist + touch "${SENT_STAMP}" else export SUBJECT="SecureDrop Submissions Error" ( @@ -34,33 +53,50 @@ function main() { fi } +function forget() { + rm -f "${1:-$SENT_STAMP}" +} + +function test_modified_in_the_past_24h() { + local stamp + stamp=$(mktemp) + + modified_in_the_past_24h "${stamp}" || exit 1 + + touch --date '-2 days' "${stamp}" + ! modified_in_the_past_24h "${stamp}" || exit 1 + + forget "${stamp}" + ! modified_in_the_past_24h "${stamp}" || exit 1 +} + function test_send_encrypted_alarm() { echo "$1" cat } -function test_main() { +function test_handle_notification() { shopt -s -o xtrace PS4='${BASH_SOURCE[0]}:$LINENO: ${FUNCNAME[0]}: ' - echo BUGOUS | main test_send_encrypted_alarm | \ + echo BUGOUS | handle_notification test_send_encrypted_alarm | \ tee /dev/stderr | \ grep -q 'failed to find 0/1 submissions boolean' || exit 1 ( echo 'ossec: output' echo 'NOTANUMBER' - ) | main test_send_encrypted_alarm | tee /dev/stderr | grep -q 'failed to find 0/1 submissions boolean' || exit 1 + ) | handle_notification test_send_encrypted_alarm | tee /dev/stderr | grep -q 'failed to find 0/1 submissions boolean' || exit 1 ( echo 'ossec: output' echo '1' - ) | main test_send_encrypted_alarm | tee /tmp/submission-yes.txt | grep -q 'There has been submission activity' || exit 1 + ) | handle_notification test_send_encrypted_alarm | tee /tmp/submission-yes.txt | grep -q 'There has been submission activity' || exit 1 ( echo 'ossec: output' echo '0' - ) | main test_send_encrypted_alarm | tee /tmp/submission-no.txt | grep -q 'There has been no submission activity' || exit 1 + ) | handle_notification test_send_encrypted_alarm | tee /tmp/submission-no.txt | grep -q 'There has been no submission activity' || exit 1 if test "$(stat --format=%s /tmp/submission-no.txt)" != "$(stat --format=%s /tmp/submission-yes.txt)" ; then echo both files are expected to have exactly the same size, padding must be missing diff --git a/testinfra/ossec/test_journalist_mail.py b/testinfra/ossec/test_journalist_mail.py index 69dd319906e..b98f1693711 100644 --- a/testinfra/ossec/test_journalist_mail.py +++ b/testinfra/ossec/test_journalist_mail.py @@ -70,6 +70,8 @@ def test_procmail(self, host): for (destination, payload) in ( ('journalist', today_payload), ('ossec', 'MYGREATPAYLOAD')): + assert self.run(host, + "/var/ossec/process_submissions_today.sh forget") assert self.run(host, "postsuper -d ALL") assert self.run( host, @@ -82,7 +84,12 @@ def test_procmail(self, host): self.service_stopped(host, "postfix") def test_process_submissions_today(self, host): - self.run(host, "/var/ossec/process_submissions_today.sh test_main") + assert self.run(host, + "/var/ossec/process_submissions_today.sh " + "test_handle_notification") + assert self.run(host, + "/var/ossec/process_submissions_today.sh " + "test_modified_in_the_past_24h") def test_send_encrypted_alert(self, host): self.service_started(host, "postfix") @@ -185,10 +192,15 @@ def test_journalist_mail_notification(self, host): # empty the mailq on mon in case there were leftovers # assert self.run(mon, "postsuper -d ALL") + # + # forget about past notifications in case there were leftovers + # + assert self.run(mon, "/var/ossec/process_submissions_today.sh forget") # # the command fires every time ossec starts, # regardless of the frequency + # https://github.com/ossec/ossec-hids/issues/1415 # with app.sudo(): self.service_restarted(app, "ossec") @@ -203,6 +215,21 @@ def test_journalist_mail_notification(self, host): mon, "test 1 = $(mailq | grep journalist@ossec.test | wc -l)") + assert self.run( + mon, + "grep --count 'notification suppressed' /var/log/syslog " + "> /tmp/before") + + # + # The second notification within less than 24h is suppressed + # + with app.sudo(): + self.service_restarted(app, "ossec") + assert self.wait_for_command(mon, """ + grep --count 'notification suppressed' /var/log/syslog > /tmp/after + test $(cat /tmp/before) -lt $(cat /tmp/after) + """) + # # teardown the ossec and postfix on mon and app #