Skip to content

Commit

Permalink
tmp
Browse files Browse the repository at this point in the history
  • Loading branch information
Loic Dachary committed Jan 8, 2018
1 parent 133c996 commit 7b9ca5a
Show file tree
Hide file tree
Showing 11 changed files with 121 additions and 35 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#!/bin/bash

cd /var/www/securedrop
./manage.py how-many-submissions-today
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,17 @@
with_items: "{{ apparmor_profiles }}"
tags: apparmor

- name: Create cron.daily directory.
file:
state: directory
dest: "{{ securedrop_app_code_deb_dir }}/etc/cron.daily"

- name: Copy crontab.
copy:
src: cron.daily
dest: "{{ securedrop_app_code_deb_dir }}/etc/cron.daily/securedrop"
mode: 0755

- name: Build securedrop-app-code Debian package.
command: dpkg-deb --build {{ securedrop_app_code_deb_dir }}

Expand Down
2 changes: 2 additions & 0 deletions install_files/ansible-base/roles/ossec-server/files/aliases
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
root: ossec
journalist: ossec

Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,8 @@ LOGFILE=/var/log/procmail.log
SUBJECT=`formail -xSubject:`
:0 c
*^To:.*root.*
|/var/ossec/send_encrypted_alarm.sh
|/var/ossec/send_encrypted_alarm.sh ossec

:0 c
*^To:.*journalist.*
|/var/ossec/send_encrypted_alarm.sh journalist
Original file line number Diff line number Diff line change
Expand Up @@ -18,32 +18,44 @@ ossec_alert_text="$(< /dev/stdin)"

# Primary "send email to Admin" functionality.
function send_encrypted_alert() {

local recepient="$1"
local gpg_fpr
local alert_email
if "$recipient" = "journalist" ; then
gpg_fpr='{{ journalist_gpg_fpr }}'
alert_email='{{ journalist_alert_email }}'
else
gpg_fpr='{{ ossec_gpg_fpr }}'
alert_email='{{ ossec_alert_email }}'
fi

local encrypted_alert_text
# Try to encrypt the alert message. We'll inspect the exit status of the
# pipeline to decide whether to send the alert text, or the default
# failure message.
encrypted_alert_text="$(printf "%s" "${ossec_alert_text}" | \
/usr/bin/formail -I '' | \
/usr/bin/gpg --homedir /var/ossec/.gnupg --trust-model always -ear '{{ ossec_gpg_fpr }}')"
/usr/bin/gpg --homedir /var/ossec/.gnupg --trust-model always -ear '$gpg_fpr')"

# Error handling.
if [[ -z "${encrypted_alert_text}" || $? -ne 0 ]]; then
send_plaintext_fail_message
send_plaintext_fail_message "$alert_email"
else
echo "${encrypted_alert_text}" | \
/usr/bin/mail -s "$(echo "${SUBJECT}" | sed -r 's/([0-9]{1,3}\.){3}[0-9]{1,3}\s?//g' )" '{{ ossec_alert_email }}'
/usr/bin/mail -s "$(echo "${SUBJECT}" | sed -r 's/([0-9]{1,3}\.){3}[0-9]{1,3}\s?//g' )" '$alert_email'
fi
}

# Failover alerting function, in case the primary function failed.
# Usually a failure is related to GPG balking on the encryption step;
# that may be due to a missing pubkey or something reason.
function send_plaintext_fail_message() {
local alert_email="$1"
printf "Failed to encrypt OSSEC alert. Investigate the mailing configuration on the Monitor Server." | \
/usr/bin/formail -I "" | \
/usr/bin/mail -s "$(echo "${SUBJECT}" | sed -r 's/([0-9]{1,3}\.){3}[0-9]{1,3}\s?//g' )" '{{ ossec_alert_email }}'
/usr/bin/mail -s "$(echo "${SUBJECT}" | sed -r 's/([0-9]{1,3}\.){3}[0-9]{1,3}\s?//g' )" "$alert_email"
}

# Encrypt the OSSEC notification and pass to mailer for sending.
send_encrypted_alert
send_encrypted_alert "$@"

6 changes: 0 additions & 6 deletions install_files/ansible-base/securedrop-prod.yml
Original file line number Diff line number Diff line change
@@ -1,11 +1,5 @@
#!/usr/bin/env ansible-playbook
---
- name: Ensure validation is run before prod install
hosts: localhost
connection: local
roles:
- { role: validate, tags: validate }

- name: Add FPF apt repository and install base packages.
hosts: securedrop
roles:
Expand Down
8 changes: 8 additions & 0 deletions install_files/securedrop-ossec-agent/var/ossec/etc/ossec.conf
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@

<ignore>/var/lib/securedrop/db.sqlite</ignore>

<ignore>/var/lib/securedrop/submissions_today.txt</ignore>

<ignore>/var/securedrop/store</ignore>

<ignore>/var/ossec/queue</ignore>
Expand Down Expand Up @@ -101,6 +103,12 @@
<command>last -n 5</command>
</localfile>

<localfile>
<log_format>full_command</log_format>
<command>head -1 /var/lib/securedrop/submissions_today.txt | grep '^[0-9]*$'</command>
<frequencey>86400</frequency>
</localfile>

<localfile>
<log_format>syslog</log_format>
<location>/var/log/kern.log</location>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,12 @@
<do_not_delay />
</email_alerts>

<email_alerts>
<email_to>journalist@localhost</email_to>
<group>multiple_drops</group>
<do_not_delay />
</email_alerts>

<email_alerts>
<email_to>root@localhost</email_to>
<group>low_diskspace</group>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -190,3 +190,11 @@
<options>no_email_alert</options>
</rule>
</group>

<group name="multiple_drops">
<rule id="400600" level="7" >
<if_sid>530</if_sid>
<match>ossec: output: 'head -1 /var/lib/securedrop/submissions_today.txt</match>
<description>Number of submissions received in the past 24h.</description>
</rule>
</group>
27 changes: 24 additions & 3 deletions securedrop/manage.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,15 +91,15 @@ def reset(args):

# Clear submission/reply storage
try:
os.stat(config.STORE_DIR)
os.stat(args.store_dir)
except OSError:
pass
else:
for source_dir in os.listdir(config.STORE_DIR):
for source_dir in os.listdir(args.store_dir):
try:
# Each entry in STORE_DIR is a directory corresponding
# to a source
shutil.rmtree(os.path.join(config.STORE_DIR, source_dir))
shutil.rmtree(os.path.join(args.store_dir, source_dir))
except OSError:
pass
return 0
Expand Down Expand Up @@ -378,11 +378,24 @@ def translate_desktop(args):
""".format(translations_dir=args.translations_dir,
sources=" ".join(args.source)))

def how_many_submissions_today(args):
count_file = os.path.join(args.data_root, 'submissions_today.txt')
sh("""
find {dir} -type f -a -mmin -1440 | wc -l > {count_file}
""".format(dir=args.store_dir,
count_file=count_file))

def get_args():
parser = argparse.ArgumentParser(prog=__file__, description='Management '
'and testing utility for SecureDrop.')
parser.add_argument('-v', '--verbose', action='store_true')
parser.add_argument('--data-root',
default=config.SECUREDROP_DATA_ROOT,
help=('directory in which the securedrop '
'data is stored'))
parser.add_argument('--store-dir',
default=config.STORE_DIR,
help=('directory in which the documents are stored'))
subps = parser.add_subparsers()
# Run WSGI app
run_subp = subps.add_parser('run', help='Run the Werkzeug source & '
Expand Down Expand Up @@ -417,10 +430,18 @@ def get_args():

set_translate_messages_parser(subps)
set_translate_desktop_parser(subps)
set_how_many_submissions_today(subps)

return parser


def set_how_many_submissions_today(subps):
parser = subps.add_parser(
'how-many-submissions-today',
help=('Update the file containing '
'the number of submissions in the past 24h'))
parser.set_defaults(func=how_many_submissions_today)

def set_translate_parser(subps,
parser,
translations_dir,
Expand Down
54 changes: 35 additions & 19 deletions securedrop/tests/test_manage.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
import subprocess
import sys
import time
import unittest
import version
import utils

Expand Down Expand Up @@ -44,11 +43,11 @@ def test_verbose(self, caplog):
assert 'VISIBLE' in caplog.text


class TestManagementCommand(unittest.TestCase):
def setUp(self):
class TestManagementCommand(object):
def setup(self):
utils.env.setup()

def tearDown(self):
def teardown(self):
utils.env.teardown()

@mock.patch("__builtin__.raw_input", return_value='jen')
Expand Down Expand Up @@ -78,9 +77,9 @@ def test_handle_invalid_secret(self, mock_username, mock_yubikey,

# We will try to provide one invalid and one valid secret
return_value = manage._add_user()
self.assertEqual(return_value, 0)
self.assertIn('Try again.', sys.stdout.getvalue())
self.assertIn('successfully added', sys.stdout.getvalue())
assert return_value == 0
assert 'Try again.' in sys.stdout.getvalue()
assert 'successfully added' in sys.stdout.getvalue()

@mock.patch("manage._get_username", return_value='foo-bar-baz')
@mock.patch("manage._get_yubikey_usage", return_value=False)
Expand All @@ -93,13 +92,13 @@ def test_exception_handling_when_duplicate_username(self,

# Inserting the user for the first time should succeed
return_value = manage._add_user()
self.assertEqual(return_value, 0)
self.assertIn('successfully added', sys.stdout.getvalue())
assert return_value == 0
assert 'successfully added' in sys.stdout.getvalue()

# Inserting the user for a second time should fail
return_value = manage._add_user()
self.assertEqual(return_value, 1)
self.assertIn('ERROR: That username is already taken!',
assert return_value == 1
assert ('ERROR: That username is already taken!' in
sys.stdout.getvalue())

@mock.patch("manage._get_username", return_value='test-user-56789')
Expand All @@ -113,10 +112,10 @@ def test_delete_user(self,
mock_user_to_delete,
mock_user_del_confirm):
return_value = manage._add_user()
self.assertEqual(return_value, 0)
assert return_value == 0

return_value = manage.delete_user(args=None)
self.assertEqual(return_value, 0)
assert return_value == 0

@mock.patch("manage._get_username_to_delete",
return_value='does-not-exist')
Expand All @@ -127,28 +126,30 @@ def test_delete_non_existent_user(self,
mock_user_del_confirm,
mock_stdout):
return_value = manage.delete_user(args=None)
self.assertEqual(return_value, 0)
self.assertIn('ERROR: That user was not found!',
assert return_value == 0
assert ('ERROR: That user was not found!' in
sys.stdout.getvalue())

@mock.patch("__builtin__.raw_input", return_value='test-user-12345')
def test_get_username_to_delete(self, mock_username):
return_value = manage._get_username_to_delete()
self.assertEqual(return_value, 'test-user-12345')
assert return_value == 'test-user-12345'

def test_reset(self):
test_journalist, _ = utils.db_helper.init_journalist()
user_should_be_gone = test_journalist.username

return_value = manage.reset(args=None)
args = argparse.Namespace(store_dir=config.STORE_DIR,
verbose=logging.DEBUG)
return_value = manage.reset(args)

self.assertEqual(return_value, 0)
assert return_value == 0
assert os.path.exists(config.DATABASE_FILE)
assert os.path.exists(config.STORE_DIR)

# Verify journalist user present in the database is gone
db_session.remove() # Close session and get a session on the new db
with self.assertRaises(NoResultFound):
with pytest.raises(NoResultFound):
Journalist.query.filter_by(username=user_should_be_gone).one()


Expand Down Expand Up @@ -364,6 +365,21 @@ def test_clean_tmp_removed(self, caplog):
manage.clean_tmp(args)
assert 'FILE removed' in caplog.text

def test_how_many_submissions_today(self, tmpdir):
data_root = tmpdir
store_dir = tmpdir.join('store')
args = argparse.Namespace(data_root=str(data_root),
store_dir=str(store_dir),
verbose=logging.DEBUG)

store_dir.join('recent').ensure()
older = store_dir.join('older')
older.ensure()
older.setmtime(time.time() - 2*24*60*60)
manage.how_many_submissions_today(args)
count_file = data_root.join('submissions_today.txt')
assert count_file.read() == "1\n"


class TestSh(object):

Expand Down

0 comments on commit 7b9ca5a

Please sign in to comment.