Chapter 35H: Rootfs security basics

This chapter is not a full security book. It is the minimum checklist before a rootfs leaves the lab.

The goal:

  • no empty root password,

  • no unnecessary network service,

  • app does not run as root unless it must,

  • secrets are not baked into public images,

  • writable data is separated from immutable system files.

35H.1 Development settings are not release policy

During bring-up, we often do this:

root::0:0:root:/root:/bin/sh

That means root has no password. Fine for a closed lab board. Bad for anything connected to a network.

Before release:

  • set a password or lock root,

  • prefer SSH keys,

  • create a non-root service user,

  • disable unused gettys and network daemons.

35H.2 Lock root login

In /etc/shadow, a locked root account starts with !:

root:!:19000:0:99999:7:::

Or on target:

[root@pa-mini:~]# passwd -l root

If you need emergency root access, use a recovery mode that requires physical access.

35H.3 SSH keys, not passwords

For Dropbear or OpenSSH, install an authorized key:

mkdir -p /home/dev/.ssh
chmod 700 /home/dev/.ssh
cat > /home/dev/.ssh/authorized_keys <<'EOF'
ssh-ed25519 AAAA... dev@host
EOF
chmod 600 /home/dev/.ssh/authorized_keys
chown -R dev:dev /home/dev/.ssh

Disable password login if the product allows it.

For OpenSSH:

PasswordAuthentication no
PermitRootLogin no

For Dropbear, pass options from its init script. The exact flags depend on the Buildroot Dropbear version, so check:

dropbear -h

35H.4 Run the app as a user

Create a service user:

myapp:x:200:200:myapp:/var/empty:/sbin/nologin

In /etc/group:

myapp:x:200:

BusyBox init script:

start-stop-daemon -S -b -c myapp -x /usr/bin/myapp

systemd unit:

[Service]
User=myapp
Group=myapp
ExecStart=/usr/bin/myapp

If the app needs GPIO, I2C, SPI, or video access, give access to that specific device or group. Do not run everything as root just because one file needed permission.

35H.5 File permissions

Useful defaults:

/usr/bin/myapp        0755 root:root
/etc/myapp.conf       0644 root:root
/data/myapp/          0750 myapp:myapp
/data/myapp/secret    0600 myapp:myapp

Check:

find / -perm -4000 -type f 2>/dev/null
find / -writable -type d 2>/dev/null | head

Setuid binaries deserve review. World-writable directories should be rare.

35H.6 Secrets

Do not bake these into a public rootfs image:

  • private SSH keys,

  • API tokens,

  • customer certificates,

  • production passwords,

  • signing keys.

Better places:

  • provision during manufacturing,

  • store under /data/secure/,

  • derive from a hardware unique key if available,

  • use a secure element or TPM for private keys.

At minimum, each shipped board should not share the same private identity.

35H.7 Reduce services

List listening sockets:

ss -lntup

On BusyBox-only systems without ss, use:

netstat -lntup

Ask for each service:

  • Why does this need to listen?

  • Is it bound to all interfaces?

  • Does it need authentication?

  • Can it be disabled in production?

Common development-only services:

  • SSH with password login,

  • telnet,

  • FTP,

  • debug HTTP servers,

  • gdbserver,

  • unrestricted MQTT broker.

35H.8 Read-only rootfs helps security

Chapter 35B used read-only rootfs for power-loss safety. It also helps security:

  • malware cannot easily modify /usr/bin,

  • accidental writes fail early,

  • factory reset can clear data without reflashing system files.

But read-only rootfs is not enough. If /data is writable and the app executes files from /data, attackers can still persist. Mount data with conservative options when possible:

LABEL=data  /data  ext4  defaults,noatime,nodev,nosuid  0  2

Use noexec only if your product never runs programs from /data.

35H.9 Release checklist

  • Root password locked or intentionally set.

  • SSH root login disabled.

  • Password SSH disabled if keys are available.

  • App runs as non-root.

  • /data permissions reviewed.

  • No private keys in the image.

  • ss -lntup shows only expected services.

  • Read-only rootfs tested.

  • Factory reset does not erase device identity unless intended.

35H.10 Key Notes

  • Security begins with authority: which account owns a service, which files it can change, and which interfaces it exposes. Running everything as root erases useful boundaries.

  • Device identity and credentials have a different lifecycle from ordinary configuration. Provisioning, backup, update and factory reset must agree on what survives.

  • An image shared across a fleet should not contain a shared private credential. Public build artifacts and secret provisioning belong to different trust domains.

  • Removing unnecessary services reduces exposure, but an immutable rootfs does not replace authentication, authorization or an update process.

  • Development convenience settings are not release policy. The relevant question is whether each privilege and listening service has a product purpose and an accountable owner.

35H.11 Pitfalls

  • One shared password across all boards. This becomes a fleet-wide secret.

  • Private keys in Git. Never put production keys in the repository.

  • App runs as root forever. Convenient in the lab, expensive later.

  • Factory reset wipes identity. Serial number, MAC address, and certificates often need to survive.

  • Security added after release. It is much harder after field deployment.


Previous chapter: Chapter 35B - Read-only rootfs and overlayfs

Next chapter: Chapter 35C - Container runtimes on embedded