This is unreleased documentation for SUSE® Virtualization v1.9 (Dev).

Host Network Security

By default, SUSE Virtualization nodes do not filter inbound traffic, allowing any host that can reach the management network to connect to any open port. To reduce the attack surface, you can apply iptables rules to restrict inbound traffic to only the ports required for cluster operations.

For the full list of required ports by node role, see Network requirements.

Applying iptables rules

The examples in this section use a custom iptables chain (HARVESTER_INPUT) attached to the INPUT hook on the management bridge (mgmt-br). The rule set follows this structure:

  • Established connections: Allow return traffic for connections already tracked by conntrack.

  • Protocol rules: Allow ICMP, DHCP, and other protocol-level traffic.

  • Service-specific ACCEPT rules: Allow traffic to ports required by the node role.

  • LOG + DROP: Log and drop all remaining traffic.

When using Host Network Configuration, you must create an additional rule for the new network interface:

iptables -A INPUT -i <interface_name> -j HARVESTER_INPUT
# Create the custom chain and attach it to the INPUT hook
iptables -N HARVESTER_INPUT
iptables -A INPUT -i mgmt-br -j HARVESTER_INPUT

iptables -A HARVESTER_INPUT -m conntrack --ctstate ESTABLISHED,RELATED \
  -m comment --comment "Accept return traffic from established connections" -j ACCEPT
iptables -A HARVESTER_INPUT -p icmp \
  -m comment --comment "ICMP" -j ACCEPT
iptables -A HARVESTER_INPUT -s 10.52.0.0/16 \
  -m comment --comment "Allow pod network return traffic" -j ACCEPT

# ... role-specific ACCEPT rules ...
iptables -A HARVESTER_INPUT -m limit --limit 10/min \
  -m comment --comment "Rate-limit logging of dropped packets" \
  -j LOG --log-prefix HARVESTER_DROP_ --log-level 4
iptables -A HARVESTER_INPUT -m comment --comment "Drop all other traffic" -j DROP

If you apply these commands manually, use iptables -L INPUT -v -n --line-numbers to verify that HARVESTER_INPUT appears as the last entry in the INPUT chain:

harvester1:/home/rancher # iptables -L INPUT -v -n --line-numbers
# Warning: iptables-legacy tables present, use iptables-legacy to see them
Chain INPUT (policy ACCEPT xxx packets, xxxx bytes)
num   pkts bytes target     prot opt in     out     source               destination
1     301K  140M cali-INPUT  all  --  *      *       0.0.0.0/0            0.0.0.0/0            /* cali:Cz_u1IQiXIMmKD4c */
2        0     0 ACCEPT     all  --  *      *       0.0.0.0/0            0.0.0.0/0            match-set ovn40services dst
3        0     0 ACCEPT     all  --  *      *       0.0.0.0/0            0.0.0.0/0            match-set ovn40services src
4        0     0 ACCEPT     all  --  *      *       0.0.0.0/0            0.0.0.0/0            match-set ovn40subnets dst
5        0     0 ACCEPT     all  --  *      *       0.0.0.0/0            0.0.0.0/0            match-set ovn40subnets src
6        0     0 ACCEPT     udp  --  *      *       0.0.0.0/0            0.0.0.0/0            udp dpt:6081
7     300K  535M KUBE-PROXY-FIREWALL  all  --  *      *       0.0.0.0/0            0.0.0.0/0            ctstate NEW /* kubernetes load balancer firewall */
8    2391K 1748M KUBE-NODEPORTS  all  --  *      *       0.0.0.0/0            0.0.0.0/0            /* kubernetes health check service ports */
9     300K  535M KUBE-EXTERNAL-SERVICES  all  --  *      *       0.0.0.0/0            0.0.0.0/0            ctstate NEW /* kubernetes externally-visible service portals */
10   2421K 1796M KUBE-FIREWALL  all  --  *      *       0.0.0.0/0            0.0.0.0/0
11   49274   19M HARVESTER_INPUT  all  --  mgmt-br *       0.0.0.0/0            0.0.0.0/0

The pod CIDR 10.52.0.0/16 is the default for SUSE Virtualization. Replace this value with your cluster’s actual pod CIDR if it differs. This rule reduces false positives from pod-to-node return traffic and is safe to omit if not required.

Debugging and verification

Monitoring INPUT traffic

The HARVESTER_INPUT chain manages incoming management traffic. To inspect rule counters (packets and bytes) and identify active traffic patterns on the node, list all rules by running the following command:

iptables -L HARVESTER_INPUT -v -n --line-numbers

Monitoring dropped packets

The LOG rule records packets that do not match any ACCEPT rule. To monitor dropped packets in real time, run the following command:

journalctl -k -f | grep HARVESTER_DROP

# from the last 5 minutes:
journalctl -k --since "5 minutes ago" | grep HARVESTER_DROP

Key fields in a log entry:

Field Description

IN=mgmt-br

Indicates the packet arrived on the management bridge

SRC / DST

Source and destination IP addresses

PROTO

Protocol (TCP or UDP)

SPT / DPT

Source and destination ports (Use DPT to identify which port requires a missing ACCEPT rule.)

False positives

After applying rules to a running node, some log entries may appear briefly and then stop. These are false positives caused by lost conntrack state. When you apply new rules, active connection states are reset. As a result, return traffic from pre-existing connections no longer matches the ESTABLISHED,RELATED rule and matches the DROP rule instead. These log entries are harmless and resolve automatically as connections are re-established.

A false positive typically exhibits the following characteristics:

  • TCP flags are ACK, ACK PSH, or ACK FIN (not SYN).

  • SPT is a known service port (for example, 6443, 9345, or 10250).

  • DPT is a high ephemeral port (above 1024).

  • Log entries stop within a few minutes.

Example:

Apr 22 10:00:01 harvester3 kernel: HARVESTER_DROP_IN=mgmt-br OUT= MAC=... SRC=192.168.122.61 DST=192.168.122.63 LEN=52 PROTO=TCP SPT=9345 DPT=46002 WINDOW=521 RES=0x00 ACK FIN URGP=0

In this example:

  • SPT=9345: RKE2 supervisor API

  • DPT=46002: Ephemeral port

  • ACK FIN: Normal connection teardown (not an incoming blocked SYN connection request)

Because this represents a connection teardown, it is a false positive and can be ignored.

If log entries persist with SYN flags or an unrecognized DPT, a required service port is likely missing. Identify the port and add the corresponding ACCEPT rule.

Persisting rules with CloudInit CRD

Custom iptables rules do not persist across reboots. To re-apply your rules automatically at boot time, use a CloudInit CRD.

Configuring the rules during the boot stage ensures they take effect before the node becomes fully operational, preventing initial exposure and avoiding conntrack state loss.

Include cleanup commands at the beginning of your script to prevent duplicate chain entries or conflicting rules when the node reboots.

apiVersion: node.harvesterhci.io/v1beta1
kind: CloudInit
metadata:
  name: iptables-control-plane
spec:
  matchSelector:
    node-role.kubernetes.io/control-plane: "true"
    node-role.kubernetes.io/etcd: "true"
  filename: 91-iptables-control-plane
  contents: |
    stages:
      boot:
        - name: "Apply iptables rules for control-plane node"
          commands:
            # Clean up any existing chain from a previous boot
            - iptables -D INPUT -i mgmt-br -j HARVESTER_INPUT 2>/dev/null || true
            - iptables -F HARVESTER_INPUT 2>/dev/null || true
            - iptables -X HARVESTER_INPUT 2>/dev/null || true
            # Create the chain and attach it
            - iptables -N HARVESTER_INPUT
            - iptables -A INPUT -i mgmt-br -j HARVESTER_INPUT
            # Allow established connections and ICMP
            - iptables -A HARVESTER_INPUT -m conntrack --ctstate ESTABLISHED,RELATED -m comment --comment "Accept return traffic from established connections" -j ACCEPT
            - iptables -A HARVESTER_INPUT -p icmp -m comment --comment "ICMP" -j ACCEPT
            - iptables -A HARVESTER_INPUT -s 10.52.0.0/16 -m comment --comment "Allow pod network return traffic" -j ACCEPT
            # ... role-specific ACCEPT rules ...
            # Log and drop everything else
            - iptables -A HARVESTER_INPUT -m limit --limit 10/min -m comment --comment "Rate-limit logging of dropped packets" -j LOG --log-prefix HARVESTER_DROP_ --log-level 4
            - iptables -A HARVESTER_INPUT -m comment --comment "Drop all other traffic" -j DROP
  paused: false

Nodes must be rebooted after creating or updating a CloudInit resource for the changes to take effect. To apply rules immediately without rebooting, run the commands manually on each host via SSH.

Removing the iptables rules

  1. Delete the CloudInit resource.

  2. Run the following commands on each host to remove the rules and flush the custom chain from the active system:

iptables -D INPUT -i mgmt-br -j HARVESTER_INPUT 2>/dev/null || true
iptables -F HARVESTER_INPUT 2>/dev/null || true
iptables -X HARVESTER_INPUT 2>/dev/null || true

Role-specific CloudInit resources

Create a separate CloudInit resource for each node role in your cluster. If you have not explicitly assigned a role in the SUSE Virtualization configuration, the node defaults to control-plane and you can apply the example in the following section.

Ensure that the spec.matchSelector for each CloudInit resource is mutually exclusive. If a node matches more than one iptables-related CloudInit resource, the custom chain is created multiple times, resulting in duplicate rules and unpredictable behavior. Before applying these examples, verify that no other CloudInit resources in your cluster have overlapping selectors.

The following examples include ports for the kubeovn-operator and rancher-monitoring add-ons. You can safely remove these rules if you are not using these add-ons. For more information, see Port requirements for add-ons.

Control plane nodes

apiVersion: node.harvesterhci.io/v1beta1
kind: CloudInit
metadata:
  name: iptables-control-plane
spec:
  matchSelector:
    node-role.kubernetes.io/control-plane: "true"
    node-role.kubernetes.io/etcd: "true"
  filename: 91-iptables-control-plane
  contents: |
    stages:
      boot:
        - name: "Apply iptables rules for control-plane node"
          commands:
            - iptables -D INPUT -i mgmt-br -j HARVESTER_INPUT 2>/dev/null || true
            - iptables -F HARVESTER_INPUT 2>/dev/null || true
            - iptables -X HARVESTER_INPUT 2>/dev/null || true
            - iptables -N HARVESTER_INPUT
            - iptables -A INPUT -i mgmt-br -j HARVESTER_INPUT
            - iptables -A HARVESTER_INPUT -m conntrack --ctstate ESTABLISHED,RELATED -m comment --comment "Accept return traffic from established connections" -j ACCEPT
            - iptables -A HARVESTER_INPUT -p icmp -m comment --comment "ICMP (ping, MTU discovery, etc.)" -j ACCEPT
            - iptables -A HARVESTER_INPUT -s 10.52.0.0/16 -m comment --comment "Allow pod network return traffic" -j ACCEPT
            - iptables -A HARVESTER_INPUT -p udp --sport 67 --dport 68 -m comment --comment "DHCP server reply" -j ACCEPT
            - iptables -A HARVESTER_INPUT -p udp --sport 68 --dport 67 -m comment --comment "DHCP client request" -j ACCEPT
            - iptables -A HARVESTER_INPUT -p tcp --dport 22 -m comment --comment "SSH" -j ACCEPT
            - iptables -A HARVESTER_INPUT -p tcp --dport 80 -m comment --comment "Harvester UI HTTP" -j ACCEPT
            - iptables -A HARVESTER_INPUT -p tcp --dport 443 -m comment --comment "Harvester UI HTTPS" -j ACCEPT
            - iptables -A HARVESTER_INPUT -p tcp --dport 2112 -m comment --comment "kube-vip Prometheus metrics" -j ACCEPT
            - iptables -A HARVESTER_INPUT -p tcp --dport 2379:2380 -m comment --comment "etcd client/peer" -j ACCEPT
            - iptables -A HARVESTER_INPUT -p tcp --dport 6443 -m comment --comment "Kubernetes API server" -j ACCEPT
            - iptables -A HARVESTER_INPUT -p tcp --dport 9091 -m comment --comment "calico-node metrics (Prometheus)" -j ACCEPT
            - iptables -A HARVESTER_INPUT -p tcp --dport 9345 -m comment --comment "RKE2 supervisor API" -j ACCEPT
            - iptables -A HARVESTER_INPUT -p tcp --dport 9796 -m comment --comment "Prometheus node-exporter metrics (rancher-monitoring)" -j ACCEPT
            - iptables -A HARVESTER_INPUT -p tcp --dport 10250 -m comment --comment "kubelet API" -j ACCEPT
            - iptables -A HARVESTER_INPUT -p tcp --dport 30000:32767 -m comment --comment "NodePort TCP range" -j ACCEPT
            - iptables -A HARVESTER_INPUT -p udp --dport 30000:32767 -m comment --comment "NodePort UDP range" -j ACCEPT
            - iptables -A HARVESTER_INPUT -p udp --dport 8472 -m comment --comment "VXLAN (Flannel/Canal)" -j ACCEPT
            - iptables -A HARVESTER_INPUT -p tcp --dport 8080 -m comment --comment "kube-ovn-webhook HTTP (kubeovn-operator)" -j ACCEPT
            - iptables -A HARVESTER_INPUT -p tcp --dport 8443 -m comment --comment "kube-ovn-webhook HTTPS (kubeovn-operator)" -j ACCEPT
            - iptables -A HARVESTER_INPUT -p tcp --dport 6641:6644 -m comment --comment "OVN NB/SB DB, Northd, and Raft (kubeovn-operator)" -j ACCEPT
            - iptables -A HARVESTER_INPUT -p tcp --dport 10661 -m comment --comment "kube-ovn-monitor metrics (kubeovn-operator)" -j ACCEPT
            - iptables -A HARVESTER_INPUT -p tcp --dport 10665 -m comment --comment "kube-ovn-daemon (kubeovn-operator)" -j ACCEPT
            - iptables -A HARVESTER_INPUT -p udp --dport 4789 -m comment --comment "VXLAN (Kube-OVN, kubeovn-operator)" -j ACCEPT
            - iptables -A HARVESTER_INPUT -m limit --limit 10/min -m comment --comment "Rate-limit logging of dropped packets" -j LOG --log-prefix HARVESTER_DROP_ --log-level 4
            - iptables -A HARVESTER_INPUT -m comment --comment "Drop all other traffic" -j DROP
  paused: false

Worker nodes

apiVersion: node.harvesterhci.io/v1beta1
kind: CloudInit
metadata:
  name: iptables-worker
spec:
  matchSelector:
    node-role.harvesterhci.io/worker: "true"
  filename: 91-iptables-worker
  contents: |
    stages:
      boot:
        - name: "Apply iptables rules for worker node"
          commands:
            - iptables -D INPUT -i mgmt-br -j HARVESTER_INPUT 2>/dev/null || true
            - iptables -F HARVESTER_INPUT 2>/dev/null || true
            - iptables -X HARVESTER_INPUT 2>/dev/null || true
            - iptables -N HARVESTER_INPUT
            - iptables -A INPUT -i mgmt-br -j HARVESTER_INPUT
            - iptables -A HARVESTER_INPUT -m conntrack --ctstate ESTABLISHED,RELATED -m comment --comment "Accept return traffic from established connections" -j ACCEPT
            - iptables -A HARVESTER_INPUT -p icmp -m comment --comment "ICMP (ping, MTU discovery, etc.)" -j ACCEPT
            - iptables -A HARVESTER_INPUT -s 10.52.0.0/16 -m comment --comment "Allow pod network return traffic" -j ACCEPT
            - iptables -A HARVESTER_INPUT -p udp --sport 67 --dport 68 -m comment --comment "DHCP server reply" -j ACCEPT
            - iptables -A HARVESTER_INPUT -p udp --sport 68 --dport 67 -m comment --comment "DHCP client request" -j ACCEPT
            - iptables -A HARVESTER_INPUT -p tcp --dport 22 -m comment --comment "SSH" -j ACCEPT
            - iptables -A HARVESTER_INPUT -p tcp --dport 80 -m comment --comment "Harvester UI HTTP" -j ACCEPT
            - iptables -A HARVESTER_INPUT -p tcp --dport 443 -m comment --comment "Harvester UI HTTPS" -j ACCEPT
            - iptables -A HARVESTER_INPUT -p tcp --dport 9091 -m comment --comment "calico-node metrics (Prometheus)" -j ACCEPT
            - iptables -A HARVESTER_INPUT -p tcp --dport 9796 -m comment --comment "Prometheus node-exporter metrics (rancher-monitoring)" -j ACCEPT
            - iptables -A HARVESTER_INPUT -p tcp --dport 10250 -m comment --comment "kubelet API" -j ACCEPT
            - iptables -A HARVESTER_INPUT -p tcp --dport 30000:32767 -m comment --comment "NodePort TCP range" -j ACCEPT
            - iptables -A HARVESTER_INPUT -p udp --dport 30000:32767 -m comment --comment "NodePort UDP range" -j ACCEPT
            - iptables -A HARVESTER_INPUT -p udp --dport 8472 -m comment --comment "VXLAN (Flannel/Canal)" -j ACCEPT
            - iptables -A HARVESTER_INPUT -p tcp --dport 8080 -m comment --comment "kube-ovn-webhook HTTP (kubeovn-operator)" -j ACCEPT
            - iptables -A HARVESTER_INPUT -p tcp --dport 8443 -m comment --comment "kube-ovn-webhook HTTPS (kubeovn-operator)" -j ACCEPT
            - iptables -A HARVESTER_INPUT -p tcp --dport 10660 -m comment --comment "kube-ovn-controller (kubeovn-operator)" -j ACCEPT
            - iptables -A HARVESTER_INPUT -p tcp --dport 10665 -m comment --comment "kube-ovn-daemon (kubeovn-operator)" -j ACCEPT
            - iptables -A HARVESTER_INPUT -p udp --dport 4789 -m comment --comment "VXLAN (Kube-OVN, kubeovn-operator)" -j ACCEPT
            - iptables -A HARVESTER_INPUT -m limit --limit 10/min -m comment --comment "Rate-limit logging of dropped packets" -j LOG --log-prefix HARVESTER_DROP_ --log-level 4
            - iptables -A HARVESTER_INPUT -m comment --comment "Drop all other traffic" -j DROP
  paused: false

Witness node

apiVersion: node.harvesterhci.io/v1beta1
kind: CloudInit
metadata:
  name: iptables-witness
spec:
  matchSelector:
    node-role.harvesterhci.io/witness: "true"
  filename: 91-iptables-witness
  contents: |
    stages:
      boot:
        - name: "Apply iptables rules for witness node"
          commands:
            - iptables -D INPUT -i mgmt-br -j HARVESTER_INPUT 2>/dev/null || true
            - iptables -F HARVESTER_INPUT 2>/dev/null || true
            - iptables -X HARVESTER_INPUT 2>/dev/null || true
            - iptables -N HARVESTER_INPUT
            - iptables -A INPUT -i mgmt-br -j HARVESTER_INPUT
            - iptables -A HARVESTER_INPUT -m conntrack --ctstate ESTABLISHED,RELATED -m comment --comment "Accept return traffic from established connections" -j ACCEPT
            - iptables -A HARVESTER_INPUT -p icmp -m comment --comment "ICMP (ping, MTU discovery, etc.)" -j ACCEPT
            - iptables -A HARVESTER_INPUT -s 10.52.0.0/16 -m comment --comment "Allow pod network return traffic" -j ACCEPT
            - iptables -A HARVESTER_INPUT -p udp --sport 67 --dport 68 -m comment --comment "DHCP server reply" -j ACCEPT
            - iptables -A HARVESTER_INPUT -p udp --sport 68 --dport 67 -m comment --comment "DHCP client request" -j ACCEPT
            - iptables -A HARVESTER_INPUT -p tcp --dport 22 -m comment --comment "SSH" -j ACCEPT
            - iptables -A HARVESTER_INPUT -p tcp --dport 2379:2380 -m comment --comment "etcd client/peer" -j ACCEPT
            - iptables -A HARVESTER_INPUT -p tcp --dport 9091 -m comment --comment "calico-node metrics (Prometheus)" -j ACCEPT
            - iptables -A HARVESTER_INPUT -p tcp --dport 9345 -m comment --comment "RKE2 supervisor API" -j ACCEPT
            - iptables -A HARVESTER_INPUT -p tcp --dport 9796 -m comment --comment "Prometheus node-exporter metrics (rancher-monitoring)" -j ACCEPT
            - iptables -A HARVESTER_INPUT -p tcp --dport 10250 -m comment --comment "kubelet API" -j ACCEPT
            - iptables -A HARVESTER_INPUT -p udp --dport 8472 -m comment --comment "VXLAN (Flannel/Canal)" -j ACCEPT
            - iptables -A HARVESTER_INPUT -p tcp --dport 10665 -m comment --comment "kube-ovn-daemon (kubeovn-operator)" -j ACCEPT
            - iptables -A HARVESTER_INPUT -p udp --dport 4789 -m comment --comment "VXLAN (Kube-OVN, kubeovn-operator)" -j ACCEPT
            - iptables -A HARVESTER_INPUT -m limit --limit 10/min -m comment --comment "Rate-limit logging of dropped packets" -j LOG --log-prefix HARVESTER_DROP_ --log-level 4
            - iptables -A HARVESTER_INPUT -m comment --comment "Drop all other traffic" -j DROP
  paused: false

Upgrades

False positives during upgrades

SUSE Virtualization restarts multiple components during a cluster upgrade. Each restart clears conntrack state for affected connections, causing return traffic to temporarily trigger the DROP rule (the same false positive mechanism described in False positives), but occurring repeatedly throughout the upgrade process.

You may see log entries similar to the following:

May 05 04:40:28 harvester1 kernel: HARVESTER_DROP_IN=mgmt-br ... SRC=192.168.122.62 DST=192.168.122.61 ... PROTO=TCP SPT=10250 DPT=56266 ... ACK RST URGP=0
May 05 04:43:35 harvester2 kernel: HARVESTER_DROP_IN=mgmt-br ... SRC=192.168.122.61 DST=192.168.122.62 ... PROTO=TCP SPT=6443 DPT=43707 ... ACK URGP=0

Both entries are false positives:

  • SPT=10250 ACK RST: kubelet restarted and reset an existing connection. The RST packet no longer matches an active conntrack state.

  • SPT=6443 ACK: The Kubernetes API server restarted. Return traffic from pre-restart connections lost its conntrack state.

These log entries may continue for the duration of the upgrade and stop once all components stabilize. This is expected behavior and does not indicate missing rules.

If log entries with SYN flags or unrecognized DPT values persist after the upgrade completes, a required service port may be missing an ACCEPT rule.

Network issues during upgrades

If network issues persist during an upgrade, temporarily remove the custom chain before upgrading, and then re-apply it after the upgrade completes. For more information, see Removing the iptables rules.