Support admin tools in Qubes
Fixes #7572 and #7573
This PR does the following things:
- Makes
securedrop-admin setupinstall more apt deps (netcat-openbsdandtor) 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 qubesconfigcommand to configure a Debian qube:- Makes sure
/etc/tor/torrcincludes the lineClientOnionAuthDir /var/lib/tor/onion_auth - Adds the onion service auth files to
/var/lib/tor/onion_auth - Configures ssh aliases for app and mon
- Makes sure
- Updates
ansible-base/roles/validateto 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.
woot \o/
Just quickly replying to your other thoughts:
The Debian qube for SD admin has secrets (including
app-journalist.auth_privateeven 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.
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.
So maybe one update is to make the
qubesconfigansible playbook at the apt.freedom.press repo
I think we can rely on the template to configure the apt.freedom.press repository on Qubes.
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.
My bad, looks like we don't have coverage for tailsconfig, so ignore that one.
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 appfails 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 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.
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:
-
appis 192.168.0.253 -
monis 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
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
rsyncto 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.
I don't have Tails setup with me but the Qubes version is looking good with the provisos
I've made the following changes:
- now
tailsconfigandqubesconfigare aliases forlocalconfig, so we don't have to update the docs yet - rsync is installed as dependency
-
backupandrestorework, in both Qubes and Tails -
reset_admin_accessworks in both -
verifyworks 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-adminqube based offdebian-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.
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.
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.
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.
Repeating tests of securedrop-admin on Tails:
- [x]
setup- works - [x]
sdconfig- works - [x]
install- works - [x]
tailsconfig- works - [x] after
localconfig,ssh appandssh monwork - [x]
generate_v3_keysworks - [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!