securedrop icon indicating copy to clipboard operation
securedrop copied to clipboard

Support admin tools in Qubes

Open micahflee opened this issue 10 months ago • 13 comments

Fixes #7572 and #7573

This PR does the following things:

  • Makes securedrop-admin setup install more apt deps (netcat-openbsd and tor) which both are already installed in Tails (so it doesn't hurt to add it) but need to be installed in the Debian qube.
  • Adds securedrop-admin qubesconfig command to configure a Debian qube:
    • Makes sure /etc/tor/torrc includes the line ClientOnionAuthDir /var/lib/tor/onion_auth
    • Adds the onion service auth files to /var/lib/tor/onion_auth
    • Configures ssh aliases for app and mon
  • Updates ansible-base/roles/validate to check if it's running in Debian or Tails, and depending on the result:
    • If it's Tails, it keeps the same Tails validation (make sure there's a persistent volume, etc.)
    • If it's Debian, it ensures that it's a Qubes VM
    • If it's neither, it fails

One important change is in the validate role, I renamed the variable from securedrop_validate_tails_environment to just securedrop_validate_environment (since it now validates either Tails or Qubes). I grepped the codebase for securedrop_validate_tails_environment and I don't see it used anywhere else, but I'm not sure if it's used in development docs or the larger SDW ecosystem.

Test plan

SD admin tools should now work in both Tails and Qubes.

There's a separate issue for supporting installing SD in Qubes (#7573), but we can test installing SD right now if the SD servers have public internet-accessible IP addresses 🙃 . So to test this, I created DigitalOcean droplets, two for Tails and two for Qubes.

Setting up SD server VMs

If you want to test in the same way, here's briefly how to set up the droplets.

Create 4 DigitalOcean droplets, running Ubuntu 24.04, and make them cheap, and give them an ssh key. You can call them tails-app, tails-mon, qubes-app, and qubes-mon.

For each VM, ssh as root with your ssh key and create an sdadmin user with a password, that's a sudoer, and that can be logged into with the same ssh key:

# Create the user
adduser sdadmin # type the password here

# Add to the sudo group
usermod -aG sudo sdadmin

# Copy the ssh authorized_keys file to the new user
mkdir /home/sdadmin/.ssh
cp ~/.ssh/authorized_keys /home/sdadmin/.ssh
chown -R sdadmin:sdadmin /home/sdadmin/.ssh
chmod 700 /home/sdadmin/.ssh

Now, you should have the VMs necessary to install SD from both Tails and Qubes to test.

Admin environments

In Qubes, create a new standalone VM based on debian-12-xfce.

In Tails, make sure you have a persistent volume and a fresh clone of the securedrop repo.

Make sure both of these environments have the SSH key that's included in the DigitalOcean VMs.

Install SD from both Tails and Qubes

We're basically at this point in the install docs now: https://docs.securedrop.org/en/stable/admin/installation/install.html

Make sure you can ssh as the sdadmin user to the appropriate app and mon servers for your environment, and that you can successfully use sudo.

In both environments, make sure setup works properly:

sudo apt update
./securedrop-admin setup

This should install apt dependencies, make a virtual env, and install pip dependencies. It should work in both platforms.

You'll need a test application PGP key and OSSEC PGP, and a test email with creds for OSSEC alerts. So make sure to generate all of that, and put the public keys in install_files/ansible-base. Then in both environments, run:

./securedrop-admin --force sdconfig

In both environments, install SD:

./securedrop-admin --force install

Wait for SD to install. It should work! If you're running Tails, it should do a Tails validation at the beginning, and if you're running Qubes, it should do a Qubes validation at the beginning.

After installs are finished:

  • In Tails, run: ./securedrop-admin --force tailsconfig
  • In Qubes, run: ./securedrop-admin --force qubesconfig

You should now be able to ssh app and ssh mon, over the authenticated tor onion services, from either Tails or Qubes.

You should also be able to run ./securedrop-admin --force install again to re-run the ansible playbook, and this time it should connect via ssh over onion services.

Remember to delete your DigitalOcean VMs!

Checklist

This change accounts for:

  • [ ] any required additional documentation
  • [x] any necessary AppArmor changes (added or removed application files)
  • [x] any impact on new SecureDrop installs and upgrades
  • [x] our dependency update policy

We'll need documentation updates to include Qubes, but that should likely be a later PR.

Other thoughts

The Debian qube for SD admin has secrets (including app-journalist.auth_private even though it won't be used for admin stuff), and it also has clearnet access to the internet. On the other hand, the admin shouldn't do anything with this VM except run specific commands in the terminal to manage SD, not even use a web browser. So I think the risk is maybe low.

The ./securedrop-admin setup command installs the tor apt package. Without specifically adding the official Debian tor repo first (which it doesn't do), this will install tor from Debian's bookworm repo, a slightly older version. This probably is fine though since we're only using tor to connect to onion services, not host them.

micahflee avatar Jun 10 '25 21:06 micahflee

woot \o/

Just quickly replying to your other thoughts:

The Debian qube for SD admin has secrets (including app-journalist.auth_private even though it won't be used for admin stuff), and it also has clearnet access to the internet. On the other hand, the admin shouldn't do anything with this VM except run specific commands in the terminal to manage SD, not even use a web browser. So I think the risk is maybe low.

I do think we need somewhere for the admin to do the web admin tasks via Tor Browser. I would be OK doing it in the same VM since that's how it already works in Tails, but not sure if there's a better way.

this will install tor from Debian's bookworm repo, a slightly older version. This probably is fine though since we're only using tor to connect to onion services, not host them.

We already mirror Tor packages for focal/noble into our apt.freedom.press repo, we can do the same for bookworm as well.

legoktm avatar Jun 10 '25 22:06 legoktm

The official tor repo includes the torbrowser-launcher package. So maybe one update is to make the qubesconfig ansible playbook at the apt.freedom.press repo, make sure tor is updated, and install torbrwoser-launcher. Then the admin can use Tor Browser in this VM.

micahflee avatar Jun 10 '25 22:06 micahflee

So maybe one update is to make the qubesconfig ansible playbook at the apt.freedom.press repo

I think we can rely on the template to configure the apt.freedom.press repository on Qubes.

legoktm avatar Jun 11 '25 13:06 legoktm

I deleted tailsconfig and qubesconfig and merged them together into localconfig. This will definitely require updating docs. I also replaced various references (mostly in comments) from tailsconfig to localconfig. And I slightly updated the code in journalist_gui to subprocess out to localconfig instead. I didn't update any of the strings though, and I made sure the tests still pass.

I made the Qubes config ansible playbook work with appVMs, not just standalone VMs. Instead of storing onion keys in /var/lib/tor/onion_auth/ it stores them in /rw/usrlocal/lib/tor/onion_auth/. And instead of updating /etc/tor/torrc, it adds a new script, /rw/config/torrc_additions.sh and makes it autorun on boot, and this script updates /etc/tor/torrc and restarts tor.

While testing this, I discovered that ./securedrop-admin localconfig fails if tor isn't installed (because the debian-tor user doesn't exist). This shouldn't be a problem, because tor will always be installed if we're using one of the sd templates with the apt.freedom.press repo instead of debian-12-xfce. I'm not sure which template we'd need to use, but we need to make sure that template includes these packages: python3-virtualenv python3-yaml python3-pip virtualenv libffi-dev libssl-dev libpython3-dev sq-keyring-linter netcat-openbsd tor

@zenmonkeykstop you wrote:

Would likely need an additional test in admin/tests/test_securedrop-admin.py

Looking through test_securedrop-admin.py, it looks like there actually isn't currently a test for tailsconfig, or for any of the commands that just subprocess out to ansible-playbook. I'm not sure how I'd go about writing a test for that too. Any suggestions? Or do you think it's fine to not add a test for this?

Other than possibly writing a test, this PR should be ready for review again now.

micahflee avatar Jun 12 '25 18:06 micahflee

My bad, looks like we don't have coverage for tailsconfig, so ignore that one.

zenmonkeykstop avatar Jun 12 '25 18:06 zenmonkeykstop

Stepped through install on Qubes (also using DO droplets for sanity's sake):

  • [x] sdconfig works
  • [X] install playbook succeeds!
  • [X] source interface is up!
  • [ ] localconfig succeeds, but connecting subsequently via ssh app fails with:
OpenSSH_9.2p1 Debian-2+deb12u6, OpenSSL 3.0.16 11 Feb 2025
debug1: Reading configuration data /home/user/.ssh/config
debug1: /home/user/.ssh/config line 2: Applying options for app
debug1: Reading configuration data /etc/ssh/ssh_config
debug1: /etc/ssh/ssh_config line 19: include /etc/ssh/ssh_config.d/*.conf matched no files
debug1: /etc/ssh/ssh_config line 21: Applying options for *
debug1: Executing proxy command: exec /bin/nc -X 5 -x 127.0.0.1:9050 [redacted].onion 22
debug1: identity file /home/user/.ssh/id_rsa type 0
debug1: identity file /home/user/.ssh/id_rsa-cert type -1
debug1: identity file /home/user/.ssh/id_ecdsa type -1
debug1: identity file /home/user/.ssh/id_ecdsa-cert type -1
debug1: identity file /home/user/.ssh/id_ecdsa_sk type -1
debug1: identity file /home/user/.ssh/id_ecdsa_sk-cert type -1
debug1: identity file /home/user/.ssh/id_ed25519 type -1
debug1: identity file /home/user/.ssh/id_ed25519-cert type -1
debug1: identity file /home/user/.ssh/id_ed25519_sk type -1
debug1: identity file /home/user/.ssh/id_ed25519_sk-cert type -1
debug1: identity file /home/user/.ssh/id_xmss type -1
debug1: identity file /home/user/.ssh/id_xmss-cert type -1
debug1: identity file /home/user/.ssh/id_dsa type -1
debug1: identity file /home/user/.ssh/id_dsa-cert type -1
debug1: Local version string SSH-2.0-OpenSSH_9.2p1 Debian-2+deb12u6
kex_exchange_identification: Connection closed by remote host
Connection closed by UNKNOWN port 65535

I'll poke at it a bit more later to see what's going on - the .ssh/config file looks correct and the auth files are also set up correctly - might need to tweak torrc so it picks them up from /rw/ or something.

zenmonkeykstop avatar Jun 13 '25 21:06 zenmonkeykstop

@zenmonkeykstop I'm not sure why I didn't reproduce this to begin with, but I reinstalled SD and can reproduce it now. And I just confirmed that it's AppArmor's fault. I know the fix. I'll have an updated PR for you soon.

Also, this PR will also close #7573 -- I just tested using VMs on my local network (running in VirtualBox with bridged networking, so they have IPs on my LAN), and it installs fine.

micahflee avatar Jun 16 '25 22:06 micahflee

Ok, ready for review again! This should fix the problem with sshing in over tor after installation is complete (it was AppArmor's fault). Also, I've tested installing from Qubes over LAN, so this should fix #7573 as well.

Test plan for installing from Qubes over LAN

Set up SD servers on the LAN instead of in the cloud

On an Ubuntu computer, using VirtualBox, I made two VMs and set networking to bridged. This way they'll have IP addresses on my LAN. I installed Ubuntu Server 24.04 on both of them basically following these instructions, but using DHCP for networking.

After installation finished, I logged into sdadmin on both VMs just to check their IP address. For me:

  • app is 192.168.0.253
  • mon is 192.168.0.176

Set up an sd-admin-dev VM

In Qubes, I created a new appVM called sd-admin-test, based on the debian-12-xfce template, and with sys-firewall as the netVM.

Add the appropriate ssh key to sd-admin-test.

From a terminal in sd-admin-test, make sure you can ssh into the servers (like ssh [email protected]) using a password, and then ssh-copy-id to copy the public key to them, and make sure you can ssh in without a password.

Clone the securedrop repo and checkout the branch:

git clone https://github.com/freedomofpress/securedrop.git
cd securedrop
git checkout 7572-admin-tools-qubes

Copy the application public key and ossec public key to install_files/ansible-base, and also keep track of their fingerprints for configuring SD. Mine are called submission.asc and ossec.asc.

Configure and install SD

Install apt deps and virtual env:

sudo apt update
./securedrop-admin setup

Configure SD -- make sure to use the local IPs for app and mon:

./securedrop-admin --force sdconfig

Then install:

./securedrop-admin --force install

After the install finishes, configure the local device:

./securedrop-admin --force localconfig

Now you can run ssh app and ssh mon.

You should also be able to install again, this time over the onion service:

./securedrop-admin --force install

To make sure I didn't break anything in Tails, I also tested this whole setup from Tails (except using DigitalOcean VMs on the internet instead VirtualBox ones locally, since I didn't have an extra computer handy while booting to Qubes). I confirmed that it still works.

Final thoughts

This PR will require new documentation, both to cover Qubes and also because the Tails command tailsconfig has changed to localconfig.

And if the goal is to have sd-admin be an appVM and not a standaloneVM, then we will need to install more dependencies in the template (I assume sd-large-bookwork-template). Otherwise the admin user will need to run ./securedrop-admin setup every time.

These packages will need to be installed in the template: python3-virtualenv python3-yaml python3-pip virtualenv libffi-dev libssl-dev libpython3-dev sq-keyring-linter netcat-openbsd tor torbrowser-launcher.

And I'd recommend the sd-admin VM include these applications in the menu:

  • Xfce Terminal
  • Tor Browser

micahflee avatar Jun 17 '25 00:06 micahflee

Plugging through the install on Qubes now: config, install, and connection via ssh-over-tor working well. I hit a snag with backups/restores though:

  • we need to add rsync to the package list for templates etc, it's not in debian-12-* by default
  • the backup rsync commands use the direct v3 onion service addresses, not aliases defined in .ssh/config. This isn't a problem in Tails as it handles Tor connections transparently but it fails in the Qubes setup.

Will update with more as I go through it.

zenmonkeykstop avatar Jun 17 '25 16:06 zenmonkeykstop

I don't have Tails setup with me but the Qubes version is looking good with the provisos

zenmonkeykstop avatar Jun 17 '25 17:06 zenmonkeykstop

I've made the following changes:

  • now tailsconfig and qubesconfig are aliases for localconfig, so we don't have to update the docs yet
  • rsync is installed as dependency
  • backup and restore work, in both Qubes and Tails
  • reset_admin_access works in both
  • verify works in both -- though when I ran it and it was running tests on my VMs, some of them seemed to be failing. I didn't let it finish, but it at least runs now

Another thing that occurred to me is the chicken and egg problem. The current instructions for installing SDW expect SD to already be installed. If a brand new admin is installing SD in Qubes for the first time, they won't have an SD template to create their sd-admin VM based on. The steps will be:

  • Install Qubes
  • Install dependencies in debian-12-xfce
  • Create an sd-admin qube based off debian-12-xfce
  • Inside sd-admin, install SD
  • After SD is installed, install SDW

This can all be handled with the documentation, but I should wish it were a bit simpler than this.

micahflee avatar Jun 17 '25 22:06 micahflee

It should be simpler for sure - we should have a one-liner to add our yum repo in dom0 soon once the securedrop-dom0-keyring RPM work lands. When we have that, an alternative could be:

  • admin installs said keyring package from qubes-contrib
  • admin installs a hypothetical RPM from yum.freedom.press that includes provisioning logic for a template and an appvm for admin tools (which just runs and sets them up via a postint script)
  • the template has the debian-packaged version of the admin tools, likely simplified coz we can drop both the apt setup bit (just have dependencies listed in the deb) and the venv setup (we can package the venv as well).
  • admin starts a terminal in the sd-admin VM and runs securedrop-admin sdconfig/install/localconfig etc.

The other missing piece is creating a submission key safely - that would have to happen in a vault VM, ideally one that's only accessible from sd-admin and sd-app if it's created.

I'm in conf mode today but will carve out some review time if I can.

zenmonkeykstop avatar Jun 18 '25 15:06 zenmonkeykstop

Hmm, I also hadn't thought of the bootstrapping problem. We could also have a mode in SDW that only sets up the admin VM and skips the others until you finish the install and configure it; I'm not sure if that's simpler or more complicated than having a second RPM/setup package.

legoktm avatar Jun 18 '25 23:06 legoktm

I think the argument for a separate RPM package is just that there may be cases where we want a workstation that's admin-only and doesn't have the submission key or client, so there's a logical separation of functionality there. But it could be a two-part setup as well.

zenmonkeykstop avatar Jun 23 '25 14:06 zenmonkeykstop

Repeating tests of securedrop-admin on Tails:

  • [x] setup - works
  • [x] sdconfig - works
  • [x] install - works
  • [x] tailsconfig - works
  • [x] after localconfig, ssh app and ssh mon work
  • [x] generate_v3_keys works
  • [x] backup - works
  • [x] restore - works
  • [x] update - works
  • [x] check_for_updates - works
  • [x] logs - works
  • [x] reset_admin_access - works
  • [x] verify (runs testinfra suite against prod servers) - works

LGTM!

zenmonkeykstop avatar Jun 23 '25 18:06 zenmonkeykstop