Browse Source

securedrop: first implementation

keep-around/799429133c1fe57b3cad6a0242c57b8e055f047d
Loïc Dachary 1 year ago
committed by Loic Dachary
parent
commit
799429133c
Signed by: dachary GPG Key ID: 992D23B392F9E4F2
  1. 1
      docs/services/index.rst
  2. 22
      docs/services/securedrop.rst
  3. 8
      inventory/services.yml
  4. 14
      playbooks/securedrop/conftest.py
  5. 4
      playbooks/securedrop/inventory/all.yml
  6. 4
      playbooks/securedrop/inventory/service.yml
  7. 10
      playbooks/securedrop/playbook.yml
  8. 25
      playbooks/securedrop/roles/securedrop/defaults/main.yml
  9. 33
      playbooks/securedrop/roles/securedrop/files/Dockerfile
  10. 29
      playbooks/securedrop/roles/securedrop/files/create-admin.py
  11. 2
      playbooks/securedrop/roles/securedrop/tasks/main.yml
  12. 97
      playbooks/securedrop/roles/securedrop/tasks/securedrop.yml
  13. 59
      playbooks/securedrop/roles/securedrop/templates/server.j2
  14. 8
      playbooks/securedrop/roles/securedrop/templates/torrc.j2
  15. 17
      playbooks/securedrop/securedrop-playbook.yml
  16. 9
      playbooks/securedrop/tests/test_icinga.py
  17. 2
      tox.ini

1
docs/services/index.rst

@ -12,6 +12,7 @@ Services
gitlab
website
wekan
securedrop
bind
VPN
postfix

22
docs/services/securedrop.rst

@ -0,0 +1,22 @@
SecureDrop
==========
`SecureDrop <https://securedrop.org/>`__ is only available via Tor.
The administrative user, its password and TOTP can be set
as documented in `this file
<https://lab.enough.community/main/infrastructure/blob/master/playbooks/securedrop/roles/securedrop/defaults/main.yml>`__
and can be modified in the
`~/.enough/example.com/inventory/group_vars/securedrop-service-group.yml`
file.
URLs
----
The URL of the source and journalist interfaces can be retrieved with:
.. code::
$ enough --domain example.com ssh securedrop-host cat /var/lib/tor/services/source/hostname /var/lib/tor/services/journalist/hostname
The URL of the journalist interface requires an authentication token
and must be copied in the torrc file.

8
inventory/services.yml

@ -151,3 +151,11 @@ backup-service-hosts:
children:
backup-service-group:
essential-service-hosts:
securedrop-service-group:
hosts: {}
securedrop-service-hosts:
children:
securedrop-service-group:
essential-service-hosts:

14
playbooks/securedrop/conftest.py

@ -0,0 +1,14 @@
def pytest_addoption(parser):
parser.addoption(
"--enough-hosts",
action="store",
default="bind-host,postfix-host,icinga-host,securedrop-host",
help="list of hosts"
)
parser.addoption(
"--enough-service",
action="store",
default="securedrop",
help="service"
)

4
playbooks/securedrop/inventory/all.yml

@ -0,0 +1,4 @@
---
all-hosts:
hosts:
securedrop-host:

4
playbooks/securedrop/inventory/service.yml

@ -0,0 +1,4 @@
securedrop-service-group:
hosts:
securedrop-host:

10
playbooks/securedrop/playbook.yml

@ -0,0 +1,10 @@
---
- import_playbook: ../infrastructure/buster-playbook.yml
- import_playbook: ../infrastructure/network-playbook.yml
- import_playbook: ../firewall/firewall-playbook.yml
- import_playbook: ../icinga/test-icinga-playbook.yml
- import_playbook: ../bind/bind-playbook.yml
- import_playbook: ../bind/bind-client-playbook.yml
- import_playbook: ../icinga/icinga-playbook.yml
- import_playbook: ../postfix/postfix-playbook.yml
- import_playbook: securedrop-playbook.yml

25
playbooks/securedrop/roles/securedrop/defaults/main.yml

@ -0,0 +1,25 @@
---
#
#############################################
#
# SecureDrop version to install, see https://lab.enough.community/main/securedrop/branches/all
#
securedrop_version: release/1.3.0
#
#############################################
#
# user name of the SecureDrop administrator
#
securedrop_admin_user: admin
#
#############################################
#
# password of the SecureDrop administrator
#
securedrop_admin_password: endurable darkened fiddle coral suave stylus mustang
#
#############################################
#
# OTP secret for the SecureDrop administrator
#
securedrop_admin_otp_secret: JHCOGO7VCER3EJ4L

33
playbooks/securedrop/roles/securedrop/files/Dockerfile

@ -0,0 +1,33 @@
# copied from securedrop/dockerfiles/xenial/python3/Dockerfile
# ubuntu 16.04 image from 2019-03-12
FROM ubuntu@sha256:58d0da8bc2f434983c6ca4713b08be00ff5586eb5cdff47bcde4b2e88fd40f88
ARG USER_NAME
ENV USER_NAME ${USER_NAME:-root}
ARG USER_ID
ENV USER_ID ${USER_ID:-0}
# If running grsecurity kernel on the host, Memprotect must be disabled on mono-sgen in the container
RUN apt-get update && apt-get install -y paxctl && \
{ apt-get install -y libgtk2.0 || echo 'libgtk2.0 was not installed'; } && \
paxctl -cm /usr/bin/mono-sgen && dpkg-reconfigure mono-runtime-sgen && \
apt-get install -y apache2-dev coreutils devscripts dh-virtualenv vim \
python3-pip python3-all python3-venv virtualenv libpython3.5-dev libssl-dev \
gnupg2 ruby redis-server git xvfb haveged curl wget \
gettext paxctl x11vnc enchant libffi-dev sqlite3 gettext sudo \
libasound2 libdbus-glib-1-2 libgtk2.0-0 libfontconfig1 libxrender1 \
libcairo-gobject2 libgtk-3-0 libstartup-notification0 tor
RUN gem install sass -v 3.4.23
COPY requirements requirements
RUN python3 -m venv /opt/venvs/securedrop-app-code && \
/opt/venvs/securedrop-app-code/bin/pip3 install --no-deps --require-hashes -r requirements/python3/docker-requirements.txt && \
/opt/venvs/securedrop-app-code/bin/pip3 install --no-deps --require-hashes -r requirements/python3/securedrop-app-code-requirements.txt && \
/opt/venvs/securedrop-app-code/bin/pip3 install --no-deps --require-hashes -r requirements/python3/test-requirements.txt
RUN if test $USER_NAME != root ; then useradd --no-create-home --home-dir /tmp --uid $USER_ID $USER_NAME && echo "$USER_NAME ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers ; fi && \
chown -R $USER_NAME.$USER_NAME /opt/venvs/securedrop-app-code/
STOPSIGNAL SIGKILL
EXPOSE 8080 8081 5909

29
playbooks/securedrop/roles/securedrop/files/create-admin.py

@ -0,0 +1,29 @@
#!/opt/venvs/securedrop-app-code/bin/python
# -*- coding: utf-8 -*-
import os
import sys
os.environ["SECUREDROP_ENV"] = "dev" # noqa
import journalist_app
from sdconfig import config
from db import db
from models import Journalist
def main(username, password, otp_secret):
app = journalist_app.create_app(config)
with app.app_context():
user = Journalist(username=username,
password=password,
is_admin=True,
first_name="",
last_name="")
user.otp_secret = otp_secret
db.session.add(user)
db.session.commit()
main(*sys.argv[1:])

2
playbooks/securedrop/roles/securedrop/tasks/main.yml

@ -0,0 +1,2 @@
---
- import_tasks: securedrop.yml

97
playbooks/securedrop/roles/securedrop/tasks/securedrop.yml

@ -0,0 +1,97 @@
---
- name: apt-get install git
apt:
name: [ git ]
state: present
- name: chown debian /srv
file:
path: /srv
owner: debian
- name: mkdir -p /srv/securedrop
file:
path: /srv/securedrop
state: directory
owner: debian
group: debian
mode: 0755
- name: git clone https://github.com/freedomofpress/securedrop
git:
repo: "https://github.com/freedomofpress/securedrop"
version: "{{ securedrop_version }}"
force: yes
dest: /srv/securedrop/securedrop
become: false
- name: Copy /srv/securedrop/securedrop/securedrop/bin/server
template:
src: server.j2
dest: /srv/securedrop/securedrop/securedrop/bin/server
mode: "0755"
become: false
- name: Copy /srv/securedrop/securedrop/securedrop/create-admin.py
copy:
src: create-admin.py
dest: /srv/securedrop/securedrop/securedrop/create-admin.py
mode: "0755"
become: false
- name: Copy /srv/securedrop/securedrop/securedrop/dockerfiles/xenial/python3/Dockerfile
copy:
src: Dockerfile
dest: /srv/securedrop/securedrop/securedrop/dockerfiles/xenial/python3/Dockerfile
mode: "0444"
become: false
- name: run SecureDrop server
shell: |
set -xe
export DOCKER_BUILD_VERBOSE=true
export DOCKER_RUN_ARGUMENTS="-v /srv/securedrop/data:/var/lib/securedrop --detach --restart=always"
bin=/srv/securedrop/securedrop/securedrop/bin
sed -i -e 's/--rm//' $bin/dev-shell
bash -x $bin/dev-shell $bin/server > /srv/securedrop/securedrop.log 2>&1
args:
executable: /bin/bash
chdir: /srv/securedrop/securedrop
become: false
- name: apt-get install tor
apt:
name: [ tor ]
state: present
- name: mkdir /var/lib/tor/services
file:
state: directory
dest: /var/lib/tor/services
owner: debian-tor
group: debian-tor
mode: "0700"
- name: mkdir /var/lib/tor/services/{journalist,source}
file:
state: directory
dest: "/var/lib/tor/services/{{ item }}"
owner: debian-tor
group: debian-tor
mode: "0700"
loop:
- journalist
- source
- name: Copy /etc/tor/torrc
template:
src: torrc.j2
dest: /etc/tor/torrc
mode: "0644"
- name: restart tor
service:
name: tor
enabled: True
state: restarted

59
playbooks/securedrop/roles/securedrop/templates/server.j2

@ -0,0 +1,59 @@
#!/bin/bash
# shellcheck disable=SC1090
set -eu
REPOROOT=$(git rev-parse --show-toplevel)
cd "${REPOROOT}/securedrop"
source "${BASH_SOURCE%/*}/dev-deps"
# copied from securedrop/bin/dev-deps:reset_demo
function prepare_server() {
# Set up gpg keys directory structure.
sudo mkdir -p /var/lib/securedrop/{store,keys,tmp}
sudo chown -R "$(id -u)" /var/lib/securedrop
cp ./tests/files/test_journalist_key.pub /var/lib/securedrop/keys
gpg2 --homedir /var/lib/securedrop/keys --import /var/lib/securedrop/keys/test_journalist_key.pub >& /tmp/gpg.out || cat /tmp/gpg.out
# Create gpg-agent.conf
echo allow-loopback-pinentry > /var/lib/securedrop/keys/gpg-agent.conf
# Kill gpg-agent(s) if they exist so it picks up the new config on restart.
pkill -f gpg-agent || true
# Note that we should avoid `gpgconf --kill gpg-agent` since the pkill command will
# handle killing multiple gpg-agent processes if they exist (this is what we want).
# Set permissions on gpg-related directories/files.
sudo chown -R "$(id -gn)" /var/lib/securedrop/keys
chmod 700 /var/lib/securedrop/keys
chmod 600 /var/lib/securedrop/keys/*
# If the following directories exist, make sure they have the proper permissions.
chmod -f 700 /var/lib/securedrop/keys/private-keys-v1.d || true
chmod -f 700 /var/lib/securedrop/keys/openpgp-revocs.d || true
# Generate translated strings
./i18n_tool.py translate-messages --compile
# create an empty database
if ! test -e /var/lib/securedrop/db.sqlite ; then
sqlite3 /var/lib/securedrop/db.sqlite .databases &> /dev/null
./manage.py reset
./create-admin.py "{{ securedrop_admin_user }}" "{{ securedrop_admin_password }}" "{{ securedrop_admin_otp_secret }}"
fi
}
run_redis &
# copied from securedrop/bin/dev-deps:run_sass
sass --stop-on-error --update sass:static/css --style compressed
maybe_create_config_py
prepare_server
# run the batch processing services normally managed by systemd
/opt/venvs/securedrop-app-code/bin/rqworker &
PYTHONPATH="${REPOROOT}/securedrop" /opt/venvs/securedrop-app-code/bin/python "${REPOROOT}/securedrop/scripts/rqrequeue" --interval 60 &
/opt/venvs/securedrop-app-code/bin/python "${REPOROOT}/securedrop/scripts/shredder" --interval 60 &
/opt/venvs/securedrop-app-code/bin/python "${REPOROOT}/securedrop/scripts/source_deleter" --interval 10 &
./manage.py run

8
playbooks/securedrop/roles/securedrop/templates/torrc.j2

@ -0,0 +1,8 @@
HiddenServiceDir /var/lib/tor/services/source
HiddenServiceVersion 2
HiddenServicePort 80 127.0.0.1:8180
HiddenServiceDir /var/lib/tor/services/journalist
HiddenServiceVersion 2
HiddenServicePort 80 127.0.0.1:8181
HiddenServiceAuthorizeClient stealth journalist

17
playbooks/securedrop/securedrop-playbook.yml

@ -0,0 +1,17 @@
---
- name: prepare SecureDrop environment
hosts: securedrop-service-group
become: true
roles:
- role: ansible-role-docker
- role: docker
- role: securedrop
- role: monitor_tor_http_vhost
vars:
tor_hostname_file: /var/lib/tor/services/source/hostname
tor_http_vhost_name: "{{ inventory_hostname }}"
tor_http_vhost_uri: "/"
tor_http_vhost_string: "SecureDrop"

9
playbooks/securedrop/tests/test_icinga.py

@ -0,0 +1,9 @@
from tests.icinga_helper import IcingaHelper
testinfra_hosts = ['ansible://icinga-host']
class TestChecks(IcingaHelper):
def test_service(self, host):
assert self.is_service_ok('securedrop-host!securedrop-host over Tor')

2
tox.ini

@ -19,7 +19,7 @@ commands = coverage run --source=enough {envbindir}/py.test -vvv --durations 10
[testenv:flake8]
commands = flake8 {posargs}
[testenv:{infrastructure,bind,authorized_keys,backup,certificate,postfix,icinga,openvpn,wekan,misc,pad,firewall,gitlab,api,wazuh,weblate,website,chat,cloud,enough,forum,packages}]
[testenv:{infrastructure,bind,authorized_keys,backup,certificate,postfix,icinga,openvpn,wekan,misc,pad,firewall,gitlab,api,wazuh,weblate,website,chat,cloud,enough,forum,packages,securedrop}]
passenv =
ENOUGH_API_TOKEN
PYTEST_ADDOPTS

Loading…
Cancel
Save