341 lines
11 KiB
Markdown
341 lines
11 KiB
Markdown
+++
|
||
title = 'Blackout'
|
||
date = 2026-01-26T07:21:51Z
|
||
description = "I tried to be more useful, but when I couldn't, I found another way."
|
||
draft = false
|
||
+++
|
||
|
||
Ever since the latest Internet/communication blackout in Iran, I've been trying to get access to my box inside of the network. I went so far as to beg people to ssh into my box and deploy something that would give me a backdoor to my box, but little to no luck came out of it. Although Arvancloud opened their panel briefly, giving me access to do some digging, I got stuck at putting some sort of binary client on my box from their online panel. I tried to b64 the binary file and copy‑paste it via clipboard, and this caused their panel to crash (or it was just bad timing) — they haven't opened it again.
|
||
|
||
After getting more and more frustrated, I decided to put my hardware to good use, and host residential-ish volunteer proxies for people wanting to reach the free internet. I chose to host my beloved Snowflake, Conduit (since it became a hot topic), and Lantern. Here’s how you can do the same if you decide to do so.
|
||
|
||
One important vibe check before we start: I’m not giving anyone a custom “backdoor” into *your* network, and you shouldn’t either. The whole point of the tools below is that they’re designed to work as **volunteer nodes** inside larger networks (Tor / Psiphon / Lantern), not as random open proxies you expose yourself.
|
||
|
||
# Running Anti‑Censorship Infrastructure at Home (Raspberry Pi, server, whatever)
|
||
|
||
I didn’t set out to become a tiny piece of global internet infrastructure. I just had a couple of Raspberry Pis, a decent internet connection, and the uncomfortable feeling that access to information shouldn’t depend on where you were born.
|
||
|
||
So I turned my Pis into helpers.
|
||
|
||
This post is about running **three different anti‑censorship tools** on Raspberry Pi hardware, quietly and continuously, without exposing scary open ports or babysitting terminals:
|
||
|
||
- **Psiphon Conduit** – to help Psiphon users automatically
|
||
- **Tor Snowflake (standalone proxy)** – to help Tor users automatically
|
||
- **Lantern Unbounded** – a browser‑based volunteer bridge, daemonized so it runs forever
|
||
|
||
Everything runs headless (or *headless‑ish*), survives reboots, and doesn’t require me to log in every week just to check if it’s still alive.
|
||
|
||
---
|
||
|
||
## The philosophy: don’t be a public server, be a volunteer node
|
||
|
||
A theme you’ll see throughout this setup is intentional modesty. I’m not running an open proxy. I’m not advertising an IP address. I’m not routing half the internet through my house.
|
||
|
||
Instead, I’m running software that’s designed to safely integrate into bigger systems that already handle discovery, encryption, throttling, abuse prevention, and client UX.
|
||
|
||
That’s the difference between helping — and waking up to your ISP asking why your home IP suddenly became “a whole thing”.
|
||
|
||
---
|
||
|
||
## What you need (one time)
|
||
|
||
This guide assumes Ubuntu on ARM (Pi). It works on a normal server too.
|
||
|
||
First, install Docker (because containers are a gift):
|
||
|
||
```bash
|
||
sudo apt update
|
||
sudo apt install -y curl
|
||
curl -fsSL https://get.docker.com -o get-docker.sh
|
||
sudo sh get-docker.sh
|
||
sudo usermod -aG docker $USER
|
||
newgrp docker
|
||
```
|
||
|
||
Now make yourself a clean playground under `/srv`:
|
||
|
||
```bash
|
||
sudo mkdir -p /srv/{conduit,snowflake,lantern}
|
||
sudo chown -R $USER:$USER /srv/{conduit,snowflake,lantern}
|
||
```
|
||
|
||
I like `/srv` because it signals “service data lives here” and I’m less likely to delete it while cleaning my home directory at 3am.
|
||
|
||
---
|
||
|
||
## Conduit: quietly helping Psiphon users (Docker)
|
||
|
||
Conduit is Psiphon’s volunteer “station” software. Once it’s running, Psiphon’s own network decides when and how clients use it. There’s nothing to configure, nothing to advertise, and no port forwarding required.
|
||
|
||
The important part is persistence. Conduit generates an identity key the first time it runs (`conduit_key.json`), and that identity builds reputation over time. If you lose it, you start from zero again.
|
||
|
||
### The file: `/srv/conduit/docker-compose.yml`
|
||
|
||
Create it:
|
||
|
||
```bash
|
||
cd /srv/conduit
|
||
vim docker-compose.yml
|
||
```
|
||
|
||
Paste this:
|
||
|
||
```yaml
|
||
services:
|
||
conduit:
|
||
image: ghcr.io/ssmirr/conduit/conduit:latest
|
||
container_name: conduit
|
||
restart: unless-stopped
|
||
|
||
# Keep keys and state across restarts
|
||
volumes:
|
||
- ./data:/app/data
|
||
|
||
# Optional: if you want a different bandwidth limit / max clients,
|
||
# you can pass CLI flags here (these are examples).
|
||
# command: ["conduit", "start", "-b", "4", "-c", "8"]
|
||
```
|
||
|
||
Then bring it up:
|
||
|
||
```bash
|
||
docker compose up -d
|
||
docker logs -f conduit
|
||
```
|
||
|
||
When it’s working, you’ll see things like:
|
||
|
||
- `[OK] Connected to Psiphon network`
|
||
- periodic `[STATS]` lines with Connecting/Connected counters (that’s your “is anyone using this?” moment)
|
||
|
||
If you ever want to stop it:
|
||
|
||
```bash
|
||
docker stop conduit
|
||
```
|
||
|
||
Or “disable but keep everything” (recommended):
|
||
|
||
```bash
|
||
docker compose down
|
||
```
|
||
|
||
Or “delete it from orbit” (not recommended unless you enjoy rebuilding):
|
||
|
||
```bash
|
||
docker rm -f conduit
|
||
```
|
||
|
||
---
|
||
|
||
## Snowflake: Tor, but even quieter (Docker Compose)
|
||
|
||
Snowflake serves Tor users. It’s explicitly designed to work behind NAT and CGNAT, which makes it perfect for home connections. The Tor Project provides an official Docker Compose file that includes **watchtower** for automatic updates.
|
||
|
||
### The file: `/srv/snowflake/docker-compose.yml`
|
||
|
||
You can download the official file exactly like this:
|
||
|
||
```bash
|
||
cd /srv/snowflake
|
||
wget -O docker-compose.yml \
|
||
"https://gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/snowflake/-/raw/main/docker-compose.yml?ref_type=heads"
|
||
```
|
||
|
||
If you’d rather create it yourself (it’s tiny), here’s the same thing in readable YAML:
|
||
|
||
```yaml
|
||
services:
|
||
snowflake-proxy:
|
||
network_mode: host
|
||
image: containers.torproject.org/tpo/anti-censorship/pluggable-transports/snowflake:latest
|
||
container_name: snowflake-proxy
|
||
restart: unless-stopped
|
||
|
||
# For a full list of Snowflake proxy args, see the upstream docs.
|
||
# Example if you ever need it:
|
||
# command: ["-ephemeral-ports-range", "30000:60000"]
|
||
|
||
watchtower:
|
||
image: containrrr/watchtower
|
||
container_name: watchtower
|
||
restart: unless-stopped
|
||
volumes:
|
||
- /var/run/docker.sock:/var/run/docker.sock
|
||
command: snowflake-proxy
|
||
```
|
||
|
||
Start it (with auto updates):
|
||
|
||
```bash
|
||
docker compose up -d
|
||
docker logs -f snowflake-proxy
|
||
```
|
||
|
||
If you see:
|
||
|
||
```text
|
||
Proxy starting
|
||
NAT type: restricted
|
||
```
|
||
|
||
…that’s totally fine. “Restricted” is normal for home NAT. If it’s running, you’re helping.
|
||
|
||
If you get the “container name already in use” error, it just means you already have a `snowflake-proxy` container from a previous attempt. Fix it with:
|
||
|
||
```bash
|
||
docker rm -f snowflake-proxy
|
||
docker compose up -d
|
||
```
|
||
|
||
---
|
||
|
||
## Lantern Unbounded: a browser that helps people (systemd + Xvfb)
|
||
|
||
Lantern Unbounded is different: it runs in a browser (WASM + WebRTC), which makes volunteering frictionless… but also makes servers go “uhh… where is your display?”.
|
||
|
||
So we cheat — politely.
|
||
|
||
We run Chromium inside a virtual display (Xvfb) and supervise it with systemd. No monitor needed. No desktop clicking. It just… exists… and helps.
|
||
|
||
### Install what we need
|
||
|
||
```bash
|
||
sudo apt update
|
||
sudo apt install -y xvfb
|
||
sudo apt install -y chromium-browser || sudo apt install -y chromium
|
||
```
|
||
|
||
### Fix the X11 socket dir permissions (the annoying but essential part)
|
||
|
||
If you don’t do this, Xvfb may throw:
|
||
|
||
`_XSERVTransmkdir: ERROR: euid != 0, directory /tmp/.X11-unix will not be created.`
|
||
|
||
Fix it once:
|
||
|
||
```bash
|
||
sudo chmod 1777 /tmp
|
||
sudo mkdir -p /tmp/.X11-unix
|
||
sudo chown root:root /tmp/.X11-unix
|
||
sudo chmod 1777 /tmp/.X11-unix
|
||
```
|
||
|
||
### The file: `/etc/systemd/system/unbounded.service`
|
||
|
||
Pick your username. On my boxes, it’s either `hub` or `rpi`. Use your actual user.
|
||
|
||
Create the service:
|
||
|
||
```bash
|
||
sudo vim /etc/systemd/system/unbounded.service
|
||
```
|
||
|
||
Paste this, and **replace `YOUR_USER`** with your username (e.g. `hub` or `rpi`):
|
||
|
||
```ini
|
||
[Unit]
|
||
Description=Lantern Unbounded (Xvfb + Chromium)
|
||
After=network-online.target
|
||
Wants=network-online.target
|
||
|
||
[Service]
|
||
User=YOUR_USER
|
||
Environment=DISPLAY=:99
|
||
Environment=XDG_RUNTIME_DIR=/run/user/%U
|
||
|
||
ExecStart=/usr/bin/bash -lc '\
|
||
/usr/bin/Xvfb :99 -screen 0 1280x720x24 -nolisten tcp -ac & \
|
||
sleep 1; \
|
||
$(command -v chromium || command -v chromium-browser) \
|
||
--no-first-run \
|
||
--disable-breakpad \
|
||
--disable-features=TranslateUI \
|
||
--autoplay-policy=no-user-gesture-required \
|
||
--use-fake-ui-for-media-stream \
|
||
--disable-gpu \
|
||
--no-sandbox \
|
||
--app=https://unbounded.lantern.io/ \
|
||
--kiosk \
|
||
--window-size=1280,720 \
|
||
--user-data-dir=/home/YOUR_USER/.config/unbounded-chromium \
|
||
--disk-cache-dir=/home/YOUR_USER/.cache/unbounded-chromium \
|
||
'
|
||
|
||
Restart=always
|
||
RestartSec=5
|
||
KillMode=mixed
|
||
|
||
[Install]
|
||
WantedBy=multi-user.target
|
||
```
|
||
|
||
Enable and start:
|
||
|
||
```bash
|
||
sudo systemctl daemon-reload
|
||
sudo systemctl enable --now unbounded.service
|
||
systemctl status unbounded.service --no-pager
|
||
```
|
||
|
||
If you see `Active: active (running)`, you’ve won.
|
||
|
||
### “It runs headless-ish, but how do I know it’s alive?”
|
||
|
||
The simplest “is it on?” checks:
|
||
|
||
```bash
|
||
systemctl status unbounded.service --no-pager
|
||
journalctl -u unbounded.service -f
|
||
```
|
||
|
||
And the “is it actually doing network things?” check:
|
||
|
||
```bash
|
||
sudo ss -tunap | egrep 'chrome|chromium|:443|:3478' || true
|
||
```
|
||
|
||
If you ever want to stop it:
|
||
|
||
```bash
|
||
sudo systemctl stop unbounded.service
|
||
```
|
||
|
||
If you want it not to start on boot:
|
||
|
||
```bash
|
||
sudo systemctl disable unbounded.service
|
||
```
|
||
|
||
---
|
||
|
||
## A note on safety, legality, and expectations
|
||
|
||
None of this makes you anonymous or magically protected from local laws. You’re contributing bandwidth and connectivity to systems designed to reduce risk — but you should still understand your own environment, and you should still be comfortable with what you’re running.
|
||
|
||
The good news is that all of these tools are designed so you’re not manually granting access to random people. You’re volunteering into networks that already do the hard parts.
|
||
|
||
That’s the part I like.
|
||
|
||
---
|
||
|
||
## The quiet satisfaction of it all
|
||
|
||
There’s something deeply satisfying about knowing that a small box on a shelf is occasionally helping someone read, learn, or communicate when they otherwise couldn’t.
|
||
|
||
No dashboard. No vanity metrics. Just the occasional log line, quietly scrolling by.
|
||
|
||
And honestly? That’s enough.
|
||
|
||
---
|
||
|
||
## Some rants
|
||
|
||
I was unable to hear from my parents in the first two weeks of the blackout. In fact, the first time I heard their voices was a few days ago. Ever since then we've been able to hear each other over calls — not fancy digital ones, the shitty GSM stuff. I saw my mom's face for a few seconds on the first day she managed to connect to Psiphon, but after that no luck. :(
|
||
|
||
This is super sad and frustrating. I've not really been writing anything due to this. When I came back from my congress + Vienna trip, I've been dealing with this situation. It's just annoying. Let's cross our fingers for one of my fantasy mind‑created things come true. Things are so incredibly much better in my mind. I want to live in my mind and never come out. Uh... :(
|
||
|
||
---
|
||
|
||
{{< sigdl >}}
|
||
|