Die besten Fakten als Buch š https://shopillon.de/faktillon/
Ehrliche Nachrichten auf šĀ der-postillon.com
If youāve ever scrolled through the Guardian homepage, you may have come across the āContact the Guardian securelyā banner. This links to a page explaining how to share sensitive information with the Guardian in a number of different ways. Of these, the one that offers the best security is SecureDrop.
SecureDrop is a system developed by the Freedom of the Press Foundation to enable sources to contact media organisations anonymously. It is a system widely respected and deployed in a large number of newsrooms.
The current configuration of SecureDrop involves three separate machines ā one connected to the SecureDrop server running Tails OS, another āair gappedā entirely offline machine for decrypting the messages on, and (typically) a final online machine for emailing PGP encrypted messages to the relevant journalists. Transferring messages between the machines typically involves a lot of carefully managed USB sticks, time and patience.
The Freedom of the Press Foundation is working on a more streamlined solution for journalists working with SecureDrop called SecureDrop Workstation. SecureDrop Workstation is based on Qubes OS. It removes the need for a separate āair gappedā machine (see here for details of the tradeoffs), replacing it with an offline virtual machine or āQubeā. Messages can be downloaded and read on the same machine ā on an interface that looks much like a standard chat client, with all the decryption handled in the background.
This blog post is focused on some of our learnings whilst working with Qubes OS. Our objective here was to set up multiple SecureDrop Workstation machines, with the SecureDrop Workstation app installed along with some other useful tools.
Configuring a Qubes workstation was a new challenge for the team as we abandoned years of experience writing Infrastructure as Code for the cloud and started learning how to write Salt configuration. Salt (also know as SaltStack) is a management engine available by default in Qubes.
Most of the Salt code in this post would have been impossible to write without inspiration from the SecureDrop Workstation repo (thank you, Freedom of the Press Foundation!). Itās also worth noting that we are not security or Qubes experts, this is just what worked for us at the Guardian.
A rough idea of what we were trying to achieve with Qubes is as follows:
An offline VM based on Debian 11;
Some packages installed from the default repositories and a custom repository;
The VM should be āamnesicā ā after a restart it should be reset to a consistent state (so any generated files/downloaded data should be deleted);
A custom package installed from a private repository hosted in Amazon S3;
A Nautilus extension;
A configuration file containing secrets that canāt be hard-coded.
To begin, letās pick just the first of those items: an offline VM based on debian 11.
Salt configuration in Qubes starts with a .top file:
# guardian.top
base:
dom0:
- guardian-vms
What this top file says is āon dom0, apply the state guardian-vmsā (dom0 is the root VM on Qubes OS)
The āstateā in question refers to a āstate fileā ā with the extension .sls. Hereās our first state file.
# guardian-vms.sls
create-guardian-template:
qvm.vm:
- name: guardian-template
- clone:
- source: debian-11
- label: black
- prefs:
- netvm: ""
create-app:
qvm.vm:
- name: app
- present:
- template: guardian-template
- label: green
- prefs:
- template: guardian-template
- netvm: ""
In the state file above we do two things:
Create the āguardian-templateā TemplateVM based on Debian 11, which is offline;
Create a āguardianā AppVM based on the template VM. It too is offline.
You can read more about Template and App VMs in the Qubes documentation. At a high level, the template VM (guardian-template) is where we install any relevant software. While this template doesnāt have direct access to the internet, we can still install packages on it via an updates proxy. The AppVM (app) is based on the TemplateVM. Every time āappā is restarted, the file system (except the home folder) resets to the state of āguardian-templateā. This is one of the many security features of Qubes OS ā resetting the system to a known stable state helps ensure any malicious code installed during a session will be removed.
OK, fantastic, weāve got an offline VM called āappā. This VM could be useful for some basic tasks, but it doesnāt yet have any useful software installed on it. Letās install some tools for viewing, editing and sanitising files. One option for installing software would be to simply open a terminal in the template VM and install some software with APT. To do that in the Salt configuration, weāll need a new state file
# install-packages.sls
install-packages:
pkg.installed:
- pkgs:
- libreoffice
- gedit
- vlc
We also need to update our .top file to tell Qubes to apply the install-packages state to the guardian-template VM:
# guardian.top
base:
dom0:
- guardian-vms
guardian-template:
- install-packages
Thatās all very well if the software you want to use is available in the default repositories. What if you want to install something else? This is possible in Qubes, but is ānot recommended for trusted templatesā. You can read the full details on installing software from other sources on the Qubes website ā here weāll look at adding a package from another source to our template in as simple a way as possible.
For our use case we wanted to install dangerzone, a tool maintained by the Freedom of the Press Foundation which allows you to ātake potentially dangerous PDFs, office documents, or images and convert them to safe PDFsā.
First, we need to modify our template VM to give it network access (software from untrusted sources cannot be fetched via the updates proxy). We do this by setting the netvm property to sys-firewall. At the same time letās set the label to āredā to indicate that this template is less trusted (now that it has a network connection):
# guardian-vms.sls
create-guardian-template:
qvm.vm:
- name: guardian-template
- clone:
- source: debian-11
- label: red
- prefs:
- netvm: sys-firewall
Next, we need to update our install software state file to set up the repository from which dangerzone will be installed:
# install-packages.sls
# freedomofpress repo is required to install dangerzone
add freedomofpress repo:
pkgrepo.managed:
- name: "deb <a href="https://packages.freedom.press/apt-tools-prod" rel="nofollow">https://packages.freedom.press/apt-tools-prod</a> bullseye main"
- keyserver: <a href="http://keys.openpgp.org" rel="nofollow">keys.openpgp.org</a>
- keyid: DE28AB241FA48260FAC9B8BAA7C9B38522604281
- humanname: freedomofpress
install-guardian-dependencies:
pkg.installed:
- pkgs:
- libreoffice
- gedit
- vlc
- dangerzone # this package will now be available
At this point, we have the ability to open office documents and clean files within our AppVM. The VM itself is not connected to the internet, which reduces the chances of it being compromised.
At the Guardian, we needed to go further. We use Qubes VMs to process sensitive documents, and weād like as much certainty as possible that those documents can only be read by specific people. With the current AppVM behaviour, while the packages and operating system of the VM will be reset on every restart, the contents of the home folder persists. For a completely āamnesicā VM that wipes all documents on a restart, we need to use a disposable VM, or āDispVMā.
Disposable VMs, rather than being based on TemplateVMs, use a special kind of AppVM as their base template. We need to make some changes to our state files:
Updated guardian-vms.sls:
# guardian-vms.sls
create-guardian-template:
qvm.vm:
- name: guardian-template
- clone:
- source: debian-11
- label: black
create-guardian-template-disp:
qvm.vm:
- name: guardian-template-disp
- present:
- template: guardian-template
- label: black
- class: AppVM
- prefs:
- template: guardian-template
- template_for_dispvms: True
- netvm: ""
create-app:
qvm.vm:
- name: app
- present:
- template: guardian-template-disp
- label: green
- class: DispVM
- prefs:
- template: guardian-template-disp
- netvm: ""
Note that we now have three VMs or Qubes defined: the template, the AppVM we use as a template for our DispVM, and the āappā DispVM itself. āappā is a ānamed disposableā Qube. Weāve now got a fully amnesic VM with some useful software installed on it.
Next, we wanted to add a custom right click menu option to the file explorer that would run a script on the file (to save users having to open up a terminal window). We did this using a nautilus extension. Nautilus extensions are installed inside the home folder, which for our āappā VM will be based on guardian-template-disp ā our disposable template. For this, weāll need a new state file, an updated top file and the extension itself:
# guardian.top
base:
dom0:
- guardian-vms
guardian-template-disp:
- install-nautilus-extension
guardian-template:
- install-packages
#install-nautilus-extension.sls
nautilus-extension:
file.managed:
- name: /home/user/.local/share/nautilus-python/extensions/nautilus-extension.py
- source: salt://guardian/nautilus-extension.py
- makedirs: true
- user: user
- group: user
- mode: 555
# nautilus-extension.py
from urllib.parse import urlparse, unquote
from gi.repository import Nautilus, GObject
class RunSpecialScriptMenuProvider(GObject.GObject, Nautilus.MenuProvider):
def run_special_script(self, menu, files):
for file in files:
file_path = unquote(urlparse(file.get_uri()).path)
print("Hi! I am processing " + file_path)
def get_file_items(self, window, files):
item = Nautilus.MenuItem(name='Run special script',
label='Run special script'
)
item.connect('activate', self.run_special_script, files)
return item,
The file.managed action in install-nautilus-extension.sls will look for a file on dom0 at /srv/salt/guardian/nautilus-extension.py and copy it to the location ānameā on the VM which is run on (in our case guardian-template-disp), creating any required intermediate directories and applying the file permissions indicated.
Letās take a look at dealing with configuration that you donāt want to hard code into your state files or other installation files (perhaps because theyāre checked in to a public repo). We can make use of the jinja2 template support in Salt to swap in values from a config file. In the example below, letās assume we want to set up an AWS credentials file on one of our VMs, with an access key and secret key.
Hereās the template credentials file:
credentials.j2:
[default]
aws_access_key_id = {{ access_key_id }}
aws_secret_access_key = {{ secret_access_key }}
We need our config file itself:
config.json:
{
"access_key_id": "ACCESSKEY",
"secret_access_key": "reallyverysecretkey"
}
Then we need a state file to wire it all together:
# setup-aws-credentials.sls
{% import_json "guardian/config.json" as config %}
install-config:
file.managed:
- name: /home/user/.aws/credentials
- source: "salt://guardian/credentials.j2"
- template: jinja
- context:
access_key_id: {{ config.access_key_id }}
secret_access_key: {{ config.secret_access_key }}
- user: user
- group: user
- makedirs: True
Weād also need to update our top file to apply the new state file to a Qube. There wouldnāt be much point having AWS credentials for an offline Qube, so in the topfile below Iāve invented a new online Qube ā which would need defining in guardian-vms.sls.
# guardian.top
base:
dom0:
- guardian-vms
guardian-template-disp:
- install-nautilus-extension
guardian-template:
- install-packages
aws-management-app:
- setup-aws-credentials
Finally, letās take a look at actually applying state files to a Qubes system.
For this project, we used an RPM package to bundle up our state files and install them into dom0. Making this package was made a lot easier thanks to a tool called FPM. After installation, the folder structure on dom0 looks like this:
/srv/salt/
guardian.top
install-nautilus-extension.sls
guardian-vms.sls
install-packages.sls
setup-aws-credentials.sls
guardian/
nautilus-extension.py
credentials.j2
config.json
Once the files are installed, we need to tell Qubes to make use of them.
Firstly, letās enable our .top file:
sudo qubesctl top.enable guardian
To apply a state to dom0, you can use the following command:
qubesctl --show-output state.apply guardian-vms
state.apply takes a comma separated list of state files to apply (without the .sls extension). If you just want to apply all states associated with an enabled topfile, then you can run:
qubesctl --show-output state.highstate
To apply all enabled states to a specific VM (eg. if we have added a new package to install-packages.sls), you can skip dom0 updates and target that VM:
qubesctl --show-output --skip-dom0 --targets guardian-template state.highstate
Or if you want to just apply a specific state to that VM:
qubesctl --show-output --skip-dom0 --targets guardian-template state.apply install-packages
Finally, if you want to apply Salt configuration to dom0 and all of your Qubes you can use the below command. Beware this will take a really long time ā¦
sudo qubesctl --show-output --all state.highstate
Getting to grips with Qubes OS and Salt was a sharp learning curve for us. Hopefully this blogpost will be of use to anyone trying to do the same. If you have any questions or feedback for us, please contact digital.investigations@theguardian.com.