Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adds support for v3 Onion urls to ./securedrop-admin tailsconfig` #4675

Merged
merged 7 commits into from
Aug 27, 2019
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions admin/tests/test_securedrop-admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -665,7 +665,7 @@ def test_only_v3_onion_services(self, tmpdir):
ansible_path='.',
app_path=dirname(__file__))
site_config = securedrop_admin.SiteConfig(args)
with open("app-source-ths", "w") as fobj:
with open("app-sourcev3-ths", "w") as fobj:
fobj.write("a" * 56 + ".onion\n")
site_config.update_onion_version_config()
site_config.save()
Expand All @@ -676,7 +676,7 @@ def test_only_v3_onion_services(self, tmpdir):
v2_onion_services: false
v3_onion_services: true
""")
os.remove("app-source-ths")
os.remove("app-sourcev3-ths")
assert expected == data

def test_validate_gpg_key(self, caplog):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
import sys
import subprocess

from shutil import copyfile


# check for root
if os.geteuid() != 0:
Expand All @@ -26,6 +28,16 @@
path_gui_updater = os.path.join(path_securedrop_root,
'journalist_gui/SecureDropUpdater')

paths_v3_authfiles = {
"app-journalist": os.path.join(path_securedrop_root,
'install_files/ansible-base/app-journalist.auth_private'),
"app-ssh": os.path.join(path_securedrop_root,
'install_files/ansible-base/app-ssh.auth_private'),
"mon-ssh": os.path.join(path_securedrop_root,
'install_files/ansible-base/mon-ssh.auth_private')
}
path_onion_auth_dir = '/var/lib/tor/onion_auth'

# load torrc_additions
if os.path.isfile(path_torrc_additions):
with io.open(path_torrc_additions) as f:
Expand All @@ -52,11 +64,35 @@
with io.open(path_torrc, 'w') as f:
f.write(torrc + torrc_additions)

# reload tor
# check for v3 aths files
v3_authfiles_present = False
for f in paths_v3_authfiles.values():
if os.path.isfile(f):
v3_authfiles_present = True

# if there are v3 authfiles, make dir and copy them into place
debian_tor_uid = pwd.getpwnam("debian-tor").pw_uid
debian_tor_gid = grp.getgrnam("debian-tor").gr_gid

if not os.path.isdir(path_onion_auth_dir):
os.mkdir(path_onion_auth_dir)

os.chmod(path_onion_auth_dir, 0o700)
os.chown(path_onion_auth_dir, debian_tor_uid, debian_tor_gid)

for key, f in paths_v3_authfiles.items():
if os.path.isfile(f):
filename = os.path.basename(f)
new_f = os.path.join(path_onion_auth_dir, filename)
copyfile(f, new_f)
os.chmod(new_f, 0o400)
os.chown(new_f, debian_tor_uid, debian_tor_gid)

# restart tor
try:
subprocess.check_call(['systemctl', 'reload', '[email protected]'])
subprocess.check_call(['systemctl', 'restart', '[email protected]'])
except subprocess.CalledProcessError:
sys.exit('Error reloading Tor')
sys.exit('Error restarting Tor')

# Turn off "automatic-decompression" in Nautilus to ensure the original
# submission filename is restored (see
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,29 @@
- '*-aths'
register: find_aths_info_result

# We need at least one ATHS value, for the Journalist Interface.
# Admin Workstations will have three, including the two SSH interfaces.
- name: Find V3 Authenticated Onion Service info for SecureDrop interfaces.
find:
paths:
- "{{ tails_config_ansible_base }}"
patterns:
# Collect all files that end in `.auth_private` - if there are any present
# then `torrc` will need a directive added
- '*.auth_private'
register: find_v3_aths_info_result

# We need at least one v2 or v3 ATHS value, for the Journalist Interface.
# If v2 is enabled, there will be 3 v2 `-aths` files on the Admin Interface.
# If v3 is enabled, there will be 3 v3 `.auth_private` files on the Admin Interface.
# If both are enabled, the Admin Interface will have 6 files in total.
# This task simply validates that at least one suitable file was found;
# if not, then the playbooks haven't been run, so fail with instructions.
- name: Confirm ATHS info was found.
assert:
that:
- find_aths_info_result.matched >= 1
- find_aths_info_result.matched + find_v3_aths_info_result.matched >= 1
msg: >-
Failed to find ATHS info locally. Make sure you've installed SecureDrop
on the servers, and that the `-aths` files are located in:
on the servers, and that the `-aths` and/or `.auth_private` files are located in:
`{{ tails_config_ansible_base }}/`.

- name: Assemble ATHS info into torrc additions.
Expand All @@ -31,3 +43,13 @@
owner: root
group: root
mode: "0400"

- name: Append ClientOnionAuthDir directive to torrc additions
become: yes
lineinfile:
dest: "{{ tails_config_torrc_additions }}"
line: "ClientOnionAuthDir /var/lib/tor/onion_auth"
owner: root
group: root
mode: "0400"
when: find_v3_aths_info_result.matched >= 1
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,43 @@
# installation. On the Admin Workstation, these files will be present
# after running the playbooks, but on the Journalist Workstation, they must
# be copied manually by the Admin.
- name: Look up Source Interface URL.
# Desktop shortcuts default to v3 URLs when available - as `site-specific`
# is not copied to Journalist Workstations, the {v2,v3}_onion_service
# booleans can't be used to choose a preference.

- name: Check for v3 Source Interface file
stat:
path: app-sourcev3-ths
register: v3_source_file

- name: Check for v3 Journalist Interface file
stat:
path: app-journalist.auth_private
register: v3_journalist_file

- name: Look up v2 Source Interface URL.
command: grep -Po '.{16}\.onion' app-source-ths
changed_when: false
register: source_interface_lookup_result
when: v3_source_file.stat.exists == false

- name: Look up Journalist Interface URL.
- name: Look up v3 Source Interface URL.
command: grep -Po '.{56}\.onion' app-sourcev3-ths
changed_when: false
register: sourcev3_interface_lookup_result
when: v3_source_file.stat.exists == true

- name: Look up v2 Journalist Interface URL.
command: grep -Po '.{16}\.onion' app-journalist-aths
changed_when: false
register: journalist_interface_lookup_result
when: v3_source_file.stat.exists == false

- name: Look up v3 Journalist Interface URL.
command: awk -F ':' '{print $1 ".onion"}' app-journalist.auth_private
changed_when: false
register: journalistv3_interface_lookup_result
when: v3_source_file.stat.exists == true

- name: Create desktop shortcut parent directories.
file:
Expand All @@ -30,16 +58,27 @@
when: item.startswith(tails_config_amnesia_home)
with_items: "{{ tails_config_desktop_icon_directories }}"

- name: Set the right variable for source
set_fact:
source_iface: "{{ sourcev3_interface_lookup_result if (v3_source_file.stat.exists == true) else source_interface_lookup_result }}"

- name: Set the right variable for journalist
set_fact:
journalist_iface: "{{ journalistv3_interface_lookup_result if (v3_source_file.stat.exists == true) else journalist_interface_lookup_result }}"

- debug:
var: source_iface

# Storing as host fact so we can loop over the data in one task.
- name: Assemble desktop icon info.
set_fact:
_securedrop_desktop_icon_info:
- src: desktop-source-icon.j2
filename: source.desktop
onion_url: "{{ source_interface_lookup_result.stdout }}"
onion_url: "{{ source_iface.stdout }}"
- src: desktop-journalist-icon.j2
filename: journalist.desktop
onion_url: "{{ journalist_interface_lookup_result.stdout }}"
onion_url: "{{ journalist_iface.stdout }}"

- name: Create SecureDrop interface desktop icons.
become: yes
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,22 @@
- name: Import variables
include_vars: "group_vars/all/site-specific"

- name: Lookup onion ssh files
- name: Lookup v2 onion ssh files
stat:
path: "{{ item }}-ssh-aths"
register: "ssh_onion_lookup"
with_items:
- app
- mon

- name: Lookup v3 onion ssh files
stat:
path: "{{ item }}-ssh.auth_private"
register: "ssh_v3_onion_lookup"
with_items:
- app
- mon

- name: Hacky work-around to get below logic working
set_fact:
mon_ip: "{{ monitor_ip }}"
Expand All @@ -18,6 +26,13 @@
assert:
that: "item.stat.exists or {{item.item}}_ip is defined"
with_items: "{{ ssh_onion_lookup.results }}"
when: v2_onion_services == True

- name: Confirm that either the app v3 onion ssh file exists or site-specific file exists
assert:
that: "item.stat.exists or {{item.item}}_ip is defined"
with_items: "{{ ssh_v3_onion_lookup.results }}"
when: v3_onion_services == True

- name: Create SSH config directory.
become: yes
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,6 @@
# Reuse validation logic.
- include: "{{ role_path }}/../validate/tasks/validate_tails_environment.yml"

- include: cleanup_legacy_artifacts.yml

- include: migrate_existing_config.yml

- include: copy_dotfiles.yml

- include: configure_torrc_additions.yml
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
{% set svc_grep = "grep -Po '.{16}\.onion' svc-ssh-aths" %}
{% set svc_awk = "awk -F ':' '{print $1 \".onion\"}' svc-ssh.auth_private" %}

{% if v2_onion_services and not v3_onion_services -%}
{% for svc in ssh_onion_lookup.results %}
Host {{ svc.item }}
{% set svc_grep = "grep -Po '.{16}\.onion' "+svc.item+"-ssh-aths" -%}
Expand All @@ -12,3 +15,36 @@ Host {{ svc.item }}
{% endif %}

{% endfor %}
{% endif %}

{% if v3_onion_services -%}
{% for svc in ssh_v3_onion_lookup.results %}
Host {{ svc.item }}
{% set svc_awk = "awk -F ':' '{print $1 \".onion\"}' "+svc.item+"-ssh.auth_private" -%}
{% set direct_ip = hostvars[inventory_hostname][svc.item+'_ip'] -%}
User {{ ssh_users }}
Hostname {{ lookup('pipe', svc_awk) if (svc.stat.exists and enable_ssh_over_tor) else direct_ip }}
{% if enable_ssh_over_tor and svc.stat.exists -%}
ProxyCommand /bin/nc -X 5 -x 127.0.0.1:9050 %h %p
{% else -%}
ProxyCommand none
{% endif %}

{% endfor %}
{% endif %}

{% if v2_onion_services and v3_onion_services -%}
{% for svc in ssh_onion_lookup.results %}
Host {{ svc.item + "-legacy" }}
{% set svc_grep = "grep -Po '.{16}\.onion' "+svc.item+"-ssh-aths" -%}
{% set direct_ip = hostvars[inventory_hostname][svc.item+'_ip'] -%}
User {{ ssh_users }}
Hostname {{ lookup('pipe', svc_grep) if (svc.stat.exists and enable_ssh_over_tor) else direct_ip }}
{% if enable_ssh_over_tor and svc.stat.exists -%}
ProxyCommand /bin/nc -X 5 -x 127.0.0.1:9050 %h %p
{% else -%}
ProxyCommand none
{% endif %}

{% endfor %}
{% endif %}
4 changes: 2 additions & 2 deletions install_files/ansible-base/securedrop-tails.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@
Successfully configured Tor and set up desktop bookmarks for SecureDrop!
You will see a notification appear on your screen when Tor is ready.

The Journalist Interface's Tor onion URL is: http://{{ journalist_interface_lookup_result.stdout }}
The Source Interfaces's Tor onion URL is: http://{{ source_interface_lookup_result.stdout }}
The Journalist Interface's Tor onion URL is: http://{{ journalist_iface.stdout }}
The Source Interfaces's Tor onion URL is: http://{{ source_iface.stdout }}
{% if find_aths_info_result.matched > 1 %}
SSH aliases are set up. You can use them with 'ssh app' and 'ssh mon'.
{% endif %}