Masquerade uses OVN-K over eno2/br-ex — no bridge setup required
Test cases(24 of 50)
Complete prerequisites first ([PRE-01](PRE-01) → [PRE-08](PRE-08) minimum) before running these tests. Go to prerequisites
NOT POSSIBLE on SNO — live migration requires ≥2 nodes. Documented as an expected failure.
Test on local storage
Optimal for single-NIC — OVN-K over eno2/br-ex, no bridge setup required
✗ NOT TESTABLE ON HETZNER -- TOS violation! OVN-K localnet works technically (verified 30.06.2026: ping successful), but any VM MAC that reaches eno2/the physical switch via this interface is detected and reported by Hetzner as an unauthorised MAC (server suspension threatened). Only the registered server MAC is permitted.
This test works technically and has been demonstrated (NNCP Available, ping from external successful). HOWEVER: Hetzner, where my test cluster is running, sent a violation warning by email quite quickly after the test was started because the VM's own MAC address reached the physical switch via eno2. Hetzner permits only the registered server MAC.
CONCLUSION: direct physical LAN access with the VM's own MAC (OVN-K localnet/bridge binding) is NOT permitted on Hetzner bare metal without ordering an additional MAC or with a dedicated second NIC.
# ══════════════════════════════════════════════════════════════════════
# ⚠ HETZNER ToS VIOLATION -- DO NOT RUN ON HETZNER
# ══════════════════════════════════════════════════════════════════════
# ══════════════════════════════════════════════════════════════════════
# IMMEDIATE MEASURE (already performed on 30.06.2026)
# ══════════════════════════════════════════════════════════════════════
# 1. IP removed from affected interface in the VM:
# sudo ip addr del 95.216.44.55/26 dev enp2s0
# sudo ip link set enp2s0 down
#
# 2. VM fully stopped:
virtctl stop vm-lan-test -n vmtest
# 3. VM deleted (do not reuse with an active localnet interface!):
oc delete vm vm-lan-test -n vmtest --ignore-not-found
# 4. Clean up NAD and NNCP -- no one should accidentally attach
# a VM to them again:
oc delete networkattachmentdefinition lan-localnet -n vmtest --ignore-not-found
oc delete nncp br-ex-localnet-mapping --ignore-not-found
# ══════════════════════════════════════════════════════════════════════
# MAC AUDIT -- check all cluster VMs for further disallowed MACs
# ══════════════════════════════════════════════════════════════════════
# List all VMI interfaces with name, binding type, and MAC:
oc get vmi -A -o json | python3 -c "
import json, sys
data = json.load(sys.stdin)
for item in data['items']:
ns = item['metadata']['namespace']
name = item['metadata']['name']
spec_ifaces = item['spec']['domain']['devices'].get('interfaces', [])
status_ifaces = item.get('status', {}).get('interfaces', [])
for si in spec_ifaces:
binding = 'masquerade' if 'masquerade' in si else ('bridge' if 'bridge' in si else 'other')
mac = next((s['mac'] for s in status_ifaces if s['name'] == si['name']), 'unknown')
flag = 'RISK -- MAC leaves server!' if binding != 'masquerade' else 'OK (NAT, MAC stays internal)'
print(f'{ns}/{name}: {si["name"]} [{binding}] MAC={mac} {flag}')
"
# Alternative without Python (grep-based):
oc get vmi -A -o jsonpath='{range .items[*]}{.metadata.namespace}{"/"}{.metadata.name}{":
"}{range .status.interfaces[*]}{" "}{.name}{" -> "}{.mac}{" ("}{.interfaceName}{")
"}{end}{end}'
# Expected result after cleanup: ONLY masquerade interfaces (RISK flag should
# no longer appear anywhere). test-vm-rhel9 with masquerade is uncritical.
# ══════════════════════════════════════════════════════════════════════
# WHAT CAN BE TESTED INSTEAD (without ToS violation)
# ══════════════════════════════════════════════════════════════════════
# - TC-NET-001 (masquerade + Service/NodePort/LoadBalancer) -- uncritical,
# MAC stays behind NAT; Hetzner sees only the single server MAC
# - TC-MLB-001/002 (MetalLB) -- likewise via service layer; no
# separate VM MAC on the wire
# - For a "real" L2 network test: separate lab environment with its own
# switch (variant B from PRE-02/03) or ask Hetzner explicitly about
# additional MAC options (fee-based, not available on all products)✗ NOT TESTABLE — SR-IOV requires a dedicated second NIC (Intel X710, Mellanox ConnectX). The sole NIC eno2 is consumed by OVN-K and cannot be used for SR-IOV VFs.
Variant A uses pure software bridges br-net1/2/3 (port: []) — no VM MAC leaves eno2/the physical switch, no TOS risk. Variant B (on-premises, trunk port) is relevant only with your own switch.
purely internal software bridges br-net1/br-net2 (port: []) — no VM MAC leaves the node
UserDefinedNetwork with Layer2 topology is a pure overlay (encapsulated Geneve tunnel within OVN-K) — explicitly NOT localnet, so no VM MAC leaves the node.
⚠️ DEDICATED NAMESPACE REQUIRED: see NOTE below.
2 Pools: internal-pool binds to br-net1 — external-pool binds to eno2, requires a 2nd public IP
external-pool uses a provider-registered additional IP (e.g. Hetzner Additional IP); MetalLB speaker announces with the node's own MAC.
⚠️ SHARED RESOURCE: external-pool currently has only ONE IP — Clean up after this test (delete vm-ssh-lb) before starting TC-HCP-002
⚠️ NOT usable by default on the current SNO cluster: there is no customer-controlled router in the path for peering. Left for documentation purposes and future tests.
L2Advertisement explicitly binds to br-net1/br-net2 — ARP announcements stay node-internal.
Single-NIC OK — HyperShift pods use the OVN-K pod network.
⚠️ Check Pod/Node Capacity before starting TC-HCP-002 — see steps below.
⚠️ SNO EXAMPLE INSTALLATION: --control-plane-availability-policy SingleReplica is mandatory on a single-node cluster.
⚠️ Be sure enough compute resources are available on the SNO node before starting this test.
Import over OVN-K — bandwidth is shared with OCP traffic on eno2/br-ex in this cluster