Quantcast
Channel: Johannes Weber – Weberblog.net
Viewing all articles
Browse latest Browse all 311

Accessing IPv6-only Resources via Legacy IP: NAT46 on a FortiGate

$
0
0

In general, Network Address Translation (NAT) solves some problems but should be avoided wherever possible. It has nothing to do with security and is only a short-term solution on the way to IPv6. (Yes, I know, the last 20 years have proven that NAT is used everywhere every time. 😉) This applies to all kinds of NATs for IPv4 (SNAT, DNAT, PAT) as well as for NPTv6 and NAT66.

However, there are two types of NATs that do not only change the network addresses but do a translation between the two Internet Protocols, that is IPv4 <-> IPv6 and vice versa. Let’s focus on NAT46 this time. In which situations is it used and why? Supplemented by a configuration guide for the FortiGates, a downloadable PCAP and Wireshark screenshots.

NAT46 Concept

Don’t get confused: I’m talking about NAT46 this time – not NAT64.

Basically, if you have IPv6-only servers (to avoid the unnecessary dual-stack burden for at least some of your infrastructure), but still want to have those servers accessible for IPv4-only clients, you have to use some kind of protocol translation somewhere. Either through reverse proxies or load balancers or through a NAT46 gateway like this:

Note that the NAT46 proxy, pronounced NAT-four-six by the way, does not necessarily have to be in the direct path between the client and server – it only has to be accessible by appropriate routes. Nevertheless, having one central firewall in place which does the job fits perfectly for small installations.

Furthermore, note that you need a hostname in the public DNS with an A record for the IPv4 address on your NAT46 gateway. But you don’t need a special DNS device such as a DNS64 box for NAT64 to work.

The basic concept of translating IP and ICMP between IPv4 and IPv6 aka “Stateless IP/ICMP Translation Algorithm (SIIT)” is described in RFC 7915. Funnily enough, the keyword “NAT46” is not said in the document at all.

Since I have at least one “server” (it’s a Raspi) running IPv6-only, I was able to test this NAT46 gateway. My True Random PSK Generator at https://random.weberlab.de/ has only an AAAA record, while I used https://random46.weberlab.de/ to access it via legacy IP:

NAT46 on a FortiGate

I’m not a big fan of FortiGate firewalls because they are neither reliable nor sound in many situations. However, they offer many cool and new features that other vendors don’t have. So let’s configure NAT46. I’m using a FortiWiFi-61E with FortiOS v7.0.9 for this setup.

At first, it requires a NAT object aka “Virtual IP” of type IPv4 which maps the (public) IPv4 address to the (internal) IPv6 address:

Second, you need an IPv6 Pool for the IPv6 source from which the firewall will initiate the internal IPv6 connections. Note the NAT46 checkbox. Also note that the “pool” is not really a pool but only capable of one or two IPv6 addresses. It took me a while to figure it out. All ranges I tested weren’t valid until I reduced the pool to one single IPv6 address. As you can see, I chose a very special-looking one: an IPv6 address with 4646 at the very end to spot it easily:

Finally, you need an appropriate Firewall Policy. Note that you must select the “NAT46” feature before you can select the destination which is the virtual IP object:

I’m a little scared when looking at this policy in the overview since the destination IPv6 address object says “any” though nothing was selected when creating the policy. AHH. It looks like this policy now allows all IPv6 destinations. Hopefully, it doesn’t. (That’s what I mean when stating that FortiGate firewalls are not that sound.)

Having a look at the Forward Traffic Log you can indeed see both Internet Protocols:

The appropriate CLI commands for this feature are: (You have to adjust some of them according to your needs/setup, e.g., the profile-group, the inspection-mode, and the like.)

config firewall vip
    edit "random46"
        set comment "Test NAT46"
        set extip 194.247.5.23
        set nat44 disable
        set nat46 enable
        set extintf "wan1"
        set ipv6-mappedip 2001:470:1f0b:16b0:6986:b8d4:3649:9cbe
    next
end
config firewall ippool6
    edit "SNAT46"
        set startip 2001:470:1f0b:16b0::4646
        set endip 2001:470:1f0b:16b0::4646
        set nat46 enable
    next
end
config firewall policy
    edit 41
        set name "random46"
        set srcintf "wan1"
        set dstintf "internal"
        set action accept
        set nat46 enable
        set srcaddr "all"
        set dstaddr "random46"
        set srcaddr6 "all"
        set dstaddr6 "all"
        set schedule "always"
        set service "HTTP" "HTTPS" "PING" "PING6"
        set utm-status enable
        set inspection-mode proxy
        set profile-type group
        set profile-group "app-only"
        set logtraffic all
        set ippool enable
        set poolname6 "SNAT46"
    next
end

 

Having a look at the sessions via CLI, you can see both ones, legacy IP and IPv6. Note the “peer” line for each IP in which the other IP is referenced. Nice! You have to use two different commands to show those sessions, though. (That’s what I mean when stating that FortiGate firewalls are not that sound.)

fg2 # diagnose sys session list

session info: proto=6 proto_state=05 duration=1 expire=0 timeout=3600 flags=00000000 socktype=0 sockport=0 av_idx=0 use=3
origin-shaper=
reply-shaper=
per_ip_shaper=
class_id=0 ha_id=0 policy_dir=0 tunnel=/ vlan_cos=0/255
state=log may_dirty npu f00
statistic(bytes/packets/allow_err): org=1343/11/1 reply=7375/9/1 tuples=2
tx speed(Bps/kbps): 1316/10 rx speed(Bps/kbps): 7230/57
orgin->sink: org pre->post, reply pre->post dev=6->20/20->6 gwy=194.247.5.23/194.247.4.1
hook=pre dir=org act=noop 85.215.94.29:53928->194.247.5.23:443(0.0.0.0:0)
hook=post dir=reply act=noop 194.247.5.23:443->85.215.94.29:53928(0.0.0.0:0)
peer=2001:470:1f0b:16b0::4646:53928->2001:470:1f0b:16b0:6986:b8d4:3649:9cbe:443 naf=1
hook=pre dir=org act=noop 2001:470:1f0b:16b0::4646:53928->2001:470:1f0b:16b0:6986:b8d4:3649:9cbe:443(:::0)
hook=post dir=reply act=noop 2001:470:1f0b:16b0:6986:b8d4:3649:9cbe:443->2001:470:1f0b:16b0::4646:53928(:::0)
pos/(before,after) 0/(0,0), 0/(0,0)
misc=0 policy_id=41 pol_uuid_idx=606 auth_info=0 chk_client_info=0 vd=0
serial=018cd679 tos=ff/ff app_list=0 app=0 url_cat=0
rpdb_link_id=00000000 ngfwid=n/a
npu_state=0x4040400 ofld-O
npu info: flag=0x00/0x00, offload=0/0, ips_offload=0/0, epid=0/0, ipid=0/0, vlan=0x0000/0x0000
vlifid=0/0, vtag_in=0x0000/0x0000 in_npu=0/0, out_npu=0/0, fwd_en=0/0, qid=0/0
no_ofld_reason:
ofld_fail_reason(kernel, drv): none/not-established, none(0)/none(0)
npu_state_err=00/04
total session 1


fg2 # diagnose sys session6 list

session6 info: proto=6 proto_state=15 duration=1 expire=0 timeout=3600 flags=00000000 sockport=0 socktype=0 use=3
origin-shaper=
reply-shaper=
per_ip_shaper=
class_id=0 ha_id=0 policy_dir=0 tunnel=/ vlan_cos=0/0
state=log may_dirty npu app_valid
statistic(bytes/packets/allow_err): org=1563/11/0 reply=7802/12/0 tuples=2
tx speed(Bps/kbps): 893/7 rx speed(Bps/kbps): 4458/35
orgin->sink: org pre->post, reply pre->post dev=20->23/23->20
hook=pre dir=org act=noop 2001:470:1f0b:16b0::4646:53928->2001:470:1f0b:16b0:6986:b8d4:3649:9cbe:443(:::0)
hook=post dir=reply act=noop 2001:470:1f0b:16b0:6986:b8d4:3649:9cbe:443->2001:470:1f0b:16b0::4646:53928(:::0)
peer=194.247.5.23:443->85.215.94.29:53928 naf=2
dst_mac=b8:27:eb:03:a0:ac
misc=0 policy_id=41 auth_info=0 chk_client_info=0 vd=0
serial=00312c35 tos=ff/ff ips_view=0 app_list=2000 app=40568 url_cat=0
rpdb_link_id = 00000000 ngfwid=n/a
npu_state=0x4041808 ofld-R
npu info: flag=0x00/0x81, offload=0/0, ips_offload=0/0, epid=0/64, ipid=0/76, vlan=0x0000/0x0000
vlifid=0/76, vtag_in=0x0000/0x0000 in_npu=0/1, out_npu=0/1, fwd_en=0/0, qid=0/3
no_ofld_reason:
ofld_fail_reason(kernel, drv): none/not-established, none(0)/none(0)
npu_state_err=00/04

 

Finally, note that this setup required several other things around the mere network config which I have not shown here. That is:

  • a hostname for random46.weberlab.de with only at least an A record
  • ServerAliases on the apache2 config for the virtual host
  • an adjusted rewrite condition to forward HTTP -> HTTPS for this ServerAlias
  • running certbot again to have a valid X.509 certificate with this hostname in the subject alternative name field

Deeper Look on the Wire

I’ve captured some basic runs, that is: doing an HTTP request, getting redirected to HTTPS, as well as a ping aka echo-request. I captured on the client as well as on the server simultaneously and merged them later on. This capture is within the Ultimate PCAP already, but you can download it solely as well:

You can easily filter for ip or ipv6 to see only one of those Internet Protocols. Here they are side-by-side, looking at the SYN for the HTTP session:

As expected, the upper-layer protocol stuff is exactly the same after the NAT46 proxy, such as the TLS handshake with its ECDH client key exchange:

However, looking at ICMPv4 vs. ICMPv6 messages for echo-requests/-replies, the data portion looks a little different. ICMPv4 in this example used a timestamp which should be silently dropped according to RFC 7915, section 4.2. Looks like the FortiGate isn’t doing it that way but keeps the timestamp information within the data portion:

However, it’s working quite good. Nice! If you can spot any other differences between those translated protocols, please write a comment!

Fun Fact: NAT646

The other day I was on a german train using my T-Mobile tethering on my iPhone which gives perfect IPv6-native access, incl. DNS64/NAT64. Now, when surfing to this IPv4-only NAT46 domain, it eventually does a 646 translation. ;)

Of course, that would not have happened if the hostname would have had an AAAA record as well, which would be the case for real-world purposes in which your server hostname has an AAAA record (since it is IPv6-only) *and* the additional A record for the NAT46 translation.

Note that this post is one of many related to IPv6. Click here for a structured list.

Photo by Joshua Sortino on Unsplash.


Viewing all articles
Browse latest Browse all 311

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>