Debian GNU/Linux · Network

Disable IP forwarding unless the system is a router

When it fails: The system forwards packets between networks. A host that forwards packets can be used to reach networks that should be separated from the one the attacker is on.

CB-DEB-NET-009mediumactiveautomatedautomated fixprofile core

As released in v0.1.0 beta · pre-release. Current state.

Description

With IP forwarding on, the kernel routes packets received on one interface to another, like a router. It is controlled by net.ipv4.ip_forward (an alias of net.ipv4.conf.all.forwarding), net.ipv4.conf.default.forwarding for interfaces created later, and net.ipv6.conf.all.forwarding / net.ipv6.conf.default.forwarding for IPv6. Debian ships forwarding off. This control checks all five values against the ip_forward parameter.

Writing net.ipv4.ip_forward also resets several per-interface settings (the kernel documentation calls it special), so the fix writes it first. IPv6 checks only run when the IPv6 stack is present.

Rationale

A host that is not meant to route should never relay traffic: forwarding lets an attacker who controls one of its networks reach the others through it, bypassing network filtering.

Impact

Hosts that legitimately route need forwarding: routers, VPN gateways, and hosts running containers or virtual machines with NAT (Docker, libvirt, LXC turn it on at start). On those, set the ip_forward parameter to 1 in the tailoring file, and filter forwarded traffic in the firewall (CB-DEB-NET-006).

Setting

Locationsysctl `net.ipv4.ip_forward`, `net.ipv4.conf.{all,default}.forwarding`, `net.ipv6.conf.{all,default}.forwarding`, persisted in `/etc/sysctl.d/`
Default0
Recommended{{ ip_forward }} (0 except on routers)

Parameters

ParameterCoreServerServer hardenedDesktopDesktop hardenedBounds
ip_forwardinteger. Expected forwarding state, 0 for a host, 1 for a router or container host.000000 to 1

The value used is your override (tailoring file), else the profile's, else the one of the profile it extends, else the default. Bounds limit what an override may set.

Audit

Run sysctl net.ipv4.ip_forward net.ipv4.conf.all.forwarding net.ipv4.conf.default.forwarding net.ipv6.conf.all.forwarding net.ipv6.conf.default.forwarding. Every value must be {{ ip_forward }}. Then check that no file in /etc/sysctl.d/, /run/sysctl.d/, /usr/lib/sysctl.d/ or /etc/sysctl.conf sets another value.

Automated: every check must pass. Scanners report each check by its id.

ipv4-ip-forward

net.ipv4.ip_forward.

key: net.ipv4.ip_forward
value: '{{ ip_forward }}'

ipv4-all

net.ipv4.conf.all.forwarding.

key: net.ipv4.conf.all.forwarding
value: '{{ ip_forward }}'

ipv4-default

net.ipv4.conf.default.forwarding.

key: net.ipv4.conf.default.forwarding
value: '{{ ip_forward }}'

ipv6-all

net.ipv6.conf.all.forwarding.

key: net.ipv6.conf.all.forwarding
value: '{{ ip_forward }}'

ipv6-default

net.ipv6.conf.default.forwarding.

key: net.ipv6.conf.default.forwarding
value: '{{ ip_forward }}'

Remediation

Add to /etc/sysctl.d/60-cirsec.conf, net.ipv4.ip_forward first:

net.ipv4.ip_forward = {{ ip_forward }}
net.ipv4.conf.all.forwarding = {{ ip_forward }}
net.ipv4.conf.default.forwarding = {{ ip_forward }}
net.ipv6.conf.all.forwarding = {{ ip_forward }}
net.ipv6.conf.default.forwarding = {{ ip_forward }}

Remove other assignments of these keys, then run sysctl --system.

Restartnone
Disruptionlow

Rollback

Remove the lines from /etc/sysctl.d/60-cirsec.conf and set the previous values with sysctl -w.

ipv4-ip-forward

Generated from check ipv4-ip-forward: the engine writes the fix for every output, it sets the expected value and the check runs again after.

ipv4-all

Generated from check ipv4-all: the engine writes the fix for every output, it sets the expected value and the check runs again after.

ipv4-default

Generated from check ipv4-default: the engine writes the fix for every output, it sets the expected value and the check runs again after.

ipv6-all

Generated from check ipv6-all: the engine writes the fix for every output, it sets the expected value and the check runs again after.

ipv6-default

Generated from check ipv6-default: the engine writes the fix for every output, it sets the expected value and the check runs again after.

Each release turns these fixes into remediate.sh and an Ansible role per profile: dry run by default, a fix runs only when its check fails.

References

Mappings

FrameworkIDRelationNote
ANSSI-BP-028 - Recommandations de configuration d'un système GNU/Linux2.0R12partialR12 lists many IPv4 settings; this control covers forwarding.
NIST SP 800-53 Rev. 55.1.1CM-7related

How this control relates to the other framework's: equivalent, stricter, looser, partial or related.

Threats mitigated

People and lifecycle

Authors
Verified2026-10-18 by Sn0wAlice on 12, Proxmox VM 921, Debian 12 genericcloud image, kernel 6.1.0-54-cloud-amd64. Debian defaults pass; checks for IPv4 and IPv6 run, values kept after reboot.
Verified2026-10-18 by Sn0wAlice on 13, Proxmox VM 931, Debian 13 genericcloud image, kernel 6.12.113+deb13-cloud-amd64. Debian defaults pass; checks for IPv4 and IPv6 run, values kept after reboot.
Introduced inv0.1.0
Created, updated2026-10-18, 2026-10-18
Translationsfr