Every packet leaving a private network through a firewall gets its return address rewritten, and almost nobody watches it happen. Source NAT is plumbing: silent when healthy, and — this is the dangerous part — equally silent when broken. A misconfigured security policy denies traffic and writes a log. A missing route shows up in the routing table. But a source NAT rule that no longer matches raises no alarm at all; the firewall just forwards packets with private return addresses into a network that will never answer them.
This article covers source NAT on Junos SRX three ways: the mechanics, using a live session as the worked example; the configuration structure and why rule order carries meaning; and a case study of the failure mode that makes this feature worth writing about — a re-IP migration that killed internet access without producing a single error. The configuration and incident below are a composite drawn from real operations, rebuilt on documentation addressing (RFC 5737 ranges and private space), so the commands and outputs are representative — run them on any SRX and you'll see the same shapes.
Part 1 — What source NAT actually does
The setup: a LAN on 10.20.30.0/24 sits behind an SRX whose internet-facing interface ge-0/0/0 holds the public address 198.51.100.2. A host at 10.20.30.50 sends an NTP query to a time server at 203.0.113.10. Private addresses aren't routable on the internet — if that packet left carrying its real source, the reply would have nowhere to go. So the SRX rewrites the source to its own public address on the way out, and un-rewrites the reply on the way back. The session table shows the whole arrangement:
> show security flow session destination-prefix 203.0.113.10 Session ID: 4821, Policy name: lan-to-internet/7, Timeout: 58 In: 10.20.30.50/54321 --> 203.0.113.10/123;udp, If: irb.30 Out: 203.0.113.10/123 --> 198.51.100.2/61102;udp, If: ge-0/0/0.0
In: is the forward wing — the client's traffic as it enters the firewall, shown with its original, pre-NAT source. Out: is the return wing — what the far end sends back, addressed to the firewall's translated identity. If translation is working, the Out wing's destination is your public address. If you ever see the private address in the Out wing's destination, NAT did not happen for this session.
Notice one more detail in that output: the port changed too — 54321 became 61102. That's port address translation, and it's what lets hundreds of internal hosts share one public address. The source IP alone can't distinguish their flows once everyone appears as 198.51.100.2; the rewritten source port becomes the demultiplexing key the firewall uses to route each reply back to the right internal host. One public address, ~64,000 usable ports, thousands of concurrent conversations.
Part 2 — Configuration structure
Source NAT on the SRX is organized as rule-sets scoped by zone context, with ordered rules inside. Here's a representative LAN-to-internet rule-set, including the pattern that matters most in real deployments — the VPN exemption:
security {
nat {
source {
rule-set lan-to-internet {
from zone LAN;
to zone WAN;
rule vpn-exempt {
match {
source-address 10.20.30.0/24;
destination-address 10.99.0.0/16;
}
then {
source-nat off;
}
}
rule corp-out {
match {
source-address 10.20.30.0/24;
}
then {
source-nat {
interface;
}
}
}
}
}
}
}
Three things carry the meaning here.
The zone context selects the rule-set. from zone LAN; to zone WAN; means this rule-set is only consulted for traffic entering from the LAN zone and leaving toward the WAN zone. Traffic taking a different path — LAN to a VPN zone, for instance — never touches these rules. That zone scoping is a feature, not a limitation: it's what lets internet-bound and tunnel-bound traffic behave completely differently without the rules fighting each other.
Rules evaluate top-down, first match wins. Within the rule-set, the first rule whose match conditions fit the packet decides the outcome, and evaluation stops. That's why vpn-exempt sits above corp-out: traffic headed for the remote tunnel networks (10.99.0.0/16) matches the exemption first and keeps its original addressing — which the far side's VPN selectors and routing expect — while everything else falls through to the catch-all and gets translated. Swap the order and the exemption never fires; the catch-all eats everything first. On the SRX, insert exists precisely because rule position is configuration.
The then clause picks one of three translations. interface translates to the egress interface's address with port translation — the standard, zero-maintenance choice for internet access. pool translates into a dedicated block of public addresses you define — used when the far end filters by source, when you need port preservation, or when one address isn't enough. And off exempts the traffic from translation entirely — the VPN pattern above, and the sharpest tool in the set because forgetting it exists is how tunnel traffic mysteriously breaks.
Part 3 — Verification, fastest first
Three checks, in the order an operator should reach for them.
The session table is the ground truth. One command, and the Out wing either shows your public address or it doesn't (see Part 1). This is a per-flow answer: it tells you what happened to this conversation.
Rule hit counters are the fastest aggregate check.
> show security nat source rule all source NAT rule: corp-out Rule-set: lan-to-internet From zone : LAN To zone : WAN Match Source addresses : 10.20.30.0 - 10.20.30.255 Action : interface Translation hits : 48213
Run it twice a few seconds apart while traffic flows. A counter that increments is a rule that's matching. A counter frozen at some old number — or at zero — on the rule you expect to be doing the work is the single fastest tell that traffic is bypassing it. This one habit finds most NAT problems in under a minute.
Watch the wire when you need certainty. monitor traffic interface ge-0/0/0.0 matching "host 203.0.113.10" shows the packets actually leaving. If the source reads 198.51.100.2, translation happened. If it reads 10.20.30.x, you're watching untranslated packets walk out the door toward a provider that will drop them.
Part 4 — Case study: the re-IP that silently killed the internet
Now the scenario that motivates all of this. Consider a LAN migration: the network is renumbered from a legacy range, 192.168.10.0/24, to a new plan, 10.20.30.0/24. The change is done carefully — IRB interfaces re-addressed, DHCP scopes rebuilt, security-policy address books updated, routes adjusted, VPN selectors amended. Everything is checked. The commit succeeds. And within minutes: the internet is down for every user — while, confusingly, the site-to-site VPN works perfectly.
The cause: the source NAT rule still says source-address 192.168.10.0/24. Nobody's checklist included it. Traffic from the new subnet enters the rule-set, matches nothing, and here is the crucial behavior — matching no source NAT rule is not an error. It simply means "no translation." The SRX forwards the packets exactly as they are, private source and all, the provider drops or black-holes them upstream, and no reply ever comes back.
What makes this failure mode genuinely nasty is how thoroughly it hides:
- No error is raised anywhere. Untranslated forwarding is legitimate behavior (it's literally what
source-nat offrequests), so there is nothing to log. The commit was clean. The chassis is healthy. - Outbound looks alive. The security policy still permits the traffic, so sessions get created and sit in the flow table — with an Out wing quietly showing the private address as the return destination. Traffic is flowing; replies just never arrive.
- The VPN keeps working, which sends troubleshooting in exactly the wrong direction. Tunnel traffic was updated with the migration and was never supposed to be translated anyway — so the one path that works is the one that proves nothing about the broken one.
- The obvious test lies. The natural move — ping something public from the firewall itself, sourced from the new LAN gateway address — fails, and the failure means nothing. See below.
ping 203.0.113.10 source 10.20.30.1 run on the SRX does not traverse the from zone LAN rule-set at all. Traffic the firewall generates itself originates from the internal junos-host context, not from the LAN zone — so it egresses untranslated regardless of whether your transit NAT is configured correctly. The ping fails when NAT is broken, and it also fails when NAT is fixed. A test that fails in both states is not a test. Verify transit NAT with transit traffic — a real host behind the firewall — plus the session table and hit counters.
The fix, once seen, is one line and a commit:
# set security nat source rule-set lan-to-internet rule corp-out match source-address 10.20.30.0/24 # delete security nat source rule-set lan-to-internet rule corp-out match source-address 192.168.10.0/24 # commit
And the verification is the Part 3 sequence in miniature: the corp-out hit counter starts climbing, and a fresh session's Out wing shows 198.51.100.2 where the private address used to be. Internet restored, root cause understood, no reboot, no drama.
Part 5 — A troubleshooting sequence that ends arguments
When "the internet is down" lands on a network with an SRX at the edge, this order settles it fastest:
- 1. Confirm traffic is reaching the firewall.
show security flow session source-prefix <host>— a session existing means the packet arrived and policy permitted it. No session at all points upstream of NAT entirely (host, switch, VLAN, policy deny). - 2. Read the Out wing. Translated destination address on the return wing = NAT is fine, look elsewhere. Private address = NAT miss, keep going.
- 3. Check rule hit counters while generating traffic. The rule you expect should be climbing. If a different rule is climbing — or none — you've localized the problem to matching.
- 4. Verify the zone context.
show security zonesandshow route <destination>— confirm the traffic's actual ingress and egress zones are the ones the rule-set names. Interfaces get moved between zones; rule-sets don't follow them. - 5. Check the match prefixes against current reality. Is the source address in the rule the network's present addressing, or last year's? This is the case-study failure, and it's checked in ten seconds.
- 6. Separate policy problems from NAT problems. A policy deny produces no session (and, if logging is on, a deny log). A NAT miss produces a live session with a wrong Out wing. They present identically to users and completely differently in the flow table.
And the pitfalls that recur, named so they can be recognized: testing from the firewall itself (the junos-host trap above); stale prefixes after any renumbering; exemption rules placed below the catch-all, where first-match-wins guarantees they never fire; zone context drifting after interfaces move; and waiting for a log entry that source NAT will never write — absence of errors is not evidence of health here, only the session table is.
Quick reference
# The ground truth — read the Out wing show security flow session destination-prefix <ip> show security flow session source-prefix <host> # Fastest aggregate check — counters should climb show security nat source rule all show security nat source summary # Context and path show security zones show route <destination> # Watch the wire monitor traffic interface ge-0/0/0.0 matching "host <ip>"
Source NAT earns almost no attention because it almost never demands any — which is exactly why its failure mode is so disorienting when it arrives. The defense isn't memorizing configurations; it's the reading habit: know what the two wings of a session mean, trust hit counters over intuition, test with transit traffic instead of the firewall's own, and put NAT prefixes on every addressing-change checklist. The layer that fails silently deserves the checks that don't rely on it speaking up.
Sunil Burigen is a senior network engineer focused on data center fabrics, infrastructure automation, and the systems that keep large networks running. More writing and background at the home page.
← Back to all Field Notes