OneLinersCommand workbench
Guides
Security / Networking & DNS

Build a baseline nftables firewall

Create a default-deny host firewall with loopback, established traffic, controlled SSH, service ports, logging, validation, and rollback.

40 min8 stepsHigh-impact changeRevision 2
Save or explore
Save to collectionCreate a collection in the sidebar first.
0 of 8 steps completed
Goal

Limit inbound traffic to explicitly approved services while preserving administration access.

Supported environments
  • Ubuntu Server 22.04 LTS, 24.04 LTS
  • nftables 1.x
Prerequisites
  • Console access A malformed policy can immediately block SSH.
  • Service inventory Map every listener to an owner and approved source.sudo ss -lntup
  • Ruleset backup Export active and persistent rules before changing them.sudo nft list ruleset
Operating boundary

OneLiners never runs these steps or stores secrets. Review placeholders, versions, current state, and change-control requirements before using a command.

Full guide

What you will build

System
  • A persistent nftables ruleset with a default-drop input policy, explicit administration and web-service allows, stateful return traffic, essential ICMP, and rate-limited drop logging.
  • A repeatable safety workflow that inventories listeners, captures the active policy, validates a candidate without applying it, and schedules an automatic rollback before the live change.
  • A verified boot-time nftables service whose on-disk and active rulesets match after a second-session reachability test.
Observable outcome
  • Only the approved management source can reach SSH, public clients can reach TCP 80 and 443, and unsolicited inbound traffic is dropped.
  • Existing established connections and essential IPv4/IPv6 control traffic continue to work.
  • A known-good ruleset is available under /root and a temporary systemd timer can restore it if the administrator loses connectivity.
  • nft --check succeeds, a new external SSH connection works, published ports behave as planned, and nftables is enabled for reboot.

Architecture

How the parts fit together

Packets traverse an inet-family input chain before reaching local services. Connection tracking accepts return traffic, explicit rules admit approved new traffic, and the terminal drop policy rejects everything else. A separate forward chain remains closed because this baseline is for a host, not a router.

Listening servicesDefine the real attack surface that the ruleset must intentionally publish or keep private.
inet filter tableApplies one dual-stack input policy to IPv4 and IPv6 traffic.
Connection trackingRecognizes established, related and invalid flows so replies remain possible without reopening all ports.
nftables.serviceLoads /etc/nftables.conf during boot and exposes service health through systemd.
Rollback timerLoads the captured known-good ruleset after five minutes unless the operator cancels it after successful tests.
  1. A packet addressed to the host reaches the input hook.
  2. Loopback and established or related traffic are accepted first; invalid state is dropped.
  3. Essential ICMP, trusted SSH and approved service ports are matched explicitly.
  4. Unmatched packets may produce a rate-limited log entry and then meet the chain's drop policy.

Assumptions

  • The target is an Ubuntu server that uses nftables as its authoritative host firewall. It is not simultaneously managed by UFW, firewalld, Docker-generated rules, Kubernetes or another controller.
  • Console or provider recovery access is tested, the current SSH session remains open, and a second client is available for reachability checks.
  • The administrator knows the real SSH port, management source CIDR, public services, IPv4 and IPv6 requirements, and every listening socket owner.
  • This is a host baseline with forward policy drop. A VPN gateway, container router or hypervisor needs a separately designed forward and NAT policy.
  • The example permits public HTTP and HTTPS. Remove those rules when the host does not actually serve them.

Key concepts

Ruleset
The complete nftables object graph currently loaded in the kernel, including tables, chains, sets, maps and rules.
Base chain
A chain attached to a Netfilter hook with a type, priority and policy. The input base chain sees traffic addressed to the local host.
Default deny
A policy where unmatched traffic is dropped and every accepted path must be declared explicitly.
Connection tracking
Kernel state that classifies packets as new, established, related or invalid, allowing reply traffic without broad reverse rules.
inet family
A table family that can match both IPv4 and IPv6 while still permitting protocol-specific expressions.
Atomic load
nft parses a complete batch and commits it as one transaction, avoiding a partially loaded policy when a statement fails.

Before you copy

Values used in this guide

{{host}}

Public DNS name or address of the server under test.

Example: server.example.com
{{managementCidr}}

Trusted source network allowed to initiate SSH.

Example: 203.0.113.0/24
{{sshPort}}

Actual TCP port used by the SSH listener.

Example: 22

Security and production boundaries

  • A default-drop policy is only as accurate as the inventory. Missing DNS, monitoring, cluster, backup or management paths can create an outage even when syntax is valid.
  • IPv6 is not disabled by using an inet table. Test it explicitly; an IPv4-only validation can miss an exposed or broken IPv6 path.
  • Logging every dropped packet can be a denial-of-service vector. The rate limit protects log capacity but must be tuned with real traffic and monitoring.
  • Do not mix independent firewall managers. Their competing reloads can reorder, flush or recreate rules unexpectedly.

Stop before continuing if

  • Stop if you cannot identify every listener or decide whether it should be reachable.
  • Stop if another firewall manager owns the host, Docker or Kubernetes depends on generated rules, or the server forwards traffic.
  • Stop if the known-good backup cannot be read by root or the rollback timer cannot be scheduled.
  • Stop on any nft --check error; never test syntax by loading it live.
  • Stop if a new SSH session or any required health check fails, and allow or trigger rollback before closing the original session.
01

command

Inventory listening services

read-only

Decide whether each socket needs external access.

Why this step matters

Firewall policy must follow the actual service inventory; otherwise a default drop can cause an outage or an obsolete listener can remain accidentally exposed.

What to understand

ss shows TCP and UDP listening sockets, numeric ports, owning processes and every bound address. A wildcard address such as 0.0.0.0 or [::] is reachable on more interfaces than loopback.

Record an owner, purpose, expected source and monitoring check for each socket before translating it into an allow rule.

System changes

  • No persistent changes; reads kernel socket and process metadata.

Syntax explained

-l
Shows listening sockets.
-n
Keeps addresses and ports numeric instead of resolving names.
-t / -u
Includes TCP and UDP sockets.
-p
Shows the process using a socket when permissions allow.
Command
sudo ss -lntup
Example output / evidence
tcp LISTEN 0 4096 0.0.0.0:22 users:(("sshd",pid=812,fd=3))

Checkpoint: Approve every listener

sudo ss -lntup

Continue whenEvery wildcard listener has a documented owner and intended source network.

Stop whenAn unknown listener exists or the real management port is not known.

Security notes

  • Do not publish a port merely because it is listening; bind private services to loopback where possible.

Alternatives

  • Use systemd socket inventory and application configuration as supporting evidence.

Stop conditions

  • Do not write the allowlist from assumptions or an old diagram.
02

command

Back up the current rules

caution

Save the active ruleset and persistent file with a timestamp.

Why this step matters

Capture both the active kernel policy and the persistent file because either may be the only known-good recovery source after a failed remote change.

What to understand

The root shell performs redirection into /root, avoiding the common mistake where sudo applies only to nft while the unprivileged shell cannot create the backup file.

cp --archive preserves ownership, mode and timestamps of the on-disk configuration. Verify both artifacts before scheduling rollback.

System changes

  • Creates /root/nftables-before.nft and /etc/nftables.conf.bak as recovery artifacts.

Syntax explained

sudo sh -c
Runs the redirection and nft command together with root privileges.
nft list ruleset
Serializes the active kernel ruleset in nft syntax.
cp --archive
Copies the persistent file while preserving metadata.
Command
sudo sh -c 'nft list ruleset > /root/nftables-before.nft' && sudo cp --archive /etc/nftables.conf /etc/nftables.conf.bak
Example output / evidence
Ruleset and configuration backups created

Checkpoint: Validate the recovery artifacts

sudo test -s /root/nftables-before.nft && sudo nft --check --file /root/nftables-before.nft && sudo test -s /etc/nftables.conf.bak

Continue whenBoth files are non-empty and the captured active ruleset parses successfully.

Stop whenAny backup is missing, empty or invalid.

If this step fails

The shell reports Permission denied for /root/nftables-before.nft.

Likely causeRedirection was performed by the unprivileged shell outside sudo.

Safe checks
  • id
  • sudo test -w /root

ResolutionUse sudo sh -c so the process opening the destination file runs as root.

Security notes

  • Rules may contain internal addresses and security policy; keep the backup root-readable.

Alternatives

  • Store a versioned encrypted copy in approved configuration management in addition to the local emergency file.

Stop conditions

  • Do not proceed without a parseable known-good ruleset.
03

config

Write a default-deny input policy

danger

Accept loopback, established traffic, essential ICMP, trusted SSH, and approved service ports.

Why this step matters

A complete candidate makes the intended trust boundaries reviewable before the kernel changes and avoids an incremental sequence that temporarily leaves the host open or unreachable.

What to understand

The inet family combines IPv4 and IPv6 filtering. The input chain's drop policy is reached only after explicit loopback, state, ICMP, management and public-service accepts.

The forward chain remains closed and output remains allowed. Rate-limited logging is diagnostic evidence, not an accept or drop verdict by itself; the chain policy performs the final drop.

System changes

  • Replaces /etc/nftables.conf as the persistent host policy when the operator saves the reviewed template.

Syntax explained

flush ruleset
Removes existing nftables objects when this complete file is loaded.
table inet filter
Creates one dual-stack filtering table.
hook input priority filter; policy drop
Attaches the chain to locally destined packets with default denial.
ct state established,related accept
Keeps replies and related flows working.
ct state invalid drop
Drops packets connection tracking cannot associate with a valid flow.
ip protocol icmp / ip6 nexthdr ipv6-icmp
Allows essential IPv4 and IPv6 control messages.
ip saddr ... tcp dport ... accept
Limits new SSH connections to the trusted source CIDR and selected port.
limit rate 5/second log
Logs unmatched packets at a bounded rate before the default drop.
File /etc/nftables.conf
Configuration
Fill variables0/2 ready

Values stay on this page and are never sent or saved.

flush ruleset

table inet filter {
  chain input {
    type filter hook input priority filter; policy drop;
    iifname "lo" accept
    ct state established,related accept
    ct state invalid drop
    ip protocol icmp accept
    ip6 nexthdr ipv6-icmp accept
    ip saddr {{managementCidr}} tcp dport {{sshPort}} accept
    tcp dport { 80, 443 } accept
    limit rate 5/second log prefix "nft-input-drop: "
  }
  chain forward { type filter hook forward priority filter; policy drop; }
  chain output { type filter hook output priority filter; policy accept; }
}
Example output / evidence
Persistent ruleset saved

Checkpoint: Perform a line-by-line policy review

Continue whenVariables are replaced, management source and port are exact, required public ports are present, and no competing manager depends on the flushed tables.

Stop whenA variable is unresolved, IPv6 intent is unknown, or the host forwards traffic.

Security notes

  • flush ruleset is destructive to tables created by Docker, Kubernetes or other managers.

Alternatives

  • Manage an isolated table without flushing the global ruleset when another system owns other tables.

Stop conditions

  • Never copy this complete baseline unchanged onto a multi-manager or routing host.
04

verification

Validate without applying

read-only

A parse error must stop the change.

Why this step matters

Dry parsing is the mandatory safety gate between editing and applying; a malformed batch must not touch the live policy.

What to understand

nft --check evaluates syntax and object references without committing the transaction.

Success is intentionally silent. Treat exit code 0, not a particular printed sentence, as the evidence.

System changes

  • No persistent or live firewall changes.

Syntax explained

--check
Validates commands without applying them.
--file
Reads the complete candidate from the named file.
Command
sudo nft --check --file /etc/nftables.conf
Example output / evidence
No output; exit code 0

Checkpoint: Require a clean exit

sudo nft --check --file /etc/nftables.conf; echo "exit=$?"

Continue whenNo parser error and exit=0.

Stop whenAny diagnostic is printed or the exit code is non-zero.

Security notes

  • A valid file can still implement the wrong policy; continue with rollback and external tests.

Alternatives

  • Validate a generated file in CI as an additional gate, not a replacement for host-version validation.

Stop conditions

  • Never use nft -f as a syntax test on a remote production host.
05

warning

Schedule a temporary rollback

caution

Before applying remotely, arrange restoration from a separate session or timed unit.

Why this step matters

A timed rollback limits a remote lockout even when the candidate is syntactically valid but the management allow rule is wrong.

What to understand

systemd-run creates a transient timer and service. After five minutes, the service loads /root/nftables-before.nft unless the timer is explicitly stopped after successful testing.

The rollback file must already exist and parse. Keep console access because a timer is an additional safety layer, not a guarantee against systemd or filesystem failure.

System changes

  • Creates transient nft-rollback.timer and nft-rollback.service units scheduled for five minutes.

Syntax explained

--unit=nft-rollback
Names the transient service and timer for clear inspection and cancellation.
--on-active=5m
Schedules execution five minutes after creation.
/usr/sbin/nft -f
Loads the captured known-good ruleset when the timer fires.
Command
sudo systemd-run --unit=nft-rollback --on-active=5m /usr/sbin/nft -f /root/nftables-before.nft
Example output / evidence
Running timer as unit: nft-rollback.timer

Checkpoint: Verify rollback is armed

systemctl list-timers nft-rollback.timer --no-pager

Continue whenThe timer is listed with a future activation time.

Stop whenThe timer is absent, failed, or references an unreadable backup.

Security notes

  • Do not cancel the timer from the original session before a new external session proves access.

Alternatives

  • Use a provider console or out-of-band automation that can restore the policy independently.

Stop conditions

  • Do not apply from a remote-only session unless both timed and console recovery are available.
06

command

Apply atomically

danger

Load the validated file and keep the original connection open.

Why this step matters

Load the already validated complete batch in one transaction, then inspect the kernel's effective rules while the old SSH session and rollback timer remain available.

What to understand

nft --file commits the complete candidate atomically or reports a failure. The chained list reads back what the kernel accepted.

Do not edit again between check and apply. If the file changes, repeat validation and confirm the rollback timer still points to the known-good capture.

System changes

  • Replaces the active nftables ruleset and may immediately alter all host network reachability.

Syntax explained

nft --file
Loads the full candidate as an nft transaction.
&&
Lists the active policy only after a successful load.
nft list ruleset
Reads back the complete effective kernel ruleset.
Command
sudo nft --file /etc/nftables.conf && sudo nft list ruleset
Example output / evidence
table inet filter { ... policy drop; ... }

Checkpoint: Inspect the effective input chain

sudo nft -a list chain inet filter input

Continue whenPolicy drop and the reviewed loopback, state, ICMP, management and service accepts are present.

Stop whenAny required rule is missing or the active chain differs from the validated file.

If this step fails

The apply command succeeds but a required service becomes unreachable.

Likely causeThe valid ruleset omitted the real address family, port, interface or source.

Safe checks
  • Keep the original session open
  • Inspect counters and rate-limited logs
  • Trigger sudo nft -f /root/nftables-before.nft if recovery is needed

ResolutionRestore first, then correct the inventory and candidate offline before another attempt.

Security notes

  • This is a danger step because flush ruleset removes all current nftables state.

Alternatives

  • Apply on a staging clone first, or update a dedicated table without a global flush when architecture permits.

Stop conditions

  • Trigger rollback immediately if administration or a critical health check fails.
07

decision

Test allowed and denied paths

read-only

Open a second SSH session and probe published ports externally.

Why this step matters

Tests from another host exercise the real packet path; localhost and the retained SSH session can both survive a policy that blocks every new remote connection.

What to understand

Probe SSH from the approved management CIDR and HTTPS from the public perspective. Also test at least one deliberately closed port and both address families where DNS publishes them.

A successful TCP connect proves the firewall path and listener, not application correctness. Follow with the service's own health request.

System changes

  • No persistent changes; creates short external test connections and updates rule counters.

Syntax explained

nc -v
Prints the connection result.
-z
Performs a connect scan without sending application data.
&&
Runs the HTTPS port probe only after the SSH probe succeeds.
Command
Fill variables0/2 ready

Values stay on this page and are never sent or saved.

nc -vz {{host}} {{sshPort}} && nc -vz {{host}} 443
Example output / evidence
Connection to host 22 port [tcp/ssh] succeeded!

Checkpoint: Prove allowed and denied paths

nc -vz {{host}} {{sshPort}} && nc -vz {{host}} 443

Continue whenApproved ports connect from their intended sources; an unapproved test port times out or is refused.

Stop whenNew SSH fails, a required service fails, or an unapproved port is reachable.

Security notes

  • Run controlled probes only against the host you administer.

Alternatives

  • Use curl or a protocol-specific health check after the TCP probe.

Stop conditions

  • Keep the rollback timer armed until every critical external test passes.
08

command

Enable persistence

caution

Enable nftables only when active and on-disk rules match.

Why this step matters

Persistence is safe only after live policy tests pass; cancel the temporary rollback last so the verified file becomes the boot-time source of truth.

What to understand

enable --now starts or confirms nftables and creates boot-time links. The active check provides immediate service evidence.

Stopping nft-rollback.timer prevents the known-good policy from unexpectedly replacing the new policy after approval. Preserve the backup for manual recovery.

System changes

  • Enables nftables.service for boot and cancels the temporary rollback timer after successful validation.

Syntax explained

systemctl enable --now nftables
Enables boot loading and starts the service immediately.
systemctl is-active nftables
Confirms systemd sees the firewall service as active.
systemctl stop nft-rollback.timer
Cancels the temporary automatic restore after all tests pass.
Command
sudo systemctl enable --now nftables && systemctl is-active nftables && sudo systemctl stop nft-rollback.timer
Example output / evidence
active
Rollback timer cancelled after successful external tests

Checkpoint: Confirm persistence and timer cancellation

systemctl is-enabled nftables && systemctl is-active nftables && ! systemctl is-active --quiet nft-rollback.timer

Continue whennftables is enabled and active; the rollback timer is no longer active.

Stop whenThe service is not enabled, its journal contains a load error, or tests were not completed before timer cancellation.

Security notes

  • Keep the known-good backup even after cancelling the timer.

Alternatives

  • Manage /etc/nftables.conf through reviewed configuration management with the same preflight and recovery gates.

Stop conditions

  • Do not cancel rollback based only on the original SSH session remaining connected.

Finish line

Verification checklist

Syntaxsudo nft --check --file /etc/nftables.confExit code 0.
Persistencesystemctl is-enabled nftablesReturns enabled.
Input policysudo nft list chain inet filter inputShows policy drop and intended accepts.

Recovery guidance

Common problems and safe checks

nft --check reports an unexpected token or unknown identifier.

Likely causeA missing brace or semicolon, an undefined variable, or syntax unsupported by the installed nft version.

Safe checks
  • sudo nft --check --file /etc/nftables.conf
  • nft --version
  • sudo nft --debug=parser --check --file /etc/nftables.conf

ResolutionCorrect the candidate file and repeat dry validation. Do not apply only the lines that happened to parse.

The original SSH session stays open but a new SSH connection times out.

Likely causeThe management source CIDR, interface, address family or SSH port in the accept rule does not match the test client.

Safe checks
  • Use the retained session to run sudo nft -a list chain inet filter input
  • Run sudo ss -lntp and ip address
  • Inspect rate-limited nft-input-drop journal messages

ResolutionRestore the saved ruleset or correct the narrow management rule, validate, apply and repeat from the real source network.

HTTPS is allowed but the application remains unreachable.

Likely causeThe service is not listening, is bound only to another address, an upstream firewall blocks it, or the request uses IPv6 while only IPv4 was checked.

Safe checks
  • sudo ss -lntp
  • curl --fail --include http://127.0.0.1/
  • getent ahosts host.example.com
  • sudo nft list counters

ResolutionFix the service or upstream network boundary rather than broadening the host firewall without evidence.

The rules work now but disappear or change after reboot.

Likely causenftables.service is disabled, /etc/nftables.conf differs from the active ruleset, or another service reloads firewall state.

Safe checks
  • systemctl is-enabled nftables
  • systemctl status nftables --no-pager
  • sudo journalctl -u nftables -b --no-pager

ResolutionMake the validated persistent file authoritative, enable nftables, disable competing managers and perform a controlled reboot test.

After the procedure

Alternatives and next steps

Consider these alternatives

  • Use UFW for a simpler Ubuntu-managed host policy when its abstraction covers every required rule and it remains the sole manager.
  • Use sets for large or frequently changing address/port allowlists instead of generating many nearly identical rules.
  • Apply policy first in a disposable staging host that mirrors network interfaces and services when console recovery is unavailable in production.

Operate it safely

  • Export rule counters and rate-limited drop events to monitoring, with alerts that distinguish scanning from legitimate service failures.
  • Replace literal management networks and service ports with reviewed sets when the policy grows.
  • Test the persistent rules during a maintenance-window reboot while console recovery remains available.
  • Add separate, documented policies for containers, VPN forwarding or NAT rather than weakening the host baseline.

Reference

Frequently asked questions

Why allow ICMP?

ICMP carries diagnostics and network control, including IPv6 neighbor discovery and path-MTU behavior. Blocking it indiscriminately can break normal connectivity.

Does nft --check guarantee I will not lose SSH?

No. It proves syntax, not policy intent. The backup, rollback timer, retained session and external positive tests protect against a syntactically valid lockout.

Why is the forward chain set to drop?

This baseline protects a non-routing host. Traffic transiting a VPN gateway or container host needs explicit forwarding rules in a separate design.

Recovery

Rollback

Load the saved rules from console or the scheduled fail-safe.

  1. Run sudo nft -f /root/nftables-before.nft.
  2. Restore /etc/nftables.conf.bak.
  3. Verify SSH and service reachability.
  4. Cancel the rollback timer only after the replacement is confirmed.

Evidence

Sources and review

Verified 2026-07-24Review due 2026-10-22
Ubuntu firewall documentationofficialnftables wikiofficialnft manualofficial