Skip to content

Commit

Permalink
Merge pull request #4675 from zenmonkeykstop/4629-v3-tailsconfig
Browse files Browse the repository at this point in the history
Adds support for v3 Onion urls to ./securedrop-admin tailsconfig`
  • Loading branch information
conorsch authored Aug 27, 2019
2 parents 771e9c5 + bbad1cb commit 1e00822
Show file tree
Hide file tree
Showing 11 changed files with 193 additions and 93 deletions.
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
36 changes: 29 additions & 7 deletions install_files/ansible-base/inventory-dynamic
Original file line number Diff line number Diff line change
Expand Up @@ -92,9 +92,9 @@ def lookup_admin_username():
return admin_username


def lookup_tor_hostname(hostname):
def lookup_tor_v2_hostname(hostname):
"""
Extract Onion URL from HidServAuth file that was fetched back locally.
Extract Onion v2 URL from HidServAuth file that was fetched back locally.
Returns Onion URL for given inventory hostname.
"""
aths_path = os.path.join(SECUREDROP_ANSIBLE_DIRECTORY,
Expand All @@ -106,13 +106,32 @@ def lookup_tor_hostname(hostname):
# assuming the file is a raw `hostname` file generated by tor,
# but the SD playbooks format the line with `HidServAuth` prefix,
# so it can be concatenated into the torrc file on Tails.
tor_hostname = tor_config[1]
tor_v2_hostname = tor_config[1]
except IndexError:
msg = ("Tor config file for '{}' ",
msg = ("Tor v2 config file for '{}' ",
"appears to be empty").format(hostname)
raise Exception(msg=msg)

return tor_hostname
return tor_v2_hostname


def lookup_tor_v3_hostname(hostname):
"""
Extract Onion v3 URL from .auth_private file that was fetched back locally.
Returns Onion URL for given inventory hostname.
"""
aths_path = os.path.join(SECUREDROP_ANSIBLE_DIRECTORY,
"{}-ssh.auth_private".format(hostname))
with io.open(aths_path, 'r') as f:
tor_config = f.readline().rstrip().split(":")
try:
tor_v3_hostname = "{}.onion".format(tor_config[0])
except IndexError:
msg = ("Tor v3 config file for '{}' ",
"appears to be empty").format(hostname)
raise Exception(msg=msg)

return tor_v3_hostname


def lookup_ssh_address(hostname):
Expand All @@ -122,10 +141,13 @@ def lookup_ssh_address(hostname):
"""
ssh_address = lookup_local_ipv4_address(hostname)
try:
ssh_address = lookup_tor_hostname(hostname)
ssh_address = lookup_tor_v3_hostname(hostname)
# Don't assume ATHS files are present; they won't be on first run.
except (IndexError, EnvironmentError):
pass
try:
ssh_address = lookup_tor_v2_hostname(hostname)
except (IndexError, EnvironmentError):
pass

return ssh_address

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
4 changes: 0 additions & 4 deletions install_files/ansible-base/roles/tails-config/tasks/main.yml
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.

Loading

0 comments on commit 1e00822

Please sign in to comment.