This is an illustrated guide that shows how to configure the various types of Network Address Translation (NAT) on the Juniper SRX series. Each example lists the configuration on the SRX, as well as what the client and server on either side of the SRX doing the NATing see and experience through working examples.
Persistent NAT is completely different than address persistence. Persistent NAT has the effect of opening up a short bi-directional NAT session once the client initiates a connection. To see how this works, we are going to start by modifying our security policies not to allow the server to initiate a connection to the client.
[edit] juniper@SRX# deactivate security policies from-zone UNTRUST to-zone TRUST [edit] juniper@SRX# commit commit complete [edit] juniper@SRX#
Testing that this had the desired effect, we verify that we can no longer connect to the client from the server or even ping it
root@server:~# ping 192.168.200.81 PING 192.168.200.81 (192.168.200.81) 56(84) bytes of data. ^C --- 192.168.200.81 ping statistics --- 2 packets transmitted, 0 received, 100% packet loss, time 1007ms root@server:~# telnet 192.168.200.81 80 Trying 192.168.200.81... telnet: Unable to connect to remote host: Connection timed out root@server:~#
Next we configure the persistent-nat option in our NAT rule. There are three flavors of persistent NAT, each a bit more restrictive than the other. We'll start with the most open style which is "permit any-remote-host." This will allow any host on the other side of the NAT device to make a connection back to the source IP through the reflexive IP. It's not necessary to disable PAT, but perusing the output of the sessioin tables is a bit less confising when it's disabled. So our SRX config is adjusted to look like the following:
[edit security nat source] juniper@SRX# show pool SOURCE-NAT-POOL { address { 10.80.80.200/30; } port no-translation; } rule-set SOURCE-NAT { from zone UNTRUST; to zone TRUST; rule SOURCE-NAT-POOL { match { source-address 0.0.0.0/0; destination-address 0.0.0.0/0; } then { source-nat { pool { SOURCE-NAT-POOL; persistent-nat { permit any-remote-host; } } } } } } [edit security nat source] juniper@SRX#
Once the client makes a connection through the SRX with some traffic that matches the persisent NAT rule, an entry will be made in the persistent-nat table on the SRX. The SRX will now allow (per our ruleset) any host to connect back to our client using the reflexive IP address and source port we connected out with originally. To demonstrate this we first initiate a connection from the client using netcat from port 2222 sourcing it from IP address 192.168.200.88 and going to the server's 10.80.80.80 IP address. The remote port the client is connecting to doesn't really matter at this poing, so we'll jut pick 1024. The server isn't even listening on port 1024 at this time.
juniper@client:~$ nc -vv -p 2222 -s 192.168.200.88 10.80.80.80 1024 nc: connect to 10.80.80.80 port 1024 (tcp) failed: Connection refused juniper@client:~$
Once we do this, on the SRX we now have an entry in the persisten-nat-table:
juniper@SRX# run show security nat source persistent-nat-table all Internal Reflective Source Type Left_time/ Curr_Sess_Num/ Source In_IP In_Port I_Proto Ref_IP Ref_Port R_Proto NAT Pool Conf_time Max_Sess_Num NAT Rule 192.168.200.88 2222 tcp 10.80.80.200 2222 tcp SOURCE-NAT-POOL any-remote-host 236/300 0/30 SOURCE-NAT-POOL [edit security nat source] juniper@SRX#
To test the short bidirectional nature of the persistent nat connection we'll create a small testfile on the client and serve it up with netcat on port 2222 -- the same port we sourced our original connection off which matches the In_Port in the persistent-nat table.
juniper@client:~$ echo "THIS IS A TEST" > testfile juniper@client:~$ nc -vvl 2222 < testfile
Back on the server, we now make a connection back through it's reflexive IP address and are able to retrieve the contents of our message:
juniper@server:~$ nc -vv -s 10.80.80.89 10.80.80.200 2222 Connection to 10.80.80.200 2222 port [tcp/*] succeeded! THIS IS A TEST juniper@server:~$
Returning to the client, netcat acknowleges the connection, serves up the contents of our testfile, and closes the connection.
juniper@client:~$ nc -vvl 2222 < testfile Connection from 10.80.80.89 port 2222 [tcp/*] accepted juniper@client:~$
The any-remote-host directive is pretty open. We can connect not only from the address the client first made the connection to, but from any others:
juniper@server:~$ nc -vv -s 10.80.80.88 10.80.80.200 2222 Connection to 10.80.80.200 2222 port [tcp/*] succeeded! THIS IS A TEST juniper@server:~$ nc -vv -s 10.80.80.80 10.80.80.200 2222 Connection to 10.80.80.200 2222 port [tcp/*] succeeded! THIS IS A TEST juniper@server:~$
To narrow this down a bit, we can change the flavor from any-remote-host
to target-host
. After making our connection to 10.80.80.80 from the client with source port 2222 again we have the following entry in the persistent-nat-table:
juniper@SRX# run show security nat source persistent-nat-table all Internal Reflective Source Type Left_time/ Curr_Sess_Num/ Source In_IP In_Port I_Proto Ref_IP Ref_Port R_Proto NAT Pool Conf_time Max_Sess_Num NAT Rule 192.168.200.88 2222 tcp 10.80.80.201 2222 tcp SOURCE-NAT-POOL target-host 300/300 0/30 SOURCE-NAT-POOL [edit security nat source] juniper@SRX#
We make the connection back from the server again, and find that only the 10.80.80.80 address works now.
juniper@server:~$ nc -vv -s 10.80.80.80 10.80.80.201 2222 Connection to 10.80.80.201 2222 port [tcp/*] succeeded! THIS IS A TEST juniper@server:~$ nc -vv -s 10.80.80.88 10.80.80.201 2222 nc: connect to 10.80.80.201 port 2222 (tcp) failed: Connection timed out juniper@server:~$
However, we can still use any source port from the server to connect back to the client.
juniper@server:~$ nc -vv -s 10.80.80.80 -p 10000 10.80.80.201 2222 Connection to 10.80.80.201 2222 port [tcp/*] succeeded! THIS IS A TEST juniper@server:~$ nc -vv -s 10.80.80.80 -p 10001 10.80.80.201 2222 Connection to 10.80.80.201 2222 port [tcp/*] succeeded! THIS IS A TEST juniper@server:~$
We can limit this even more by using target-host-port
, now the reverse connection will only work when the source port matches the port that the client previously attempted to connect to, which in our case is port 1024. The persitent NAT table on the SRX looks like the following:
juniper@SRX# run show security nat source persistent-nat-table all Internal Reflective Source Type Left_time/ Curr_Sess_Num/ Source In_IP In_Port I_Proto Ref_IP Ref_Port R_Proto NAT Pool Conf_time Max_Sess_Num NAT Rule 192.168.200.88 2222 tcp 10.80.80.200 2222 tcp SOURCE-NAT-POOL target-host-port 158/300 0/8 SOURCE-NAT-POOL [edit security nat source] juniper@SRX#
And here is the server trying to connect back over the persistent connection. Notice that the connection fails when using port 10001, but succeeds when using 1024.
juniper@server:~$ nc -vv -s 10.80.80.80 -p 10001 10.80.80.200 2222 nc: connect to 10.80.80.200 port 2222 (tcp) failed: Connection timed out juniper@server:~$ nc -vv -s 10.80.80.80 -p 1024 10.80.80.200 2222 Connection to 10.80.80.200 2222 port [tcp/*] succeeded! THIS IS A TEST juniper@server:~$
address-mapping
OptionPersisitent NAT only works with TCP and UDP traffic by default. ICMP cannot trigger a persistent-nat table entry. To enable ICMP to work with perisient-nat, the address-mapping
option is specified. This option only works with the permit any-remote-host
type of persistent NAT. The SRX config now looks like:
[edit security nat] juniper@SRX# show source { pool SOURCE-NAT-POOL { address { 10.80.80.200/30; } port no-translation; } rule-set SOURCE-NAT { from zone UNTRUST; to zone TRUST; rule SOURCE-NAT-POOL { match { source-address 0.0.0.0/0; destination-address 0.0.0.0/0; } then { source-nat { pool { SOURCE-NAT-POOL; persistent-nat { permit any-remote-host; address-mapping; } } } } } } } proxy-arp { interface ge-0/0/3.0 { address { 10.80.80.200/30; } } } [edit security nat] juniper@SRX#
After commiting the above changes, we can trigger a table entry with a ping from the client.
juniper@client:~$ ping -c 1 10.80.80.88 PING 10.80.80.88 (10.80.80.88) 56(84) bytes of data. 64 bytes from 10.80.80.88: icmp_req=1 ttl=63 time=2.68 ms --- 10.80.80.88 ping statistics --- 1 packets transmitted, 1 received, 0% packet loss, time 0ms rtt min/avg/max/mdev = 2.681/2.681/2.681/0.000 ms juniper@client:~$
The corresponding persistent NAT table entry on the SRX:
juniper@SRX# run show security nat source persistent-nat-table all Internal Reflective Source Type Left_time/ Curr_Sess_Num/ Source In_IP In_Port I_Proto Ref_IP Ref_Port R_Proto NAT Pool Conf_time Max_Sess_Num NAT Rule 192.168.200.81 * * 10.80.80.201 * * SOURCE-NAT-POOL any-remote-host 282/300 0/30 SOURCE-NAT-POOL [edit security nat] juniper@SRX#
And we can now ping from the server to the client using the reflexive IP address.
juniper@server:~$ ping 10.80.80.201 PING 10.80.80.201 (10.80.80.201) 56(84) bytes of data. 64 bytes from 10.80.80.201: icmp_req=1 ttl=63 time=3.22 ms 64 bytes from 10.80.80.201: icmp_req=2 ttl=63 time=2.28 ms 64 bytes from 10.80.80.201: icmp_req=3 ttl=63 time=2.23 ms ^C --- 10.80.80.201 ping statistics --- 3 packets transmitted, 3 received, 0% packet loss, time 2003ms rtt min/avg/max/mdev = 2.230/2.577/3.220/0.455 ms juniper@server:~$
Verifying we received these pings on the client are indeed the ones sent from the server we use tcpdump:
juniper@client:~$ sudo tcpdump -nvi eth1 tcpdump: listening on eth1, link-type EN10MB (Ethernet), capture size 65535 bytes 18:26:17.042570 IP (tos 0x0, ttl 63, id 0, offset 0, flags [DF], proto ICMP (1), length 84) 10.80.80.80 > 192.168.200.81: ICMP echo request, id 4504, seq 1, length 64 18:26:17.042616 IP (tos 0x0, ttl 64, id 59596, offset 0, flags [none], proto ICMP (1), length 84) 192.168.200.81 > 10.80.80.80: ICMP echo reply, id 4504, seq 1, length 64 18:26:18.043215 IP (tos 0x0, ttl 63, id 0, offset 0, flags [DF], proto ICMP (1), length 84) 10.80.80.80 > 192.168.200.81: ICMP echo request, id 4504, seq 2, length 64 18:26:18.043266 IP (tos 0x0, ttl 64, id 59597, offset 0, flags [none], proto ICMP (1), length 84) 192.168.200.81 > 10.80.80.80: ICMP echo reply, id 4504, seq 2, length 64 18:26:19.044880 IP (tos 0x0, ttl 63, id 0, offset 0, flags [DF], proto ICMP (1), length 84) 10.80.80.80 > 192.168.200.81: ICMP echo request, id 4504, seq 3, length 64 18:26:19.044919 IP (tos 0x0, ttl 64, id 59598, offset 0, flags [none], proto ICMP (1), length 84) 192.168.200.81 > 10.80.80.80: ICMP echo reply, id 4504, seq 3, length 64 ^C 6 packets captured 6 packets received by filter 0 packets dropped by kernel juniper@client:~$
Note that with the wildcard entries in the table, the connection is completely open, we can now do things like ssh to the client using the reflexive IP.
juniper@server:~$ ssh 10.80.80.201 The authenticity of host '10.80.80.201 (10.80.80.201)' can't be established. ECDSA key fingerprint is 8e:c3:a5:30:c4:b7:03:5c:80:bc:ac:6c:8c:cd:e5:13. Are you sure you want to continue connecting (yes/no)? yes Warning: Permanently added '10.80.80.201' (ECDSA) to the list of known hosts. juniper@10.80.80.201's password: Welcome to Ubuntu 12.04.3 LTS (GNU/Linux 3.2.0-24-generic x86_64) * Documentation: https://help.ubuntu.com/ System information as of Fri Jan 10 18:26:50 CET 2014 System load: 0.1 Processes: 68 Usage of /: 29.6% of 7.97GB Users logged in: 1 Memory usage: 18% IP address for eth0: 10.0.99.81 Swap usage: 0% IP address for eth1: 192.168.200.81 Graph this data and manage this system at: https://landscape.canonical.com/ Last login: Fri Jan 10 15:21:19 2014 juniper@client:~$ w 18:28:28 up 3:07, 2 users, load average: 0.02, 0.03, 0.05 USER TTY FROM LOGIN@ IDLE JCPU PCPU WHAT juniper ttyS0 15:21 2:15 2.23s 2.17s -bash juniper pts/0 10.80.80.80 18:26 0.00s 0.23s 0.00s w juniper@client:~$
And for completeness, here is what the session table entry looks like for this connection through a persistent nat entry.
juniper@SRX# run show security flow session extensive Session ID: 5046, Status: Normal Flag: 0x4000000 Policy name: ACCEPT-LOG/4 Source NAT pool: Null Maximum timeout: 1800, Current timeout: 1798 Session State: Valid Start time: 11827, Duration: 10 In: 10.80.80.80/43494 --> 10.80.80.201/22;tcp, Interface: ge-0/0/3.0, Session token: 0x7, Flag: 0x21 Route: 0xa0010, Gateway: 10.80.80.80, Tunnel: 0 Port sequence: 0, FIN sequence: 0, FIN state: 0, Pkts: 57, Bytes: 6589 Out: 192.168.200.81/22 --> 10.80.80.80/43494;tcp, Interface: ge-0/0/4.0, Session token: 0x6, Flag: 0x20 Route: 0xe0010, Gateway: 192.168.200.81, Tunnel: 0 Port sequence: 0, FIN sequence: 0, FIN state: 0, Pkts: 40, Bytes: 5681 Total sessions: 1 [edit security nat] juniper@SRX#
Keep in mind that there is a lifetime associated with the bidirectional mapping of each persistent NAT table entry. By defalt this is 300 seconds, but is configurable with the inactivity-timeout
parameter under the persient NAT parameters for each NAT rule. The maximum number of sessions can also be controlled with the max-session-number
in the same configuration area. As long as there is traffic that matches the persistent NAT table entry, the entry will be kept open. Once there is a lack of traffic, the countdown to session clousre will begin.