Skip to content

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:

ss -tlnp

This shows all tCP listening sockets with numeric ports and the process name.

Note

Typical output on a fresh Raspberry Pi OS:

State   Recv-Q  Send-Q  Local Address:Port  Peer Address:Port  Process
LISTEN  0       128     0.0.0.0:22          0.0.0.0:*          users:(("sshd",pid=456,...))
LISTEN  0       5       0.0.0.0:5353        0.0.0.0:*          users:(("avahi-daemon",...))
LISTEN  0       128     0.0.0.0:631         0.0.0.0:*          users:(("cupsd",pid=789,...))

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:

ss -tlnp

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:

# On your laptop (NOT the Pi)
ssh-keygen -t ed25519 -C "your.email@example.com"

Copy the public key to the Pi:

ssh-copy-id pi@<your-pi-ip>

Test that key-based login works:

ssh pi@<your-pi-ip>
# Should NOT ask for a password

3.2 Disable Password Authentication

Edit the SSH server configuration on the Pi:

sudo nano /etc/ssh/sshd_config

Find and set these options:

PermitRootLogin no
PasswordAuthentication no
PubkeyAuthentication yes

Restart SSH:

sudo systemctl restart sshd
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:

sudo apt install -y nftables
sudo systemctl enable --now nftables

Create a basic firewall configuration:

sudo nano /etc/nftables.conf

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:

sudo nft -f /etc/nftables.conf

Verify:

sudo nft list ruleset
Note

This firewall allows only SSH and ping. If your application needs other ports (e.g., MQTT on port 8883), add rules like:

tcp dport 8883 accept

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:

mount | grep " / "

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:

sudo apt install -y mosquitto mosquitto-clients

Edit the configuration:

sudo nano /etc/mosquitto/conf.d/tls.conf

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:

  1. Audited the attack surface (open ports)
  2. Minimized the attack surface (disabled unnecessary services)
  3. Hardened SSH (key-only auth, no root login)
  4. Firewalled the device (only SSH and ping allowed)
  5. Referenced read-only rootfs for persistence protection
  6. 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

Data Logger Appliance | Tutorials Index