Zola2023-05-16T00:00:00+00:00/atom.xmlSetting up a Tailscale ProxyVM on QubesOS2023-05-16T00:00:00+00:002023-05-16T00:00:00+00:00/blog/qubesos-tailscale/<p>QubesOS and Tailscale are both useful tools to protect your privacy and
security. However, QubesOS's unique network structure of many VMs being used on
a single host requires additional configuration to be used similarly to
Tailscale.</p>
<p>Once this setup is complete, any AppVM configured to use <code>sys-tailnet</code> as its
network qube will have access to your Tailscale network ("tailnet"). All AppVMs
will be able to utilize Tailscale features such as MagicDNS or custom DNS
records to route traffic within your tailnet. The Qubes firewall rules can also
be used to restrict traffic to and from a particular AppVM.</p>
<blockquote>
<p><em>Warning</em>: Tailscale is a unique mesh VPN that doesn't have the same privacy
and security properties as a typical VPN provider. Ensure you understand how
Tailscale works before relying on it to protect your QubesOS network traffic.</p>
</blockquote>
<h1 id="potential-strategies">Potential Strategies</h1>
<h2 id="a-tailscale-appvm">A Tailscale AppVM</h2>
<p>If you only need to access your tailnet for a particular purpose, it may be
sufficient to simply install Tailscale in the AppVM that needs access it. For
example, if you need to access your job's git server from your <code>work</code> AppVM,
just install Tailscale in <code>work</code> and you're good to go -- it will only affect
the network traffic from your <code>work</code> VM.</p>
<p>Conveniently, this works around one of the most common Tailscale problems: it
allows you to connect to multiple tailnets at once. This is useful if you need
your work AppVM to be connected to your employer's tailnet, but you also want to
access a personal tailnet.</p>
<p>However, if you need to connect several AppVMs, or regularly connect DispVMs, to
your tailnet, a more complex configuration is needed.</p>
<h2 id="one-node-per-appvm">One Node per AppVM</h2>
<p>Perhaps the easiest way to configure Tailscale on QubesOS would be to install
the Tailscale daemon (<code>tailscaled</code>) in your Template VMs, then register each
AppVM or DispVM based off of those templates as its own node.</p>
<p>The one benefit that this method provides is that you have much greater control
over what each VM can access on your tailnet. For instance, you could log into
different groups of AppVMs with different users, then use Tailscale's Access
Control Lists to limit access to network resources.</p>
<p>However, this method is inconvenient in several ways. For one, Tailscale's
pricing model limits you by the number of devices on your tailnet:</p>
<p><img src="./tailscale-pricing.png" alt="Screenshot of IRC pricing model." /></p>
<p>While it would be pretty difficult to use 100 devices, using 100 VMs (especially
if you want to use disposable VMs frequently on within your tailnet) isn't. I'm
not actually sure how deregistering a device works on Tailscale, but it's not
something I would want to think about regularly, especially if I intend to have
multiple QubesOS machines or users on my network.</p>
<h2 id="sys-tailnet"><code>sys-tailnet</code></h2>
<p>A better way to connect your QubesOS machine to your tailnet is to create a
dedicated network VM, which I call <code>sys-tailnet</code>. Following the idioms of
QubesOS, you can register <code>sys-tailnet</code> as a network provider for other AppVMs.
While this is certainly the most complicated way to configure Tailscale on your
Qubes machine, it has the benefit of integrating well with the rest of the
QubesOS networking stack, including enforcing per-AppVM firewalls and
controlling which AppVMs route traffic through your tailnet.</p>
<p>An additional benefit of this method is that, as far as Tailscale is concerned,
your QubesOS machine is only a single device, no matter how many VMs you're
running through <code>sys-tailnet</code>. This greatly reduces the frequency with which you
have to authenticate (at least compared to the previous method).</p>
<h1 id="sys-tailnet-guide"><code>sys-tailnet</code> Guide</h1>
<p>You can connect <code>sys-tailnet</code> to Tailscale like any other Linux system. Once
you're authenticated and connected to your tailnet, configuring <code>sys-tailnet</code> is
much like configuring any other ProxyVM in QubesOS.</p>
<h2 id="creating-sys-tailnet">Creating <code>sys-tailnet</code></h2>
<p>First, create a new AppVM named <code>sys-tailnet</code>. For now, the only requirement for
the qube is that you can install <code>tailscaled</code>, so it's best to pick a template
that Tailscale has a package repository you can get automatic updates from. See
Tailscale's <a href="https://tailscale.com/kb/1031/install-linux/">Setting up Tailscale on Linux</a>
guide to check if they publish a repository for your distro.</p>
<p><img src="./create-sys-tailnet.png" alt="Creating sys-tailnet" /></p>
<p>If you want to avoiding adding Tailscale's package repository to your
TemplateVM, consider making <code>sys-tailnet</code> a StandaloneVM or creating a new
TemplateVM for it. You should also select a networking qube to route your
Tailscale traffic through (you likely want this to be <code>sys-firewall</code>).</p>
<p><img src="./advanced-sys-tailnet.png" alt="Advanced settings for sys-tailnet" /></p>
<p>Also, in the "Advanced" tab, mark that <code>sys-tailnet</code> provides network access to
other qubes.</p>
<h2 id="installing-tailscaled">Installing <code>tailscaled</code></h2>
<p>Tailscale <a href="https://tailscale.com/kb/1031/install-linux/">provides instructions</a>
for installing <code>tailscaled</code> on a large variety of Linux distros. You'll want to
perform these instructions in <code>sys-tailnet</code> if you configured it as a
StandaloneVM, or in your <code>sys-tailnet</code>'s TemplateVM if you configured it as an
AppVM. For Fedora, for example, you just install the Tailscale repository:</p>
<pre style="background-color:#282828;">
<code><span style="color:#fdf4c1;">sudo dnf config-manager --add-repo https://pkgs.tailscale.com/stable/fedora/tailscale.repo
</span></code></pre>
<p>And then use <code>dnf</code> to install <code>tailscaled</code> like any other package:</p>
<pre style="background-color:#282828;">
<code><span style="color:#fdf4c1;">sudo dnf install tailscale
</span></code></pre><h2 id="configuring-sys-tailnet">Configuring <code>sys-tailnet</code></h2>
<h3 id="if-sys-tailnet-is-a-standalonevm">If <code>sys-tailnet</code> is a StandaloneVM</h3>
<p>If <code>sys-tailnet</code> is a StandaloneVM, you can continue to configure it just like
any other Linux system. Enable and start <code>tailscaled</code>:</p>
<pre style="background-color:#282828;">
<code><span style="color:#fdf4c1;">sudo systemctl enable --now tailscaled
</span></code></pre>
<p>Then log into your Tailscale account:</p>
<pre style="background-color:#282828;">
<code><span style="color:#fdf4c1;">sudo tailscale up
</span></code></pre>
<blockquote>
<p>Tip: see <code>tailscale up --help</code> for additional configuration options.</p>
</blockquote>
<p>Since Tailscale's session will persist in a StandaloneVM, this is all of the
configuration you need to log into Tailscale.</p>
<h3 id="if-sys-tailnet-is-an-appvm">If <code>sys-tailnet</code> is an AppVM</h3>
<p>In order to start <code>tailscaled</code> at launch, you'll need to modify the
<code>/rw/config/rc.local</code> script within your <code>sys-tailnet</code>. This script is run in
any AppVM whenever it starts. We'll be using it to initialize Tailscale.</p>
<p>Run <code>sudoedit /rw/config/rc.local</code> inside <code>sys-tailnet</code> and add the following
line to the end of it:</p>
<pre style="background-color:#282828;">
<code><span style="color:#fdf4c1;">systemctl --no-block start tailscaled
</span></code></pre>
<p>Before we log into Tailscale, however, we need to tell QubesOS to persist the
Tailscale login data on reboot. In general terms, system data usually isn't
persisted between reboots of an AppVM. The <code>/var/lib/tailscale</code>directory must be
persisted for Tailscale to remain logged in.</p>
<p>QubesOS provides a mechanism for this called
<a href="https://www.qubes-os.org/doc/bind-dirs/">"bind-dirs"</a>. On AppVM boot, it bind
mounts directories in <code>/rw/bind-dirs</code>, which is persist across AppVM reboots, to
a corresponding location in the filesystem.</p>
<p>We can persist the Tailscale directory by adding a new configuration file to
<code>/rw/config/qubes-bind-dirs.d/</code> in our <code>sys-tailnet</code>. If it doesn't exist
already, create the directory by running the following inside <code>sys-tailnet</code>:</p>
<pre style="background-color:#282828;">
<code><span style="color:#fdf4c1;">sudo mkdir /rw/config/qubes-bind-dirs.d/
</span></code></pre>
<p>Then, run <code>sudoedit /rw/config/qubes-bind-dirs.d/50_user.conf</code> and add the
following contents to the file:</p>
<pre style="background-color:#282828;">
<code><span style="color:#fdf4c1;">binds</span><span style="color:#fe8019;">+=</span><span style="color:#fdf4c1aa;">( </span><span style="color:#b8bb26;">'/var/lib/tailscale'</span><span style="color:#fdf4c1aa;"> )
</span></code></pre>
<p>Finally, <code>bind-dirs</code> won't work correctly if the <code>/var/lib/tailscale</code> directory
doesn't exist in the template, so we need to manually create it:</p>
<pre style="background-color:#282828;">
<code><span style="color:#fdf4c1;">sudo mkdir -p /rw/bind-dirs/var/lib/tailscale
</span></code></pre>
<p>Once you've configured persistence for the <code>/var/lib/tailscale</code> directory,
reboot your <code>sys-tailnet</code> AppVM. From here, proceed as you normally would to
connect a Linux system to your tailnet:</p>
<pre style="background-color:#282828;">
<code><span style="color:#fdf4c1;">sudo tailscale up
</span></code></pre>
<blockquote>
<p>Tip: see <code>tailscale up --help</code> for additional configuration options.</p>
</blockquote>
<h1 id="dns">DNS</h1>
<p>Tailscale relies on DNS for <a href="https://tailscale.com/kb/1054/dns/">various
features</a>. On Linux, Tailscale modifies
<code>/etc/resolv.conf</code> to configure the system's DNS resolver. However, these
changes within your <code>sys-tailnet</code> do not apply to any AppVMs that use
<code>sys-tailnet</code> as their network qube.</p>
<p>In order to forward DNS queries to the Tailscale DNS server, we can forward any
incoming DNS requests in <code>sys-tailnet</code> coming from downstream AppVMs to the
Tailscale nameserver. For details on how Tailscale handles DNS traffic, the
article <a href="https://tailscale.com/blog/2021-09-private-dns-with-magicdns/">Private DNS with
MagicDNS</a> is a
good overview.</p>
<p>In <code>sys-tailnet</code>, add the following <code>iptables</code> commands to
<code>/rw/config/qubes-firewall-user-script</code>:</p>
<pre style="background-color:#282828;">
<code><span style="font-style:italic;color:#928374;">#!/bin/sh
# Based on https://forum.qubes-os.org/t/how-do-i-setup-a-custom-dns-in-appvm/5207
# This will Flush PR-QBS chain
</span><span style="color:#fdf4c1;">iptables -F PR-QBS -t nat
</span><span style="font-style:italic;color:#928374;"># Redirects all the DNS traffic to Tailscale DNS server
</span><span style="color:#fdf4c1;">iptables -t nat -I PR-QBS -i vif+ -p udp --dport 53 -j DNAT --to-destination 100.100.100.100
</span><span style="font-style:italic;color:#928374;"># Accepts the traffic coming to Tailscale DNS server from XEN's virtual interfaces on port 53
</span><span style="color:#fdf4c1;">iptables -I INPUT -i vif+ -p udp --dport 53 -d 100.100.100.100 -j ACCEPT
</span></code></pre>
<blockquote>
<p>This script will run whenever the AppVM starts, but to avoid restarting
<code>sys-tailnet</code>, you can manually trigger it by running <code>sudo /rw/config/qubes-firewall-user-script</code></p>
</blockquote>
<p>Tailscale's DNS server runs locally on your device and will forward any DNS
queries that don't belong to your tailnet to the upstream netvm of your
<code>sys-tailnet</code> qube, just like any other AppVM.</p>
<blockquote>
<p>Technically, your tailnet administrator can configure DNS settings, but these
only apply if you include the <code>--accept-dns</code> flag in your <code>tailscale up</code>
command.</p>
</blockquote>
<hr />
<p>That's it! Now you can start configuring your AppVMs access your tailnet while
maintaining the security benefits of QubesOS's compartmentalization.</p>
Password Security through Compartmentalization2022-03-20T00:00:00+00:002022-03-20T00:00:00+00:00/blog/password-security-through-compartmentalization/<p>Not all passwords are created equal. You can find evidence of this in the
countless people who use "layered" password schemes. While a some people just
re-use the same password on every website, others have an intuition for the
different trust levels between websites that they use.</p>
<p>Nobody needs absolute protection for every single account that they sign up for.
If you make an account to sign up for a newsletter, the impact of that account
getting breached is small. On the other hand, compromised "critical" ones like
email, Google, and bank accounts can cause catastrophic (and often
unrecoverable) damage to your financial, social, and professional identity.</p>
<p>There aren't many proxies for this in the offline world. Abysmal "security"
measures like Social Security numbers (in the U.S.; those who live in countries
with government-issued public/private keys can smugly skip this part) are
incredibly sensitive, given that they're basically master keys to our financial
lives. Each person generally only has a single SSN, meaning that every
institution who mandates it gets the same one.</p>
<p>People using the "layered" strategy online recognize different sensitivity
levels of accounts. Generally, they'll have one "high security" password used
for critical accounts, a "regular" password for things like social media and
other relatively trustworthy apps, and then a "throwaway" password for junk
accounts like retailers, newsletters, and the like.</p>
<p>My generation distrusts online companies and have shown through the above scheme
that they can recognize these different security levels. Unfortunately, today's
biggest password managers create single, all-or-nothing security vaults. I've
yet to see any password manager explore encouraging users to compartmentalize
their vaults cryptographically.</p>
<p>Compartmentalization enables a wide variety of additional use-cases and
potential features. When stop making "vaults" and start distinguishing
"passwords" and "devices," features like two-factor encryption, password
sharing, and compartmentalization become a natural extension of both the
cryptography and the user's mental model.</p>
<p>Now, to be fair, 1Password has <a href="https://support.1password.com/share-items/">built in support for sharing vault
entries</a> (sadly, I can't find too
much of the feature's clever "Psst!" branding anymore). Even if I think it's a
bit clunky, it covers a lot of compartmentalization's use cases.</p>
<p>In the <a href="https://1passwordstatic.com/files/security/1password-white-paper.pdf">Access control
enforcement</a>
section of their security whitepaper, 1Password explains that they have several
mechanisms for protecting shared passwords: cryptographic, server-side, and
client-side protections.</p>
<p><em>1Password warns that this section of their whitepaper is potentially
incomplete. I've done my best to verify that all of the below is accurate, but
if the information is incorrect or outdated, please <a href="mailto:me@nickzana.dev">contact
me</a>.</em></p>
<p>Say Alice, a 1Password user, wants to share a password with Bob, who doesn't use
1Password. Alice can generate a link to an encrypted <em>copy</em> of the password on
1Password's servers. The password is encrypted with a newly-generated key that
is then encoded into the link Alice is sharing. Alice needs to send this link to
Bob somehow, but once he has it, he can enter it into his web browser and view
the password.</p>
<p>If Alice would like, she can also use email-based access control that's enforced
by the 1Password server. When Bob opens the link, 1Password will send him an
email with a one-time code that he has to enter before he can access the
password.</p>
<p>This is a pretty good low-friction alternative to sharing passwords over text or
email. It provides some marginal benefit in terms of server-side controls. In
the end, though, it just shifts the sharing of the cryptographic material from
the password itself to the link. Unless Alice shares the link over an end-to-end
encrypted channel with Bob, the largest benefits provided by this method are the
server-side access controls for things like email verification, time expiration,
and one-time access limits.</p>
<p>It also comes with the downside of being inherently web-based. Yes, asking the
person you want to share a password with to download a specific piece of
software is unreasonable. But this weakens the security of this particular
feature if you want to use it for another purpose, such as using a password on a
public or otherwise untrusted computer.</p>
<p>Don't get me wrong, this is a good improvement. But for the case of sharing
a limited set of passwords with someone else - yourself - it doesn't quite stack
up.</p>
<p>On the other end of the spectrum, 1Password has many features that focus on
enterprise users, where compartmentalization, access control, and centralized
management are major focuses. <a href="https://1passwordstatic.com/files/security/1password-white-paper.pdf#chapter*.16">From their
whitepaper</a>:</p>
<blockquote>
<p>Alice is running a small company and would like to share the office Wi-Fi
password and the keypad code for the front door with the entire team. Alice
will create the items in the Everyone vault, using the 1Password client on her
device. These items will be encrypted with the vault key. When Alice adds Bob
to the Everyone vault, Alice's 1Password client will encrypt a copy of the
vault key with Bob's public key.</p>
</blockquote>
<blockquote>
<p>Bob will be notified that he has access to a new vault, and his client will
download the encrypted vault key, along with the encrypted vault items. Bob's
client can decrypt the vault key using Bob's private key, giving him access to
the items within the vault and allowing Bob to see the Wi-Fi password and the
front door keypad code.</p>
</blockquote>
<p>If a password manager could enforce per-device access cryptographically (likely
by giving each device its own keypair, to which entry and/or vault keys are
encrypted), losing your phone wouldn't mean a potential compromise of, say, your
bank password or SSN, so long as only more "trusted" devices have access to
these entries.</p>
<p>I could even imagine a service that allowed you to temporarily access passwords
from an untrusted device. Imagine if you had a relatively unimportant account.
Maybe it's a web-based game, or just information that's convenient to have in a
lot of places like your bookmarks. Downloading a client (or, more likely,
visiting a website, as terrible an idea as that is) and entering just a vault
password on any public computer could gain access to just these "low security"
entries in your vault. This vastly improves over the typical methods that solve
this problem, usually by either re-using passwords on these low-sensitivity
sites or logging into a password vault via the web.</p>
<p>Compartmentalization is one of the strongest defenses against exposing private
files to compromised environments. It can be mitigated if that environment only
has access to the minimum information that it needs. Given the potentially vast
differences in trust levels between our devices, it seems necessary that we
begin to reassess the threats we're designing our password managers around.</p>
Exploring the World of Password Managers2022-02-27T00:00:00+00:002022-02-27T00:00:00+00:00/blog/exploring-password-management/<p>Passwords are miserable to deal with. Bad passwords are both terrifyingly
tempting and incredibly common. Good passwords and hygiene all but require
complicated management schemes like password managers and a fair bit of
diligence. At the same time, passwords are used so frequently that it's most
practical to have access to all of your passwords on every device.</p>
<p>Further, password breaches are so commonplace that just about anyone who's used
the internet in the past 20 years has probably had one compromised. While
efforts to supplement and displace passwords as the standard form of
authentication are slowly gaining traction, it's inevitable that passwords are
going to remain a core part of digital security for the foreseeable future.</p>
<p>Fortunately, many of the pitfalls of passwords can be mitigated through
two-factor authentication, or <code>2FA</code>. Even so, it is frighteningly common for
companies to rely on <code>SMS</code> for <code>2FA</code>, or otherwise allow <code>2FA</code> to be bypassed
through social engineering, access to a phone number, or an email.</p>
<p>In this post, I'll be talking about the password managers I've used and
recommend. Then, I'll outline the properties a good password manager should
have. Finally, I'll introduce a project I've been working on related to this.</p>
<p>As a small disclaimer, I'm not a cryptographer. My "formal" training essentially
boils down to a few first-year intro to CS classes in I took this year in
college. So, while I'll be talking a lot about different password managers,
remember that the best password manager for you is the one that you fully
understand.</p>
<h2 id="you-should-just-use-a-password-manager">You Should Just Use a Password Manager</h2>
<p>For most threat models, this is a solved problem. Problems that are as important
yet difficult as password management have many pre-existing solutions for all
sorts of devices, security levels, and preferences.</p>
<p>My go-to recommendation for people is <a href="https://bitwarden.com/">Bitwarden</a>.
Bitwarden is an Open Source password manager with secure cloud sync, convenient
and appealing clients for nearly every platform, and a very generous free tier.
They have a track record of good performance in <a href="https://bitwarden.com/blog/third-party-security-audit/">third-party security
audits</a>, <a href="https://addons.mozilla.org/en-US/firefox/addon/bitwarden-password-manager/">a browser
extension</a>,
and <a href="https://bitwarden.com/help/what-encryption-is-used/">zero-knowledge cloud
sync</a> with standard,
reliable cryptography.</p>
<p>If you're more the self-hosting type, the <a href="https://github.com/bitwarden/server">official Bitwarden
server</a> is fully Open Source and available
as a Docker image. Alternatively, the popular and lightweight
<a href="https://github.com/dani-garcia/vaultwarden">vaultwarden</a> is a Bitwarden server
implementation (written in Rust!) that's compatible with the regular Bitwarden
client applications, making deployment for individuals, families, and small
teams really easy.</p>
<p>Don't like the cloud? <a href="https://keepassxc.org/">KeePassXC</a> is a Free Software
application that provides a local password database with various additional
encryption options such as keyfiles and a hardware token Challenge-Response.</p>
<p>While I don't necessarily recommend relying on them, there are even more
options if, for some reason, the above options don't work for you.
<a href="https://1password.com/">1Password</a> provides a pretty seamless experience,
especially within the Apple ecosystem. Your <a href="https://www.mozilla.org/en-US/firefox/features/password-manager/">browser probably has one built
in</a>, and
<a href="https://support.apple.com/en-us/HT211145">maybe even your Operating System,
too</a>. These options can be especially
useful if the user is only going to use a password manager if it's less
friction than whatever they were doing before. Any password manager is going to
be way better than trying to remember your passwords or, worse, re-use them.</p>
<h2 id="what-i-use">What I Use</h2>
<p>If I were a sensible person, I would still be using Bitwarden. However, I just
seem to really love making my computer harder to use for the fun of it. Once I
switched to <a href="https://www.qubes-os.org/">QubesOS</a> full time, I wanted to keep all
of my passwords in an offline virtual machine, which requires an offline
password manager. At this point, I switched to KeePassXC. It works very well,
and is probably the most sensible solution for most QubesOS users or those who
otherwise prefer an entirely local solution.</p>
<p>Recently, in an effort to eliminate as many GUI applications as possible from my
machine, I've been occasionally thinking about other offline password manager
options that worked from the terminal.</p>
<p>It wasn't until I happened to read a few blog posts by <a href="https://filippo.io/">Filippo
Valsorda</a> that the idea for a real alternative began to
form. Filippo, <a href="https://filippo.io/">"a cryptography and software engineer... in charge of
cryptography and security on the Go team at Google,"</a> is
the author of <a href="https://github.com/FiloSottile/age"><code>age</code></a>, a file encryption
tool.</p>
<p>What does this have to do with password managers? At the core of every password
manager (or, at least, any password manager that's worthwhile) is some
cryptographic algorithm to take your passwords and obfuscate them so that only
you can access them. For example, Bitwarden encrypts your passwords with
<a href="https://bitwarden.com/help/what-encryption-is-used/"><code>AES-CBC</code>, using <code>PBKDF2</code> to derive the encryption key from your master
password</a>. You can see this
in action with Bitwarden's <a href="https://bitwarden.com/crypto">interactive crypto
page</a>, which I think is pretty cool.</p>
<p><a href="https://www.passwordstore.org/"><code>pass</code></a> is another password manager that
describes itself as <a href="https://www.passwordstore.org/">"the standard unix password
manager"</a>. <code>pass</code> is actually a very clever
script run through the command line that stores passwords at <code>~/.password-store</code>
as a tree of normal directories and files.</p>
<p>For example, from the <a href="https://www.passwordstore.org/"><code>pass</code></a> website, the
directory could look something like this:</p>
<pre style="background-color:#282828;">
<code><span style="color:#fdf4c1aa;">Password Store
├── Business
│ ├── some-silly-business-site.com
│ └── another-business-site.net
├── Email
│ ├── donenfeld.com
│ └── zx2c4.com
└── France
├── bank
├── freebox
└── mobilephone
</span></code></pre>
<p>Each of those files contains a <a href="https://www.gnupg.org/"><code>gpg</code></a>-encrypted
password for the website. They're accessed by using the <code>pass</code> command to
decrypt the files with a <code>gpg</code> private key. However, I strongly dislike the idea
of relying on <code>gpg</code> to protect my passwords. While I do have a PGP key, I avoid
using it whenever there's a more suitable alternative available. <a href="https://latacora.micro.blog/2019/07/16/the-pgp-problem.html">Many
cryptographers have written about the issues with
PGP</a>, but the
biggest concern for me is the number of foot-guns with the PGP protocol and
<code>gpg</code> tool. Even in a standard configuration like <code>pass</code>, where I'm offloading
calling <code>gpg</code> to the script, the system is just too complicated for me to be
confident in my understanding of it.</p>
<p>By contrast, <code>age</code> touts its opinionated nature as a feature. The lack of
configuration, secure-by-design specification, and simplicity make it, in my
opinion, the perfect encryption tool to be used with <code>pass</code>.</p>
<h2 id="properties-of-a-good-password-manager">Properties of a Good Password Manager</h2>
<h3 id="confidentiality">Confidentiality</h3>
<p>The most obviously-essential property of a password manager is to make sure
that nobody can read its contents without the keys. There are several types of
adversary a password manager needs to protect against, but it primarily boils
down to protecting its passwords even if someone gains access to your encrypted
password database.</p>
<p>You should be able to post your encrypted database on your blog or send it to
Mark Zuckerberg himself without a concern in the world. In other words, the
security properties of your database should be completely upheld by
cryptography, and not rely on more error-prone protections like storing it
offline or keeping it in a private cloud account.</p>
<h3 id="reliability">Reliability</h3>
<p>Have you ever been unable to login to an account when you really, really needed
to? Yeah, I have. Wasn't a fun time. Passwords are so important that even the
possibility of downtime is a serious concern. Even if you can eventually recover
the data, being unable to decrypt your password database at a vital moment can
is stressful, humiliating, and potentially disastrous.</p>
<p>A good password manager works when you don't have internet, when your laptop
runs out of battery, when you're traveling, and when all of those are happening
at once but you have a flight to catch in an hour. You should be following (at
bare minimum) a standard <a href="https://www.backblaze.com/blog/the-3-2-1-backup-strategy/">3-2-1 backup
strategy</a> -- and your
password manager should make that as easy as possible.</p>
<h3 id="usability">Usability</h3>
<p>Cryptography that's hard to use is worse than no cryptography at all; just ask
PGP.</p>
<p>If reaching for your password manager isn't the lowest-friction way of signing
up for an account, you're going to mess up and re-use a password, or make one up
and forget it. Convenience is really key here. You can have the most secure
password manager in the world, but if you can't access it on your phone, or
another computer, you'll quickly email or text a password to yourself to log
into that account you really, really need right now.</p>
<p>On the other hand, it also has to be extra resistant to misuse. All it takes is
saving a password unencrypted once, or accidentally typing your Master Password
into an innocuous pop-up you didn't notice, to completely compromise yourself.</p>
<h3 id="resiliency">Resiliency</h3>
<p>You aren't the only one who needs access to your accounts. Should something
happen to you, or some other unforeseen situation come up, there needs to be a
"break glass in case of emergency" option -- without compromising on the
password manager's fundamental security properties. With password managers, this
often comes in the form of some adaptation of "Emergency Access." Essentially,
the idea is to provide a trusted party or parties with enough information to,
individually or collectively, gain access to some or all of your encrypted data.</p>
<p>Bitwarden has the concept of "Trusted Emergency Contacts" in their <a href="https://bitwarden.com/help/emergency-access/">"Emergency
Access"</a> system. Essentially, it
works by providing Bitwarden with the secret key material necessary to decrypt
your vault encrypted to the trusted contact's public key. That contact can then
send Bitwarden a "request for emergency access," after which, following a
amount of time you specify, they are sent the encrypted secret key material.
They than have access to view your entire vault.</p>
<p>1Password, on the other hand, lets you handle distribution of your secret key
material. They provide something they call an <a href="https://support.1password.com/emergency-kit/">"Emergency
Kit"</a>, which essentially is a PDF
containing your email, secret-key material, and a space to write out your Master
Password, along with instructions to begin the recovery. Unlike Bitwarden, who
acts as an escrow for your encrypted recovery data, 1Password lets you manually
distribute your recovery material. Notably, the trusted party does not need to
have a 1Password account, as the recovery material is a PDF that can be printed.
Like Bitwarden, however, once the trusted party receives the Emergency Kit, they
can then recover all of the material stored in your account.</p>
<p>An escrowed solution like Bitwarden is not ideal, as they potentially have the
power to withhold your secret key material (although I strongly doubt they would
ever do so unless somehow legally compelled to). The one benefit this has is
that you retain the power to prevent the secret key material from being sent to
them in the event that they try to abuse it. 1Password's PDF with a hand-written
master password seems like a better solution in terms of resiliency, but is
theoretically more susceptible to abuse. Ultimately, which of these two
solutions is better comes down to how much you trust your cloud provider
compared to how much you trust your emergency contacts.</p>
<p>One third way to solve this is to require multiple trusted parties to
collectively agree to access your password manager. Changing the number and
trustworthiness of those parties can help you adjust that ratio of "resiliency
against loss" to "abuse by trusted parties."</p>
<h2 id="can-we-do-better">Can we do better?</h2>
<p>Rather than sticking with established options, I've decided to start working on
my own password manager, <a href="/projects/ciphey"><code>ciphey</code></a>. The goal is to explore
what cryptography exists out there that can meet the properties listed above in
a simple and understandable manner.</p>
<p>The project is still in its exploratory stages, so any feedback is welcome!</p>
Setting up IRC with soju and senpai2022-02-23T00:00:00+00:002022-02-23T00:00:00+00:00/blog/irc-soju-senpai/<p>Despite the rapid movement of many Free Software project communities to
proprietary platforms like Slack and Discord, many communities continue to live
on IRC. Notably, <a href="https://www.torproject.org/contact">The Tor Project</a> has
<a href="https://www.torproject.org/contact/">several channels</a> on
<a href="https://www.oftc.net/">OFTC</a>. While many of these projects also have bridges to
Matrix channels, I don't use Matrix, and there are many communities who continue
to live only on IRC.</p>
<p>Thus, I wanted to set up a simple IRC bouncer and client combination. While I've
used projects like <a href="https://thelounge.chat/">TheLounge</a> in the past, I wanted an
IRC experience that could exist without a browser.</p>
<p><a href="https://sr.ht">SourceHut</a> is a project that I've been interested in for a
while. They provide git hosting like GitHub or GitLab, but avoid locking in
projects and communities with proprietary platform-specific features. They make
heavy use of mailing lists, all of their software is completely Free, and,
perhaps most importantly, the project is refreshingly human. In short, they
value many of the things that make Free Software more than just Open Source.</p>
<p>Projects that are independent, make entirely Free Software, and design systems
to protect user privacy are often difficult to fund, which is why SourceHut is
so exciting to watch. They seem to have a relatively <a href="https://sourcehut.org/blog/2022-01-09-how-does-our-business-work/">steady
flow</a> of
income, and I'm optimistic that their model will prove itself, paving the way
for more Free Software projects to become sustainable.</p>
<p>While not directly related to git hosting, they also operate an <a href="https://en.wikipedia.org/wiki/BNC_(software)#IRC">IRC
bouncer</a>. In short, an IRC
bouncer maintains a connection to IRC networks and collects messages for you.
You can then connect to the bouncer to read those messages and send messages in
the channels that you've joined.</p>
<p>This has many benefits over operating a client on your own machine. For me,
however, the two biggest advantages were the ability to register and connect
over the Tor network and financially support SourceHut. Most IRC networks fight
spam by blocking registration over Tor. By contrast, SourceHut is happy to
accept registration without any JavaScript; all it needed was a username, email
address, and password. While the <a href="https://chat.sr.ht">chat.sr.ht</a> project
requires a paid account, this was a perfectly acceptable compromise for me, as
anonymity was never my goal.</p>
<p><a href="https://soju.im/"><code>soju</code></a>, a project by SourceHut, is the software underlying
the <a href="https://chat.sr.ht">chat.sr.ht</a> bouncer. It's written in <code>Go</code>, financially
supported by SourceHut, and seems relatively simple to interface with.</p>
<p>Normally, I would self-host a service like this. Actually, for a while, that's
what I did. What compelled me to try the hosted solution was the inherently
public nature of how I plan to use IRC. I won't be using IRC for one-to-one
communication, or even within small groups. That's what messaging platforms like
Signal are for. I intend to use IRC for interacting with public communities,
where all of my messages can (and should) be readable by everyone.</p>
<h3 id="sourcehut-account-setup">SourceHut Account Setup</h3>
<p>As mentioned previously, SourceHut's hosted
<a href="https://chat.sr.ht">https://chat.sr.ht</a> instance of <code>soju</code> requires a paid
account. This seems fairly reasonable, especially given the very generous and
flexible <a href="https://sourcehut.org/pricing/">pricing</a>.</p>
<p><img src="/blog/irc-soju-senpai/signup.png" alt="signup" /></p>
<p>Registration is incredibly simple. All I needed to do was provide a username,
email address, and password. They even let me include a PGP key to both encrypt
emails sent from their service and be presented on my profile.</p>
<p>Upgrading to a paid account was just as simple. Heading to my <a href="https://meta.sr.ht/profile">profile
page</a>, I clicked on billing:</p>
<p><img src="/blog/irc-soju-senpai/profile.png" alt="profile" /></p>
<p>The billing page provided clear information on who needed to sign up for a paid
account and transparency into the payment processor. I particularly appreciate
the notice that non-free JavaScript would be required for payment, along with an
invitation to find an alternative for those who wish to avoid using Stripe.</p>
<p><img src="/blog/irc-soju-senpai/billing.png" alt="billing" /></p>
<h3 id="senpai-client-setup">senpai client setup</h3>
<p>For my IRC client, I've chosen to go with
<a href="https://sr.ht/%7Etaiite/senpai/"><code>senpai</code></a>. While there are a <a href="https://git.sr.ht/%7Eemersion/soju/tree/master/item/contrib/clients.md">few other
clients</a>
listed in the <code>soju</code> docs, <code>senpai</code> is, like <code>soju</code>, written in <code>Go</code>, and
explicitly designed to work well with the bouncer. Documentation for connecting
clients, including <code>senpai</code>, to <code>chat.sr.ht</code> can be found <a href="https://man.sr.ht/chat.sr.ht/quickstart.md">on the official
quickstart guide</a>.</p>
<p>First thing's first, we need to create a Personal Access Token for our chat. A
Personal Access Token gives SourceHut API access to your account for third-party
applications and scripts. This is done very simply in SourceHut by returning to
the profile page and selecting <a href="https://meta.sr.ht/oauth"><code>oauth</code></a>.</p>
<p><img src="/blog/irc-soju-senpai/oauth.png" alt="oauth" /></p>
<p>Fortunately, <code>senpai</code> supports SourceHut's experimental <code>OAuth 2.0</code> API.
Proceeding to the <a href="https://meta.sr.ht/oauth2">OAuth 2.0 Dashboard</a>:</p>
<p><img src="/blog/irc-soju-senpai/oauth2.png" alt="oauth2" /></p>
<p>We can generate a new token. Thanks to the experimental API, we can limit the
permissions of this token so that it doesn't have access to other SourceHut
services, like git hosting or profile management. As of writing, the permission
that it needs is the <code>meta.sr.ht/PROFILE:RO</code> permission. This seems like a
strange permission for access to the <code>chat.sr.ht</code> service, so I'd venture to
guess that this permission will change as the <code>chat.sr.ht</code> service improves, or
the <code>OAuth 2.0</code> API comes out of beta. I strongly recommend checking the
<a href="https://man.sr.ht/chat.sr.ht/quickstart.md">official quick start guide</a> to see
if this is still the case. As of writing, the direct link to generate a new
token is
<a href="https://meta.sr.ht/oauth2/personal-token?grants=meta.sr.ht/PROFILE:RO">https://meta.sr.ht/oauth2/personal-token?grants=meta.sr.ht/PROFILE:RO</a>:</p>
<p><img src="/blog/irc-soju-senpai/oauth2-direct.png" alt="oauth2-direct.png" /></p>
<p>Finally, we need to install and configure the <code>senpai</code> client. You can install
it from the <a href="https://git.sr.ht/%7Etaiite/senpai">official SourceHut repository</a>.
Your system's software repository may have a pre-existing package for it.
Otherwise, it can be installed relatively simply from source by cloning the
repository and calling <code>go install ./cmd/senpai</code>. Make sure that your <code>$GOBIN</code>
is in your <code>$PATH</code>.</p>
<p>The configuration file for <code>senpai</code> is located at
<code>~/.config/senpai/senpai.scfg</code>. To use it with <code>chat.sr.ht</code>, it should look
something like this:</p>
<pre style="background-color:#282828;">
<code><span style="color:#fdf4c1aa;">nickname your_irc_nickname
address chat.sr.ht
username your_sourcehut_username
password "your_sourcehut_personal_access_token"
</span></code></pre>
<p>Note the quotation marks around your password, those are required. More
configuration options and examples can be found on the <a href="https://git.sr.ht/%7Etaiite/senpai/tree/master/item/doc/senpai.5.scd"><code>senpai</code> man
page</a>. The
default settings send typing indicators, so I also disabled those with <code>typings false</code>.</p>
<p>You can finally launch <code>senpai</code> with the <code>senpai</code> command. Type <code>/HELP</code> to get
usage instructions for <code>senpai</code>, or view <a href="https://git.sr.ht/%7Etaiite/senpai/tree/master/item/doc/senpai.1.scd">the man
page</a>. Below
is an example of adding <code>irc.libera.chat</code> as a network. I switch to the
automatically-created <code>BUFFER 1</code>, which is an IRC chat with the <code>chat.sr.ht</code>
<code>BouncerServ</code> interface, which allows you to configure your bouncer. This is
essentially an interface to configure your <code>soju</code> user. Details can, of course,
be found by looking at the <code>soju</code> <a href="https://soju.im/doc/soju.1.html#IRC_SERVICE">man
page</a>.</p>
<pre style="background-color:#282828;">
<code><span style="color:#fdf4c1aa;">> /MSG BouncerServ help
> /BUFFER 1
> network create -addr ircs://irc.libera.chat:6697 -name libera -nick your_irc_nickname
BouncerServ created network "libera"
</span></code></pre>
<p>Great! Now, I'll check the status of the network:</p>
<pre style="background-color:#282828;">
<code><span style="color:#fdf4c1aa;">nickzana network status
BouncerServ libera (ircs://irc.libera.chat:6697) [connected as your_irc_nickname]: 0 channels
</span></code></pre>
<p>Unfortunately, I haven't figured out how to open a buffer to a network without
quitting and re-launching <code>senpai</code>. It seems like some of the bootstrapping is
undocumented without the web UI, or I'm just misunderstanding the <code>senpai</code> man
page. Regardless, after relaunching, I can switch to the newly-created <code>libera</code>
buffer.</p>
<pre style="background-color:#282828;">
<code><span style="color:#fdf4c1aa;">/BUFFER 1
</span></code></pre>
<p>To secure my nickname, I'll generate a <code>CertFP</code> self-signed certificate for the
<code>libera.chat</code> network. Details on CertFP and how to use it can be found on the
<a href="https://libera.chat/guides/certfp">libera.chat guide</a>.</p>
<pre style="background-color:#282828;">
<code><span style="color:#fdf4c1aa;">/MSG BouncerServ help
/BUFFER 1
</span></code></pre><pre style="background-color:#282828;">
<code><span style="color:#fdf4c1aa;">nickzana certfp generate -key-type ed25519 -network libera
BouncerServ certificated generated
BouncerServ SHA-1 fingerprint: ...
BouncerServ SHA-256 fingerprint: ...
BouncerServ SHA-512 fingerprint: ...
</span></code></pre>
<p>Then, I'll register my nickname (in the <code>libera.chat</code> buffer):</p>
<pre style="background-color:#282828;">
<code><span style="color:#fdf4c1aa;">/msg NickServ REGISTER your_password your_email@example.com
</span></code></pre>
<p>Don't forget to verify your email!</p>
<p>And, per the <a href="https://libera.chat/guides/certfp">libera.chat guide</a>, register my
certificate fingerprint with my account:</p>
<pre style="background-color:#282828;">
<code><span style="color:#fdf4c1aa;">/msg NickServ CERT ADD your_sha512_fingerprint
NickServ: Added fingerprint ... to your fingerprint list.
</span></code></pre>
<p>Finally, I'll join a channel in the <code>libera</code> buffer:</p>
<pre style="background-color:#282828;">
<code><span style="color:#fdf4c1aa;">/join #sr.ht
</span></code></pre>
<p>This shows up as a new buffer in <code>senpai</code>.</p>
<h2 id="closing-thoughts">Closing Thoughts</h2>
<p>This process ended up being a bit more difficult than I had initially thought it
would be. Most of the issues would've been solved by just using the <code>chat.sr.ht</code>
web client. However, <code>senpai</code> is <a href="https://git.sr.ht/%7Etaiite/senpai/commit/881d63465cdb17357438763d4d2996d5f1d92fcd">less than two years
old</a>,
so there's plenty of room to improve. Maybe I'll try another client sometime
soon. Overall, though, I'm quite happy with how it turned out!</p>
Adding FreshRSS to NetNewsWire2021-04-26T00:00:00+00:002021-04-26T00:00:00+00:00/blog/freshrss-netnewswire/<p>A pain point that I have always had with RSS is syncing my feeds and read
status between devices. There are various solutions out there;
<a href="http://www.abitrss.com/">TinyRSS</a>, <a href="https://www.feedly.com/">Feedly</a>,
<a href="https://alternativeto.net/software/the-old-reader/about/">TheOldReader</a>...
the <a href="https://alternativeto.net/software/google-reader/">list goes on</a>.</p>
<p>The fact that I couldn't sync my constantly-changing feeds and content read statuses between my devices and <a href="https://www.qubes-os.org/">virtual machines</a> made me use RSS less and less. It wasn't that RSS was any less useful. It just became too inconvenient compared to directly visiting a small handful of centralized websites like HackerNews, Reddit, and YouTube, directly leading to me losing track of smaller and independent creators. I am slightly ashamed of this; as someone in favor of the open and decentralized web, I gave in to the invasive and harmful platforms.</p>
<p>As of today, that is no more. I have moved back to using RSS, and I am all the more happier for it.</p>
<p>What I really want is a system that works like so:</p>
<ul>
<li>A centralized server that manages feeds and read/unread statuses and exposes an API</li>
<li>A native application to access said API on Linux, iOS, Android, and macOS</li>
<li>A web application to manage feeds and read articles from anywhere if needed</li>
<li>Self-hostable and lacks any trackers, analytics, ads, etc.</li>
<li>Works completely with FLOSS</li>
</ul>
<h3 id="my-solution">My Solution</h3>
<p>I came upon this when, earlier today, my <a href="https://ersei.saggis.com">friend</a> sent me a <a href="https://nnw.ranchero.com/2021/04/11/how-to-help.html">link</a> to the NetNewsWire TestFlight beta. Why? Turns out, <a href="https://nnw.ranchero.com/2021/03/27/netnewswire-for-mac.html">NetNewsWire 6.0</a> is adding support for <a href="https://www.freshrss.org/">FreshRSS</a>. As far as I'm aware, this is the first iOS application to support FreshRSS. Several months back, when I attempted to meet the above requirements, I could not get a working docker container to expose an API to clients that supported the second and third requirements. However, upon seeing the above news, I gave FreshRSS another hard look.</p>
<p>An hour of searching and a few config files later, I ended up with the following setup:</p>
<ul>
<li><a href="https://www.freshrss.org/">FreshRSS</a>, a self-hosted FLOSS application that manages feeds and read/unread status running in a <a href="https://docs.linuxserver.io/images/docker-freshrss">Docker container</a> using <code>docker-compose</code>.</li>
<li>NetNewsWire 6.0 TestFlight Beta on my iOS device acting as a client for FreshRSS</li>
<li><a href="https://jangernert.github.io/FeedReader/">FeedReader</a> on my Desktop</li>
</ul>
<p>This doesn't seem like much, but it has completely centralized my content consumption. Don't get my wrong, it's not perfect. FeedReader opens links in its native browser instead of my system browser, and I'm yet to find a way to avoid that without simply copying the URLs. FreshRSS is written in PHP, which my strongly-typed Rust-loving self finds unsettling. I must expose my FreshRSS instance to the internet, as, in order to allow clients to access it, I can't put it behind Authelia until I successfully manage to get my own VPN set up in the way I want. However, these are minor issues given that I finally have a centralized location for all of my YouTube, Reddit, Twitter, PeerTube, Mastadon, podcast, and blog content.</p>
<h3 id="guide">Guide</h3>
<p>I ran into a few nags along the way, so here's a guide to how I set it up:</p>
<pre style="background-color:#282828;">
<code><span style="color:#8ec07c;">---
</span><span style="font-weight:bold;color:#8ec07c;">version</span><span style="color:#fdf4c1aa;">: </span><span style="color:#b8bb26;">"2.1"
</span><span style="font-weight:bold;color:#8ec07c;">services</span><span style="color:#fdf4c1aa;">:
</span><span style="font-weight:bold;color:#8ec07c;">freshrss</span><span style="color:#fdf4c1aa;">:
</span><span style="font-weight:bold;color:#8ec07c;">image</span><span style="color:#fdf4c1aa;">: </span><span style="font-weight:bold;color:#8ec07c;">ghcr.io/linuxserver/freshrss
container_name</span><span style="color:#fdf4c1aa;">: </span><span style="font-weight:bold;color:#8ec07c;">freshrss
environment</span><span style="color:#fdf4c1aa;">:
- </span><span style="font-weight:bold;color:#8ec07c;">PUID=$PUID
</span><span style="color:#fdf4c1aa;">- </span><span style="font-weight:bold;color:#8ec07c;">PGID=$PGID
</span><span style="color:#fdf4c1aa;">- </span><span style="font-weight:bold;color:#8ec07c;">TZ=$TZ
volumes</span><span style="color:#fdf4c1aa;">:
- </span><span style="font-weight:bold;color:#8ec07c;">$FreshRSS_NAS:/config
ports</span><span style="color:#fdf4c1aa;">:
- </span><span style="font-weight:bold;color:#8ec07c;">8180:80
restart</span><span style="color:#fdf4c1aa;">: </span><span style="color:#b8bb26;">unless-stopped
</span></code></pre>
<p>After adding on some Traefik labels, I was a simple <code>docker-compose up -d</code> away from a full FreshRSS instance.</p>
<p>The setup was mostly simple. The only tricky thing on the server side was ensuring the API was accessible. This can be done by going to the <code>Authentication</code> settings, then clicking <code>Allow API access (required for mobile apps)</code>, followed by <code>Submit</code>.</p>
<p><img src="/blog/freshrss-netnewswire/FreshRSS-Authentication.png" alt="FreshRSS-Authentication" /></p>
<p><img src="./FreshRSS-API-Access.png" alt="FreshRSS-API-Access" /></p>
<p>FeedReader setup was also quite simple. Upon launching the application, I selected "freshRSS" for the location of my feeds:</p>
<p><img src="./FeedReader-Feeds.png" alt="FeedReader-Feeds" /></p>
<p>Entering my <code>URL</code>, <code>username</code>, and <code>password</code> worked exactly as expected.</p>
<p><img src="./FreshRSS-FeedReader-Login.png" alt="FreshRSS-FeedReader-Login" /></p>
<h3 id="netnewswire">NetNewsWire</h3>
<p>NetNewsWire, probably due to its current beta status, had a slightly more frustrating onboarding process. By going to <code>Settings -> Add Account</code> in the app, FreshRSS was kindly placed in its own self-hosted section.</p>
<p><img src="./NetNewsWire-FreshRSS.jpeg" alt="NetNewsWire-FreshRSS" /></p>
<p>The <code>username</code> and <code>password</code> fields worked as expected. However, the <code>URL</code> field required the full address (<code>https://freshrss.nickzana.dev/api/greader.php</code>).</p>
<p><img src="./NetNewsWire-FreshRSS-Login.jpeg" alt="NetNewsWire-FreshRSS-Login" /></p>
<p>That's it! While NetNewsWire also has a macOS client, the only platform I haven't tested is Android. However, <a href="https://f-droid.org/en/packages/fr.chenry.android.freshrss/">this</a> app on F-Droid looks like it would work perfectly.</p>