Not your keys, not your coins.
In the crypto world, this is an essential truth. Even if you’re not an expert on private-public key cryptography, you intuitively understand that allowing someone else to control your assets is a losing move.
Communication is the same. Not your keys, not your contacts!
Fortunately, there are communication protocols that provide strong encryption. The most well-known is probably Signal, which acts as a drop-in replacement for your phone SMS app. Long story short, if you’re using plain SMS to talk with friends and family, please consider switching to Signal.
There are some downsides to a service like Signal, however. The primary risk is that Signal is highly centralized. Because the Signal servers are a central point of failure, they can be attacked directly (via DDoS) or indirectly (via supply chain attack, corporate espionage, or a legal attack).
That said, I still think Signal is an excellent choice. But please be aware that using any centralized service creates a certain security-convenience tradeoff.
For one-to-one communication, Signal and other encrypted messengers work nicely.
But what about group communication? Here the security-convenience tradeoff gets even more out of whack. It’s hard enough convincing your friends and family to join you on a new platform. Imagine trying to get ten people together on a new platform. Never going to happen!
The crypto community in general, and the Bowtied Jungle in specific, seems to have congregated around Discord as a means to facilitate easy group communication.
That’s fine for random shitposting, but I caution anyone who cares about privacy to look elsewhere.
Telegram has some good reputation for being a good encrypted application, but there are huge red flags. First, Telegram is not configured to use end-to-end encryption by default for messages. Second, there is no option for end-to-end encryption for group chats. Third, the desktop client offers no encryption at all!
Telegram is a mess, so stay off it for anything secure.
Wait until GripToe Tweeter learns that every single phone number and every single photo will be scraped on every single crypto chat. YES THIS MEANS DISCORD!— BowTiedBull - Read Pinned Tweet or NGMI (@BowTiedBull) August 24, 2021
Surely they are all compliant.
You were warned.
Recently, our friendly neighborhood Bull reminded us that shady things were going on in Discords. All data from those Discords could and would be consumed and analyzed for legal compliance later. I don’t know much about regulations surrounding cryptocurrency pay-for-promotion (PFP) schemes, but I can spot a privacy-security risk from a mile away.
I used the term “end-to-end encryption” above, but did not define it. It’s a semi-common term that people likely do not understand. In essence, an end-to-end encryption protocol ensures that data is encrypted by the sender at the point of submission (on a browser, in an app, or at the point of physical entry like a microphone or keyboard), and it remains encrypted all along its transport path to the point of reception. Only when it reaches the ultimate destination can the message be decrypted.
This is usually accomplished with private-public key cryptography. A private key is used to generate a secure encrypted message, then sent to a recipient who uses an associated public key to decrypt that data. Since the public key is derived from the private key, the authenticity of a privately-signed message can be verified with the public key. It is simple to go from private-to-public, but very computationally expensive to go from public-to-private.
This computational asymmetry is what gives cryptography its inherent security.
I often tell people to “decentralize”, which can mean a lot of different things. I typically use it as shorthand for “stop relying on a centralized single point of failure for critical parts of your life”. Not so easy to fit into a tweet.
One way to decentralize is to choose a service that, by design, cannot be centralized. The concept of Federation is not new in the software world, but the term is somewhat recent. A centralized service must, by definition, rely on some coordinated group or platform to operate. A federated service, as a counter-example, is designed such that peer-to-peer networks are established and maintained. A particular portion of a federated network can go offline and the rest will continue operating.
You already use a federated system every day — Email!
Email relies on a standard set of protocols to send, receive, and route email messages between thousands of different servers each serving thousands of different users.
It’s no problem to see an email address
email@example.com and understand that our user
bob has an account with
example.com. You can send and receive email with Bob from any other service without trouble. In fact, you can even build an email service of your very own (two parts: sending & receiving.)
Did you know that Bitcoin is a federated network?
They are very powerful, and very hard to shut down.
Food for thought.
Enter Matrix. Their website described Matrix as an open source project that publishes the Matrix open standard for secure, decentralised, real-time communication…
Hey, that sounds pretty good!
It gets even better when you learn that Matrix is federated and offers end-to-end encryption by default. And, even better, Matrix is itself an open protocol and not a product. They offer a reference implementation of their server, but anyone is free to write a compatible server application.
A few more bonuses:
- No phone number necessary to register!
- Built-in real-time voice & video chat (WebRTC).
- Bridging capabilities are included, which allows you to send & receive messages through Matrix to other open protocols (Signal, IRC, Skype, Slack, Discord, iMessage, Telegram, Instagram, etc). Read about bridges HERE.
- APIs to create chat bots and helper programs.
- Simple seed-based export for private keys.
The killer feature for me is the ability to easily host a homeserver for my own use, and to use it to communicate securely with any other Matrix user, regardless of their location. 100 different users on their own homeservers can chat in a common area as easily as if they were all on the same server.
I dig it, and I think you will too.
If you’re interested in the extra security of running your own homeserver, read on.
Matrix provides a Docker image of their reference server implementation called Synapse. I use a lot of Docker already, so it’s trivially easy to add this to the stack and secure it behind my Traefik reverse proxy.
My instructions below will assume a special directory
/docker/synapse has been created to store relevant files.
The first task is to generate a pre-configured data volume and
linodevm:~$ sudo su - linodevm:~# cd /docker/synapse linodevm:/docker/synapse# docker run -it --rm --mount type=volume,src=synapse_data,dst=/data -e SYNAPSE_SERVER_NAME=bowtieddevil.com -e SYNAPSE_REPORT_STATS=no matrixdotorg/synapse:latest generate
This will generate a
homeserver.yaml file inside the
synapse_data volume. On most systems, this will be stored in
homeserver.yaml file using
vim, paying particular attention to these variables:
server_namewhich I’ve set to
public_baseurlwhich I’ve set to
allow_public_rooms_over_federationwhich I’ve set to
true, allowing users from other servers to view the public rooms on my homeserver.
Comment out the
sqlitedatabase section, replacing it with these values for PostgreSQL:
Edit the following
server_name is set to
bowtieddevil.com (the top level domain) and the
public_baseurl references the sub-domain
matrix.bowtieddevil.com. This is odd looking, but it allows me to specify my username as
firstname.lastname@example.org (similar to an email address) without including the
matrix sub-domain. Overall it looks cleaner, but requires an extra step to ensure proper federation.
When that’s done, create a
docker-compose.yml file with the following contents:
version: "2" services: db: image: postgres:13 container_name: synapse-db restart: unless-stopped volumes: - db:/var/lib/postgresql/data environment: - POSTGRES_PASSWORD=synapsePass - POSTGRES_USER=synapseUser - POSTGRES_DB=synapseDB - POSTGRES_INITDB_ARGS="--encoding='UTF8'" - LANG=C synapse: image: matrixdotorg/synapse:latest container_name: synapse restart: unless-stopped volumes: - data:/data labels: - traefik.enable=true - traefik.docker.network=synapse - traefik.http.routers.synapse.entrypoints=websecure - traefik.http.routers.synapse.rule=Host(`matrix.bowtieddevil.com`) || (Host(`bowtieddevil.com`) && PathPrefix(`/_matrix/`)) - traefik.http.services.synapse.loadbalancer.server.port=8008 volumes: data: db: networks: default: name: synapse
Note that this
docker-compose.yml assumes you have a Traefik reverse proxy already running. If so, be sure to add the
synapse network to its networks using
docker network connect synapse traefik (zero downtime) and making it permanent by adding it to the
networks block in Traefik’s
Note: change all instances of
bowtieddevil.com to your domain name!
Then bring the server up with
docker-compose up to watch the logs. If all looks well, stop the stack with
c and re-run with
docker-compose up -d.
Create a Local User
By default, user registration is disabled from the client side, so you need to create a user from the command line. This is easy to do, simply run the following:
linodevm:/docker/synapse# docker exec -it synapse register_new_matrix_user http://localhost:8008 -c /data/homeserver.yaml
This will prompt you for a username and password, and ask if you want to make that user an admin. I’m not sure what effect the admin choice has, but I answered yes.
Now you should have a working Matrix server and a user, so visit
https://matrix.bowtieddevil.com (or your equivalent) to see if the server is operational.
Federation and DNS Records
To properly connect to the federated Matrix network, Synapse needs to be reachable in one of two ways:
- On port 8448 using the hostname specified in
- On another port or hostname specified in
.well-known/matrix/serveror in a
Since I prefer to let Traefik manage all secured traffic, and did not want to open a new port or write new proxy rules, I chose to federate using the hostnames I specified above. Adding a
SRV DNS record is easier than managing a
.well-known rule, so I went that route.
Using the Linode Domains Control Panel, I added two DNS records:
matrixwith my IP addresses.
SRVrecord with the following values:
- Service: matrix
- Protocol: tcp
- Priority: 10
- Weight: 5
- Port: 443
- Target: matrix.bowtieddevil.com
- TTL: Default
Then, test for proper federation using the tester HERE.
If you see SUCCESS for all Connections, you’re all set.
The interesting thing about Matrix is that servers and clients are completely separate. You can log into your homeserver from any client (I recommend Element, including a client hosted by someone else! If you prefer to not install anything yet, you can use the Element web application at app.element.io.
By default, Element is set to use the official matrix.org homeserver, but you can click Edit and change it to
bowtieddevil.com to authenticate with the account you’ve just created at the command line.
If everything worked correctly, you’ll see a screen like this:
If you want to send me an end-to-end encrypted message, start a private chat with @bowtieddevil:bowtieddevil.com.
You can now create rooms or join chats specific to this server, invite other users to join your rooms, or join rooms on other servers. For fun, I have created a public room on my server called “Jungle Meet ’n Greet”, which you can join by visiting https://matrix.to/#/#jungle-meet-n-greet:bowtieddevil.com or by adding my server (
bowtieddevil.com) to your list of Matrix servers.
Pretty good stuff. I hope you’ll send me some messages soon!