Security Audit and Hardening
Time estimate: ~45 minutes Prerequisites: SSH Login, Data Logger Appliance
Learning Objectives
After this tutorial you will be able to: - Audit open network ports on an embedded Linux device - Disable unnecessary services to reduce the attack surface - Configure SSH for key-only authentication - Set up a basic firewall with nftables - Understand TLS basics for embedded communication
Why Embedded Devices Are Vulnerable
Unlike servers locked in data centers, embedded devices are often physically accessible and deployed on hostile networks. Every device has multiple attack surfaces:
- Network: open ports (SSH, mDNS, CUPS), unencrypted protocols (plaintext MQTT/HTTP), weak or default credentials
- Physical: exposed UART/JTAG debug interfaces, removable SD cards, USB ports
- Software: CVEs in bundled libraries, unsigned firmware updates, services running as root
The Mirai botnet (2016) infected 600,000 IoT devices simply by scanning for default credentials (admin/admin) on open telnet ports -- launching DDoS attacks reaching 1.2 Tbps. Heartbleed (2014) allowed reading server memory through a missing bounds check in OpenSSL. Both exploited basic, preventable weaknesses.
The hardening approach is systematic: audit open ports (ss -tlnp), disable unnecessary services, enforce key-only SSH, enable a firewall with default-drop policy, encrypt all communication with TLS, and use a read-only rootfs to prevent persistent compromise. This checklist covers the vast majority of real-world embedded attacks.
For the full hardening checklist and protocol details, see the Networking and Security reference.
1. Audit Open Ports
First, discover what your Raspberry Pi is exposing to the network. SSH into your Pi and run:
This shows all tCP listening sockets with numeric ports and the process name.
Note
Typical output on a fresh Raspberry Pi OS:
Record the output — you'll compare it after hardening.
Each open port is a potential attack vector. Ask: Does this device need to print? Does it need mDNS discovery?
2. Disable Unnecessary Services
For an embedded device that only needs SSH access, most default services are unnecessary:
# Disable Bluetooth (not needed for our wired sensor setup)
sudo systemctl disable --now bluetooth
# Disable Avahi/mDNS (network service discovery)
sudo systemctl disable --now avahi-daemon
# Disable CUPS (printing)
sudo systemctl disable --now cups cups-browsed
Warning
Only disable services you understand. If you're unsure what a service does, research it first.
To re-enable: sudo systemctl enable --now <service>
Verify the result:
You should see fewer listening ports now. Only SSH (port 22) should remain.
Checkpoint
Running ss -tlnp shows only port 22 (SSH). All other services are stopped and disabled.
3. Harden SSH
SSH is your device's primary remote access — it must be hardened.
3.1 Generate an SSH Key Pair (on your laptop)
If you don't already have an SSH key:
Copy the public key to the Pi:
Test that key-based login works:
3.2 Disable Password Authentication
Edit the SSH server configuration on the Pi:
Find and set these options:
Restart SSH:
Warning
Before closing your current SSH session, open a second terminal and verify you can still connect. If you lock yourself out, you'll need physical access to the Pi to fix it.
Checkpoint
SSH works with your key. Password login is rejected. Root login is disabled.
4. Basic Firewall with nftables
nftables is the modern Linux firewall framework (replaces iptables). We'll set up a minimal ruleset.
Install nftables if not present:
Create a basic firewall configuration:
Replace the contents with:
#!/usr/sbin/nft -f
flush ruleset
table inet filter {
chain input {
type filter hook input priority 0; policy drop;
# Allow established/related connections
ct state established,related accept
# Allow loopback
iif lo accept
# Allow SSH
tcp dport 22 accept
# Allow ICMP (ping)
ip protocol icmp accept
ip6 nexthdr icmpv6 accept
# Log and drop everything else
log prefix "nftables-drop: " drop
}
chain forward {
type filter hook forward priority 0; policy drop;
}
chain output {
type filter hook output priority 0; policy accept;
}
}
Apply the rules:
Verify:
Note
This firewall allows only SSH and ping. If your application needs other ports (e.g., MQTT on port 8883), add rules like:
Checkpoint
Firewall is active. Only SSH (22) and ICMP (ping) are allowed. All other incoming traffic is dropped.
5. Read-Only Root Filesystem
A read-only root filesystem prevents persistent modification of system files — even if an attacker gains access, they cannot install backdoors that survive a reboot.
Info
The full procedure for setting up a read-only rootfs with overlayfs is covered in Data Logger Appliance. If you've already completed that tutorial, your rootfs is already read-only.
For a quick check:
If you see ro in the mount options, your root is read-only. If not, consider implementing it following the Data Logger Appliance tutorial.
6. TLS for MQTT (Optional)
If your device communicates over MQTT, all traffic should be encrypted with TLS.
6.1 Generate a Self-Signed Certificate
# On the Pi (or your gateway)
openssl req -x509 -newkey ec -pkeyopt ec_paramgen_curve:prime256v1 \
-keyout mqtt-server.key -out mqtt-server.crt \
-days 365 -nodes \
-subj "/CN=mqtt-broker/O=Lab"
6.2 Configure Mosquitto with TLS
Install Mosquitto:
Edit the configuration:
Add:
listener 8883
certfile /etc/mosquitto/certs/mqtt-server.crt
keyfile /etc/mosquitto/certs/mqtt-server.key
require_certificate false
Copy certificates and restart:
sudo mkdir -p /etc/mosquitto/certs
sudo cp mqtt-server.crt mqtt-server.key /etc/mosquitto/certs/
sudo chown mosquitto: /etc/mosquitto/certs/*
sudo systemctl restart mosquitto
6.3 Test TLS Connection
# Subscribe (terminal 1)
mosquitto_sub -h localhost -p 8883 --cafile mqtt-server.crt -t "test/secure"
# Publish (terminal 2)
mosquitto_pub -h localhost -p 8883 --cafile mqtt-server.crt -t "test/secure" -m "Hello, TLS!"
Checkpoint
MQTT communication works over TLS on port 8883. Plaintext port 1883 is not open.
What Just Happened?
You applied the core items from the security hardening checklist:
- Audited the attack surface (open ports)
- Minimized the attack surface (disabled unnecessary services)
- Hardened SSH (key-only auth, no root login)
- Firewalled the device (only SSH and ping allowed)
- Referenced read-only rootfs for persistence protection
- Encrypted communication with TLS (MQTT example)
These steps cover the most common vulnerabilities found in real-world embedded devices (see the Mirai botnet case study in the Networking and Security reference).
Challenges
Tip
Challenge 1: Port Knocking Research port knocking as an additional layer for SSH access. Implement a simple port-knock sequence using nftables that opens port 22 only after receiving packets on ports 7000, 8000, 9000 in sequence.
Tip
Challenge 2: Fail2Ban
Install and configure fail2ban to automatically block IP addresses after 3 failed SSH attempts. Test it by intentionally failing authentication.
Tip
Challenge 3: Full Audit Report Write a one-page security audit report for your Pi. Include: open ports, running services, SSH configuration, firewall rules, filesystem mount options, and installed package count. Compare against the hardening checklist from the Networking and Security reference.
Deliverable
| Item | Expected |
|---|---|
ss -tlnp output (before and after) |
Only port 22 remains |
| SSH config | Key-only auth, no root login |
| nftables rules | Default drop, SSH + ICMP allowed |
| MQTT TLS test | Messages sent/received on port 8883 |