Secure Communications with Matrix

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.

Group Communication

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!

So in the long term, group communication tends to converge around a small selection of convenient-to-use platforms. In the group communication space, the most popular are Discord and Telegram.

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.

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.

End-to-end Encryption

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.

You can read more about end-to-end encryption at Wikipedia or ProtonMail.

Federation

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 bob@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.

Matrix

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 want to use Matrix without any commitment, simply register an account on the main matrix.org homeserver using the Element web app and skip to the Let’s Talk section.

If you’re interested in the extra security of running your own homeserver, read on.

Homeserver Installation

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 homeserver.yaml file.

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 /var/lib/docker/volumes/synapse_data/_data/.

Edit the homeserver.yaml file using nano or vim, paying particular attention to these variables:

  • server_name which I’ve set to bowtieddevil.com.

  • public_baseurl which I’ve set to https://matrix.bowtieddevil.com.

  • allow_public_rooms_over_federation which I’ve set to true, allowing users from other servers to view the public rooms on my homeserver.

  • Comment out the sqlite database section, replacing it with these values for PostgreSQL:

  • database:

    • name: psycopg2
    • user: synapseUser
    • password: synapsePass
    • database: synapseDB
    • host: db
    • cp_min: 5
    • cp_max: 10
  • Edit the following email variables (refer to my email sending lesson if needed:

    • smtp_host
    • smtp_port
    • smtp_user
    • smtp_pass
    • require_transport_security
    • notif_from

Note: the 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 bowtieddevil@bowtieddevil.com (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 docker-compose.yml.

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 CTRL+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 server_name.
  • On another port or hostname specified in .well-known/matrix/server or in a SRV DNS record.

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:

  • An A and AAAA record for matrix with my IP addresses.
  • A SRV record 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.

Logging In

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: Image

Let’s Talk

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!

Newsletter

Tip Jar

My content is free, but my time is not. If you're getting value from my writing, please support my efforts with a donation! You can donate directly using my public Ethereum address bowtieddevil.eth. Or you can use the donation button below, which works through my self-hosted BTCPay Server.