Gateway NAT & Internet Access
A node connects your private fabric to the public internet. Tenant workloads sit on a private subnet whose addresses are not routable on the internet, while the node holds an uplink with a public IP. NAT rewrites addresses as traffic crosses that boundary, so a whole tenant subnet can reach the internet over one shared public IP and so you can publish internal services on it.
Translation happens in the packet path, in the same pass as the firewall and routing decisions, so nothing is proxied and no extra hop is added. Throughput depends on your traffic profile and hardware — measure it for your workload rather than assuming a figure.
NAT is opt-in. With no rules in place, source NAT is off and the data path is unchanged, so you enable internet access one subnet at a time.
Which nodes can do this
All of them. There is one kind of node: every node hosts workloads, and every node can sit at the edge, translate addresses and exchange routes with your upstream routers.
Earlier versions asked you to pick Compute or Gateway when the node was installed, and the choice could not be changed afterwards. That is gone — see Nodes. Nodes running an older version are migrated on update with nothing for you to do.
What still varies is your topology, not the software: a node needs an uplink to the outside world before it can be an edge for anything, and NAT is opt-in per subnet either way.
The two directions
Everything on this page is one of two movements, and they use different rules.
Outbound — many workloads, one public address. A workload opens a connection to the internet. The node replaces the workload's private source address with its own public one, remembers the conversation, and puts the original address back on the replies. The internet sees only your public address. This is masquerade, below.
Inbound — one public port, one internal service. Something on the internet connects to your public address on a chosen port, and the node sends it to a specific workload. This is a port forward, and it is the only way traffic starts from outside; masquerade alone does not let anyone in.
outbound workload ─► node (source becomes public) ─► internet
inbound internet ─► node (destination becomes the workload) ─► workload
They are independent. A subnet with masquerade can reach out without publishing anything, and a published service works whether or not that subnet also has masquerade.
Where the firewall fits
NAT decides where a packet goes; the firewall decides whether it may. Both apply — publishing a service does not exempt it from firewall policy, so an inbound port forward still needs a rule permitting the traffic if your default action is deny. Plan the two together: a forward with no matching allow rule is a service that appears configured and answers nothing.
Publishing a service (inbound)
A port forward maps one public address and port to **one workload address and port**. It is available through the API:
curl -k -X POST "$NODE/api/v1/forward" \
-H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
-d '{"dest_ip":"203.0.113.10","dest_port":443,"target_ip":"10.20.0.50","target_port":8443}'
dest_ip and dest_port are what the outside world connects to; target_ip and
target_port are the workload that should receive it. The ports do not have to
match — publishing 443 to an application listening on 8443 is normal.
List, read and remove them the same way:
curl -k "$NODE/api/v1/forward" -H "Authorization: Bearer $TOKEN" # {"port_forwards":[ ... ]}
curl -k "$NODE/api/v1/forward/<id>" -H "Authorization: Bearer $TOKEN" # {"port_forward":{ ... }}
curl -k -X DELETE "$NODE/api/v1/forward/<id>" -H "Authorization: Bearer $TOKEN"
The list shows only port forwards, each with the same four fields plus its id. Masquerade rules live in gateway snat list, below, which shows both kinds.
One public port goes to one target. To publish several services, add a forward for each, using a different public port or a different public address.
Masquerade (shared public IP)
Masquerade (source NAT) lets an entire private LAN subnet reach the internet through a single public IP. When a tenant on the subnet opens an outbound connection, the node rewrites the packet's source address to the WAN interface's IP and tracks the flow; replies arriving at that public IP are translated back to the original tenant. TCP, UDP, and ICMP echo (ping) are all masqueraded.
Add a rule by giving the LAN subnet as a CIDR and the WAN egress interface:
sudo cenvero-str-ctl gateway snat add 10.50.0.0/24 --wan cnv-nic-0
The public IP is taken from the WAN interface itself, so you name the interface rather than an address. The response confirms the stored rule:
{
"status": "added",
"id": 1,
"type": "snat",
"source_cidr": "10.50.0.0/24",
"interface": "cnv-nic-0"
}
List the NAT rules in place — this shows both masquerade rules and any published port-forwards:
cenvero-str-ctl gateway snat list
{
"nat_rules": [
{
"id": 1,
"type": "snat",
"source_ip": "10.50.0.0/24",
"interface": "cnv-nic-0"
}
]
}
Remove a rule by its id:
sudo cenvero-str-ctl gateway snat remove 1
With no masquerade rule, outbound traffic from a private network is not
translated — and it does not leave: the node drops it on the uplink (see
below). Add one rule per LAN subnet you want to give internet access — or create
the network with --host-gateway --snat, which adds the rule for you (out the
configured uplink), keeps it in place and removes it with the network; see
The host gateway.
Private addresses never leave untranslated
A packet from one of your networks whose range is private (10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, or the shared 100.64.0.0/10) is dropped when it would leave on the uplink still carrying that private source address — that is, when no masquerade rule translated it. The internet could not answer it anyway, and providers treat private source addresses leaving a server as abuse, up to locking the server. It applies to every network on the node, whoever owns it.
- The uplink is the interface named in
gateway_wan_interface, or, when none is set, the interface of the node's default route — on a node the installer set up, the management bridge. Traffic is checked on the network card it finally leaves through: the uplink itself when it is a card the node manages (cnv-nic-…), or the managed cards under it when the uplink is a bridge (the management bridge and its card) or a bond of them. - The agent log says which uplinks are checked and on which interfaces, when the agent starts and whenever that changes. It warns, by name, about an uplink that is not checked: one that is not a card the node manages, nor a bridge or bond of them, and a
gateway_wan_interfacethat names an interface this node does not have (correct the setting, or clear it to use the default route). On such an uplink, or if your uplink is a VLAN interface, do not rely on the check: give every private network that needs the internet a masquerade rule. - A network that contains the node's own address on its uplink — a management network that overlaps one of your networks, say — is not checked, because checking it would cut the node's own traffic off. The agent log warns about it with the address; change the network's range, or give it a masquerade rule.
- Translated traffic leaves with the uplink's public address and is not affected, and neither is a network whose range is public (a block your provider routes to you), a public address a virtual machine holds, or the node's own traffic.
- Traffic between your networks, and to the node, stays inside the node and is not affected.
- A VLAN interface on top of the uplink's network card — a provider's private VLAN, for instance — is a network of its own: traffic the node sends into it is not affected, whatever its source address.
If your provider routes private ranges to an upstream NAT router of its own — so private source addresses are expected to leave the server — switch the check off, then restart the agent:
sudo cenvero-str-ctl config set allow_untranslated_private_egress true
sudo systemctl restart cenvero-stratum
Set it back to false to drop such traffic again.
Public addresses are never translated. A virtual machine holding one of the public addresses your provider routes to you (see Public Addresses) sends from that address unchanged — even if a masquerade rule's subnet happens to include it.
When a connection cannot be translated
Each new outbound connection needs a free source port on the public address toward its destination. In the rare case that every candidate port toward one destination is already in use, the node drops the new connection's packet rather than sending it out with the workload's private address — the client simply retries. Established connections are unaffected.
Ping works the same way. Two workloads pinging the same outside host with the same ICMP identifier would collide on one translation, so the second one's request is dropped rather than letting its replies be delivered to the first. Pinging tools pick a fresh identifier per run, so this is seen only when two clients happen to pick the same one at the same time.
Hairpin NAT (reaching your own public IP)
Hairpin NAT (also called NAT loopback) handles the case where a tenant on the LAN reaches one of your own published services by its public address. Suppose you publish an internal service on the node's public IP, and a client on the same LAN connects to that public IP instead of the internal address. Without hairpin, the server's reply would return straight to the client without passing back through the node, and the connection would break.
With hairpin, the node rewrites both ends of the forward packet: the destination is translated to the internal host (as with any published service), and the source is translated to the node's own LAN address. The reply then comes back to the node, which reverses both translations, so the client sees a consistent, working conversation.
Hairpin is automatic — there is no separate command. For IPv4 it engages when:
- a masquerade rule covers the LAN subnet (this is what tells the node its own address on that subnet), and
- the client and the published service's target host are both on that subnet.
If there is no masquerade rule for the subnet, a published service still works normally from outside; only the same-subnet loopback case relies on hairpin. Hairpin NAT is IPv4 only. See the API reference for publishing a service with a port-forward rule.
Two workloads on the same node stay on that node
This is the case worth knowing about if you host several customers on one server.
One workload calls another's public address — a VPS calling an API next door, say. Both addresses are on your node. That traffic is turned around **inside the node**. It does not go out to your provider's router and come back.
What that buys you:
| Latency | a local hop instead of a round trip off the box |
| Your uplink | untouched, so it stays free for traffic that genuinely leaves |
| Provider bandwidth | not consumed — the packets never reach the provider |
It is still fully accounted. Usage is measured in the packet path, not at the uplink, so both workloads are metered exactly as they would be for external traffic. Bandwidth limits, shared pools, monthly quotas and the node's licensed speed ceiling all apply to it. Keeping traffic local speeds it up; it does not make it invisible or free.
This is not a mode you turn on — it is what happens when both addresses are on the same node.
Private addresses behave differently, on purpose. Traffic between the private networks of two different tenants is refused, because private addresses are not reachable from outside and blocking them is real separation. Public addresses are reachable from the internet by definition, so refusing the local shortcut between two of them would not separate anything — it would only make the same conversation slower.
NAT64 (IPv6-only clients)
NAT64 lets an IPv6-only tenant reach an IPv4-only service on the internet. The client sends traffic to an IPv4 destination embedded in the well-known NAT64 prefix 64:ff9b::/96; the node translates the IPv6 packet to IPv4 (sourced from a shared IPv4 pool address) and translates the reply back to IPv6. This covers TCP and UDP.
NAT64 is off by default. Turn it on by first configuring an IPv4 pool address, then enabling it:
sudo cenvero-str-ctl nat64 configure 203.0.113.10
sudo cenvero-str-ctl nat64 enable
The pool address is the public IPv4 address that translated flows are sourced from. To use a prefix other than the well-known one, pass it as a second argument:
sudo cenvero-str-ctl nat64 configure 203.0.113.10 64:ff9b::/96
Check the current state and the number of active flows:
cenvero-str-ctl nat64 status
{
"enabled": true,
"prefix": "64:ff9b::/96",
"v4_pool": "203.0.113.10",
"active_bindings": 0,
"scope": "TCP/UDP (ICMP + fragmentation not translated)"
}
Disable it to return the node to its previous behavior:
sudo cenvero-str-ctl nat64 disable
NAT64 translates TCP and UDP. ICMP and fragmented datagrams are passed through untranslated rather than dropped. IPv6-only clients typically rely on a DNS64 resolver to synthesize 64:ff9b:: addresses for IPv4-only names; DNS64 is a resolver function and is configured separately from NAT64.
ICMP / ping
Tenants behind a masquerade rule can ping IPv4 hosts on the internet. ICMP echo requests are source-NATed to the WAN IP alongside TCP and UDP, and the matching echo replies are translated back to the originating tenant, so ordinary ping connectivity checks work through the node.
This covers ICMP echo (ping) only. It rides on the same masquerade rule — there is no separate command to enable it — so as soon as a subnet has a masquerade rule, its tenants can ping out. ICMP error messages are not translated.
Command reference
Mutating commands require root, so prefix them with sudo; the read-only list and status commands do not.
| Command | Action |
|---|---|
gateway snat add <lan-cidr> --wan <iface> | Masquerade a private LAN subnet out the WAN interface's public IP |
gateway snat list | List NAT rules (masquerade and published port-forwards) |
gateway snat remove <id> | Remove a NAT rule by id |
nat64 configure <v4-pool> [prefix] | Set the IPv4 pool address (and optional /96 prefix) |
nat64 enable | Enable NAT64 (requires a configured pool) |
nat64 disable | Disable NAT64 (forwarding reverts to unchanged) |
nat64 status | Show NAT64 state, prefix, pool, and active flow count |
See also
- Networking Overview — networks, endpoints, and how traffic moves.
- BGP Edge Routing — announce your fabric prefixes to upstream routers.
- Firewall — outbound and published traffic still passes the ACL.
- Gateway High Availability — a redundant pair of nodes and VIP failover.
- API Reference — publish internal services with the port-forward endpoint.