Skip to content
// embedded linux · systems design · cybersecurity
MOMINUX · 2026
mominux
← All posts
// BLOG POST

Embedded Firewalls ( Part 2)

Embedded Firewalls ( Part 2)
Featured image — shown in full, contained.⤢ ZOOM

In Part 1, I told a little bit of a story. We left the “stupid black boxes” era behind, looked at why embedded devices must have network-level protection, and then unpacked how and why Linux firewalls work with nftables, and why it’s a much cleaner, modern approach compared to the old iptables/xtables world.
We also mapped the key kernel modules

CONFIG_NETFILTER  
CONFIG_NF_TABLES  
CONFIG_NF_CONNTRACK  
CONFIG_NFT_CT  
CONFIG_NFT_COUNTER

to the architecture diagram so you could see exactly what enables what.
That was the theory.

Now it’s time to get our hands dirty and actually put all of this into an embedded Linux image with Yocto.
Because theory is fun, but nothing beats booting a QEMU terminal and watching your firewall fight with bad packets and drop them in real time.

What’s in the layer

From the moment I decided to bring this firewall into Yocto and test it on QEMU, I started building a layer around it.
At first, the idea was simple: just collect what I talked about in Part 1 and make it buildable.
But as I went deeper, I realized I could make it stronger, so I added a few extra pieces of hardening along the way.

You can clone the repository here:
github.com/mominux/meta-nf-table-systemd-firewall

Here’s what’s inside the layer (I won’t repeat the full README here, just the structure):

meta-nf-table-systemd-firewall/
├── conf/
│   └── layer.conf
├── recipes-core/
│   ├── nftables/
│   │   ├── files/
│   │   │   ├── nftables.conf
│   │   │   └── nft-apply.service
│   │   └── nftables_%.bbappend
│   ├── images/
│   │   └── firewall-systemd-image.bb
│   ├── dropbear/
│   │   ├── files/
│   │   │   └── dropbear_config
│   │   └── dropbear-config_%.bbappend
│   └── ssh-keys-root/
│       └── ssh-keys-root_1.0.bb
└── recipes-systemd/
    └── systemd_%.bbappend

You can read the technical breakdown in the repository’s README, but here I want to talk about something that wasn’t really covered in Part 1.

Firewall Hardening

In this layer, the firewall isn’t just enforcing nftables rules; it’s also locking down remote access.
I’ve configured Dropbear so that it no longer accepts password logins, and only SSH keys are allowed.

That single change already raises the bar dramatically. Password-based logins are the easiest targets for brute-force attacks, and on embedded systems with limited CPU or slow storage, even a simple password-cracking attempt can degrade performance or lock the device.

In this setup, Dropbear is rebuilt with password authentication disabled, your public SSH key is embedded directly into the image through a dedicated recipe, and only key-based logins are permitted (no exceptions).

Of course, there are many more ways to harden a firewall, but for now, this post focuses on access control and service protection, because those are the most visible weak points on small embedded devices.

Why we use keys instead of passwords

Passwords feel simple and familiar, but that’s exactly their weakness. They’re easy to guess, steal, or reuse. Brute-force SSH attacks are cheap and can overwhelm small systems. SSH keys solve this elegantly: the device keeps only public keys, while the private key remains under the operator’s control. Authentication becomes proof of ownership, not guesswork. For embedded products, this model is ideal: no shared secrets, no password resets, just clean key management and quick revocation when needed.

Design and theory of the key model we use

Under the hood, SSH key authentication is elegantly simple. The server stores authorized public keys for accounts, and the client proves possession of the corresponding private key by signing a challenge.
In our layer, we keep that flow minimal and robust, only public keys are placed into the image (nothing private), and Dropbear is configured to refuse password authentication. This yields a clear, auditable model: build-time provisioning installs the public keys you trust, and run-time authentication succeeds only when a client can prove possession of the matching private key.

For the cryptographic choice, we favor modern, compact, and fast keys. Ed25519 keys are a great fit for embedded systems because they are short, fast to compute, and resistant to many classical attacks that still affect common RSA key sizes. They also produce small fingerprints that are easy to verify. If you need compatibility with older infrastructure, you might include an RSA fallback, but by default Ed25519 offers the best balance between security, performance, and size for low-power devices.

A practical provisioning architecture follows naturally from this model. There are two paths in the layer: embedding the operator’s public key into the image at build time, and supporting live provisioning during manufacturing or in-field service.

Embedding via the ssh-keys-root recipe is simple and reproducible, the public key file is copied into /root/.ssh/authorized_keys during image installation, with strict permissions so that no one on the device can tamper with it (and yes, in production, you’ll want to think about this carefully, but let’s assume we’ve done that homework). The live path is equally important: for devices built in quantity, a manufacturing step might inject a unique key per unit, or an onboarding service might write a per-device key at first boot. Both work seamlessly because Dropbear simply checks the authorized-key list and accepts or rejects based on that.

Operational details and small but critical protections

Key-based authentication also needs a bit of housekeeping to be truly secure. Even small details can be critical.
File permissions matter: ~/.ssh must be 700, and authorized_keys must be 600, otherwise, SSH daemons will often ignore them. We recommend disabling root password logins entirely and using the key for root if you must allow direct access. Better yet, create a dedicated admin account with sudo restricted to necessary commands.Passphrases on private keys add protection if the key file is stolen, but they complicate automation. Hardware-backed keys or an ssh-agent on the operator’s side are better solutions: the operator unlocks a token once, and the device never sees the private key itself.

Key rotation and revocation are part of the design as well. When keys are embedded at build time, updating the authorized set requires either a firmware update or a remote provisioning step. For fleets, you can expose a secure management channel to rotate keys programmatically or use a per-device key written at manufacture. If a key is compromised, simply remove its public entry from the authorized list or push a config update that deletes it. This approach is cleaner than password resets because it’s based on a list of public credentials rather than shared secrets.

Why this model fits embedded device

Embedded devices are constrained, long-lived, and often physically accessible. That makes minimizing attack surface and avoiding human interaction essential. Key-based SSH authentication reduces that surface, eliminates brute-force noise, and supports scalable revocation and rotation. Ed25519 keeps CPU overhead low on both sides, and build-time or manufacturing provisioning fits naturally into automated Yocto or CI pipelines without complicating runtime. Pairing this model with systemd protections — locking down the nft-apply service, tightening Dropbear options, and disabling unused services — results in a compact, maintainable security posture suited to embedded environments.

Where to keep your private keys (a practical word)

Never bake private keys into images. Keep them out of the build tree entirely.
For development, use temporary keys on your workstation.
For production, rely on hardware tokens (YubiKeys, smartcards) or a secure secrets management process that injects public-key material into devices at manufacturing time.
If automation requires private keys (for CI or remote updates), ensure they’re stored in a secure vault, and that any process using them is audited and follows strict rotation policies.

Wrap-up (how this ties back to the layer)

In the repository, I added the structural pieces needed to implement this model.
Dropbear is configured to reject passwords, the ssh-keys-root recipe places authorized public keys into the image with correct permissions, and both build-time and live provisioning methods are documented in the README.
With that in place, the firewall is no longer just rules on a box, it’s a practical access model, where only cryptographically proven clients get in, and on-device configuration stays minimal, predictable, and auditable.

However, there’s another layer we still need to add, process-level hardening with systemd. The nftables and Dropbear services currently run with root privileges, and while they’re isolated in purpose, they’re not yet confined by the Principle of Least Privilege.

In the next iteration of the repository, I’ll extend the systemd unit files (nft-apply.service, dropbear.service) with strict sandboxing directives, to restrict what these daemons can see or modify on the filesystem.

That change will close the gap between an “embedded firewall that filters packets” and a “hardened firewall appliance” where even a compromised process is safely contained.

This part of the work will be documented and implemented in a dedicated Process Hardening section of the repository, as well as covered in the next post of this series.

This story continues….

Sources/References:

iptables vs nftables in Linux: What is The Difference? – TuxCare

tuxcare.com/blog/iptables-vs-nftables

Main differences with iptables – nftables wiki

wiki.nftables.org/wiki-nftables/index.php/Main_differences_with_iptables

CVE-2024-53141: Critical Netfilter Root Escalation Vulnerability – Linux Security

https://nikk.is-a.dev/blog/ed25119_n_rsa/

https://www.cavliwireless.com/blog/nerdiest-of-things/dropbear-ssh-lightweight-solution-iot-device-management
https://unix.stackexchange.com/questions/257590/ssh-key-permissions-chmod-settings

Visited 116 times, 1 visit(s) today