Building and securing a Signal Proxy service in Azure

Building and securing a Signal Proxy service in Azure

Some time ago, I came upon a blog from Signal on how to help people in Iran. The idea is to run a proxy that relays traffic for Signal messaging. This is then used in scenarios where the government blocks traffic to Signal’s frontends, for example. The Signal app will connect to the proxy (instead of Signal’s servers) and hopefully bypass or circumvent any government firewall and blocking attempts.

I like the idea – of helping others while utilizing Azure to learn something new, perhaps! Once the proxy runs, anyone (who knows the DNS address) can use it – it isn’t restricted or meant just for Iran. This blog is about my experience setting up the proxy and operating it for over a month at the time of writing.

💡
[ October 23, 2023 Update]: I've shutdown the proxy for now, due to low usage and a pretty steep cost for me personally.

Setting up the proxy

The official guidance for installing the proxy can be found here. You’ll need to run a Docker container that can be exposed via ports 80 and 443. That’s it. As I’ve written before (see here), running a single Docker container cost-efficiently in Azure requires a bit of thinking. There are so many services to choose from.

I’m a fan of simplicity, as it’s usually easier to fix when something inevitable breaks. I opted for a virtual machine running Ubuntu Linux. The guidance from Signal does not state what sort of resources are required for the container. My go-to VM size is the B2s – a burstable VM with two vCPU and 4 GB of RAM.

I spun it up with the usual settings. I’ll later need to create a template for this to make it more resilient, but during the first few months, I’m OK with a manual approach. Signal also states on their blog a proxy configuration is a temporary approach. This could, of course, mean years.

Once my VM was warmed up, I ran the usual updates with sudo apt update && sudo apt upgrade. The version of the distribution is 22.04 LTS. Let’s talk about securing the VM in a bit.

Next, I installed the Signal Proxy solution. It requires a valid SSL certificate, which uses Let’s Encrypt.

And that’s it! How much does the container need resources, then? It depends on the number of users you are planning to support. I’m supporting a two-digit number of users – including myself.

Checking with sudo docker stats I can see the containers are very lightweight:

The relay is just ~5.6 MB, Certbot is 27 MB, and another container supporting the relay is about 13 MB. Less than 50 MB in total. Network traffic is critical here – and as you can see from the stats in the image above, network I/O is in the gigabytes.

Monitoring from Azure, we can see that the CPU and network are looking good:

Average CPU utilization over 30 days is just a hair over 1 %. Network traffic (egress, which is what I’m interested in) is about 7.8 GB.

My B2s-sized VM is a bit oversize for this need. The amount of free RAM is about 2.5 GB.

Securing the Signal Proxy VM

I’m confident that people will be using the proxy for secure communications, so it’s only to be expected that I need to secure the virtual machine as well as I can.

To begin with, I’ve enrolled Defender for Servers on the VM. It’s a paid offering, part of Defender for Cloud in Azure. This capability provides me with real-time monitoring that identifies potential vulnerabilities and recommends actions to mitigate against those and other security risks. I followed up diligently on all possible recommendations in my first week of running the service.

In addition, I’m pulling all logs from the VM and feeding those to Microsoft Sentinel – the SIEM platform Azure natively provides me. This way, I can get alerts and incidents based on anything suspicious within the VM.

I’ve enabled only 80/TCP and 443/TCP for the box for network access. This is per recommendations from Signal, but I’m working on adding further boundaries and security control around these. The answer is no if you’re wondering if I can snoop on the Signal traffic. I could dump the (encrypted) traffic, but it wouldn’t show me anything useful.

Finally, I have pretty strict alerting enabled on the VM – anything from excess network traffic, to unexpected connection attempts, to outbound traffic that I’m not expecting. The VM is also sandboxed in a separate virtual network for this reason. Remote access to the VM is also secured and encrypted, utilizing the Just-In-Time network access of Defender for Servers.

What I like about the Signal Proxy solution is that the solution isn’t reliant on anything confidential. I do not need to login into Signal through the VM to activate it. In a way, it’s recyclable should I re-build the solution later on.

Using Signal Proxy

The service is only supported for Signal on a mobile device. Thus, using the Signal desktop app doesn’t route traffic via the proxy. On the mobile app, once configured, you’ll see a green shield if traffic is routed via the proxy:

You can stop using the proxy at any time by clearing the green shield within the app. No authentication information is needed, but the public DNS record is required for anyone wanting to connect to the proxy. And that, I feel, is the main obstacle for utilizing this proxy – once the DNS record leaks out, and once a hostile entity wants to block that, there is no way around that – unless the proxy can be re-published with another DNS record. And this requires re-configuring the Let’s Encrypt certificate.

How much does it cost?

Glad that you asked!

The base VM is costing me about $35 per month. Outbound traffic is negligible, with the current traffic at around $0.20 per month. Defender for Servers (P2) is $12 per month. Backup and other essential things are perhaps $1-2 in total.

So, in total, it’s less than $50 a month. Is it cheap? I could shave a few dollars off, but I don’t think my time is worth that. Offering a service that provides secure communications access to people who cannot otherwise secure their confidential messages is – I feel – more than worth the money I pay for it.

I’m using the proxy myself, also. Not because I need to. But I wanted to try how it works – and there is zero difference in user experience.

This is an excellent little hobby project to ensure a production service runs smoothly in Azure.