Hardening a Linux server for an IoT backend
An IoT backend is a high-value target. It typically holds device credentials, ingests telemetry from the field, and — most dangerously — often controls over-the-air updates. Compromise it and an attacker doesn’t get one device; they potentially get the whole fleet. That makes the server behind your product worth hardening properly, not with a vague “we ran updates” wave of the hand.
Here’s a practical baseline. None of it is exotic — the value is in doing all of it, consistently.
1. Reduce the attack surface
The safest service is the one that isn’t running.
- Install the minimum: no desktop packages, no compilers you don’t need in production, no forgotten demo apps.
- Audit listening ports (
ss -tulpn) and shut down anything that doesn’t need to be public. - One server, one job where you can. A database that only the app talks to should not be listening on a public interface.
2. Lock down SSH
SSH is the front door. Treat it like one.
- Keys only — disable password authentication entirely.
- No direct root login — log in as a user, escalate with
sudo. - Restrict who can log in, and from where (firewall the SSH port to known admin IPs/VPN if you can).
- Consider a non-default port only as noise reduction — it’s not security by itself.
3. Firewall by default-deny
Start from “block everything,” then open exactly what’s needed. nftables (or
ufw as a friendly front end) with a default-deny inbound policy, allowing only
the ports your service actually serves. Egress filtering is worth it too for a
backend that shouldn’t be making arbitrary outbound connections.
4. TLS everywhere
- Public endpoints: TLS with automatic certificate renewal (Let’s Encrypt).
- Device-to-backend traffic encrypted — and if your threat model warrants it, mutual TLS so devices authenticate the server and the server authenticates devices.
- No plaintext internal hops that cross a network boundary.
5. Least privilege
- Services run as their own unprivileged users, never root.
- Filesystem permissions tight; secrets not world-readable.
- Use systemd sandboxing (
ProtectSystem,NoNewPrivileges,PrivateTmp) to box in each service cheaply.
6. Automatic security updates
Unpatched known vulnerabilities are the most common way in. Enable unattended security updates so critical fixes land without waiting for someone to remember. Pair it with a reboot strategy for kernel updates.
7. Secrets, not hard-coded credentials
Device keys, database passwords, API tokens — out of source code and config files in plaintext. Use environment injection, a secrets manager, or at minimum root-owned files with strict permissions. Rotate credentials you suspect were ever exposed.
8. Logs and backups you can actually use
- Centralise logs so a compromised host can’t simply erase its own tracks, and so you can spot anomalies.
- Backups you have actually test-restored. A backup you’ve never restored is a hope, not a backup — especially for the database holding your fleet state.
The point
Individually these are basic. Together they’re the difference between “an attacker who gets one credential owns everything” and “an attacker who gets one credential hits a wall.” For an IoT product, where the backend is the fleet’s brain, that baseline is not optional.
Standing up or hardening the infrastructure behind a hardware product? Linux administration work — with a security bias, from people who also build the device side.
