My RSS feed reader setup#

I run a CommaFeed instance on a free-tier Google Cloud Platform virtual machine. It’s a Google Reader-inspired self-hosted personal RSS reader.

I’m not using Docker, in part because the VM has only 0.25 CPU and, in part, because I like simple things.

Setup#

CommaFeed service runs under a SystemD Unit using a separate system user with a home directory:

sudo useradd -rms /sbin/nologin commafeed
sudo -u commafeed wget -O /home/commafeed/commafeed \
    https://github.com/Athou/commafeed/releases/download/5.12.1/commafeed-5.12.1-h2-linux-x86_64-runner
sudo chmod +x /home/commafeed/commafeed

(Check the releases page to get the latest native binary for your operating system).

Reverse proxy#

Nginx has a section dedicated to proxy pass feed subdomain to a localhost port:

/etc/nginx/sites-available/default:

server {
    server_name feed.demin.dev;
    location / {
      proxy_pass http://127.0.0.1:8082;
      proxy_set_header  X-Forwarded-Proto https;
      proxy_set_header Host $http_host;
    }
}

Make sure you configure TLS certificates, using certbot, for example.

SystemD Unit#

/etc/systemd/system/commafeed.service:

[Unit]
Description=A bloat-free feed reader
After=local-fs.target network.target

[Service]
User=commafeed
Group=commafeed
WorkingDirectory=/home/commafeed
ExecStart=/home/commafeed/commafeed
SyslogIdentifier=commafeed
Restart=always

[Install]
WantedBy=multi-user.target

(Check out the hardened version below.)

User registration#

By default, CommaFeed doesn’t allow user registration, which is sensible for a public, self-hosted service. To add a user for yourself, launch it with registration enabled:

sudo -u commafeed /bin/sh -c 'cd ~commafeed; COMMAFEED_USERS_ALLOW_REGISTRATIONS=true ./commafeed'

Then open the CommaFeed Web UI in your browser (in my case, https://feed.demin.dev) and create an account. Now stop the service (By pressing Ctrl+C), and launch it through the SystemD Unit:

sudo systemctl daemon-reload
sudo systemctl enable commafeed.service
sudo systemctl start commafeed.service

Backup#

I wouldn’t want to lose the extensive library of feeds I aggregated over many years. My solution is to export the OPML file whenever I think about how I don’t want to lose it. That’s a standard format that is easy to inspect and migrate to another service. I wish CommaFeed maintained a copy of this file in its data directory so I can back it up.

The treasure#

Here’s the list of feeds from the OPML file in alphabetical order:

Hardened SystemD Unit#

As a usual best practice, we should apply the principle of least privilege. The hardened version, which won’t let CommaFeed process do any harm even if it gets hacked:

[Unit]
Description=A bloat-free feed reader
Wants=network-online.target
After=local-fs.target network-online.target

[Service]
Type=simple
User=commafeed
Group=commafeed
WorkingDirectory=/home/commafeed
ExecStart=/home/commafeed/commafeed
SyslogIdentifier=commafeed
Restart=always
RestartSec=2

# Baseline safety
NoNewPrivileges=yes
UMask=0077
RemoveIPC=yes
CapabilityBoundingSet=
AmbientCapabilities=

# Filesystem sandbox
ProtectSystem=strict
ProtectHome=read-only
ReadWritePaths=/home/commafeed/data

# Stop it from seeing other processes' details
ProtectProc=invisible
ProcSubset=pid

# Kernel / namespace / device isolation
PrivateTmp=yes
PrivateDevices=yes
PrivateUsers=yes
PrivateMounts=yes

ProtectControlGroups=yes
ProtectKernelTunables=yes
ProtectKernelModules=yes
ProtectKernelLogs=yes
ProtectClock=yes
ProtectHostname=yes

LockPersonality=yes
RestrictSUIDSGID=yes
RestrictRealtime=yes
RestrictNamespaces=yes
MemoryDenyWriteExecute=yes

# Syscall filtering
SystemCallArchitectures=native
SystemCallFilter=@system-service @network-io
SystemCallErrorNumber=EPERM

# Only allow the socket families it likely needs
RestrictAddressFamilies=AF_UNIX AF_INET AF_INET6

# Optional resource limits
LimitNOFILE=4096

[Install]
WantedBy=multi-user.target