Rule accepting input HTTP in ip6tables not enough to allow IPv6 requests

I am facing an IPv6 accessibility problem with my server.

  • The server is IPv6-capable and is capable of contacting/being contacted by third-parties on IPv6 (ping6 and traceroute6 tested on my Debian stable Wheezy, up-to-date)
  • The DNS IPv6 AAAA entry for the website is existing and functioning properly
  • The webserver (nginx) is listening on the IPv6 link and is ready to handle the requests the same way as it is for IPv4
  • ip6tables INPUT table is configured to allow HTTP requests just like iptables (default policy DROP + TCP 80 ACCEPT rule):

    Chain INPUT (policy DROP 648 packets, 46788 bytes)
    pkts bytes target     prot opt in     out     source               destination
    6   480 ACCEPT     tcp      *      *       ::/0                 ::/0                 tcp dpt:80
    

I narrowed the problem down to the fact that if I set the default policy to ACCEPT, the HTTP connection works, otherwise not.

Thus, it seems than some other port redirections might be required? oO

Could that be related to some kernel configuration of the routing/IPv6 stack?

Here is the output of sudo ip6tables --line-numbers -nvL:

Chain INPUT (policy DROP 0 packets, 0 bytes)
num   pkts bytes target     prot opt in     out     source               destination
1     8169  784K ACCEPT     all      *      *       ::/0                 ::/0                 state RELATED,ESTABLISHED
2        0     0 ACCEPT     tcp      *      *       ::/0                 ::/0                 tcp dpt:22
3        0     0 ACCEPT     tcp      *      *       ::/0                 ::/0                 tcp dpt:80
4        0     0 ACCEPT     tcp      *      *       ::/0                 ::/0                 tcp dpt:443

Chain FORWARD (policy ACCEPT 0 packets, 0 bytes)
num   pkts bytes target     prot opt in     out     source               destination

Chain OUTPUT (policy ACCEPT 0 packets, 0 bytes)
num   pkts bytes target     prot opt in     out     source               destination

Solution 1:

Folks, you must not ignore ICMPv6 as you did for legacy IP - ICMPv6 and particularly the Neighbor Discovery Protocol (NDP) is vital for the proper functioning of IPv6. (The NDP, among other things, is a substitute for ARP.)

This means, you must allow at least for ICMPv6 types 133-136 from link local (i.e. fe80::/10). Moreover, you must allow certain error messages to arrive, as for instance routers don't fragment any more. You also don't want to drop link local multicast messages.

The full story is told in RFC 4890.

Below is an excerpt from one of my machines, a vm host that acts as a router:

#! /bin/sh

drop () {
    /sbin/ip6tables --jump DROP --append "$@";
}

accept () {
    /sbin/ip6tables --jump ACCEPT --append "$@";
}

chain () {
    /sbin/ip6tables --new-chain "$@"
}

ICMP_RATELIMIT="--match limit --limit 2/s"

# ...

#       Validate ingoing ICMPv6 messages
#
chain   ICMPv6_IN

# error messages

# allow error messages that are related to previously seen traffic
accept  ICMPv6_IN --protocol icmpv6 --icmpv6-type destination-unreachable --match conntrack --ctstate ESTABLISHED,RELATED $ICMP_RATELIMIT
accept  ICMPv6_IN --protocol icmpv6 --icmpv6-type packet-too-big --match conntrack --ctstate ESTABLISHED,RELATED $ICMP_RATELIMIT
accept  ICMPv6_IN --protocol icmpv6 --icmpv6-type ttl-exceeded --match conntrack --ctstate ESTABLISHED,RELATED $ICMP_RATELIMIT
accept  ICMPv6_IN --protocol icmpv6 --icmpv6-type parameter-problem --match conntrack --ctstate ESTABLISHED,RELATED $ICMP_RATELIMIT

# accept neighbor discovery
accept  ICMPv6_IN --protocol icmpv6 --icmpv6-type neighbor-solicitation $ICMP_RATELIMIT
accept  ICMPv6_IN --protocol icmpv6 --icmpv6-type neighbor-advertisement $ICMP_RATELIMIT

# accept router discovery
accept  ICMPv6_IN --protocol icmpv6 --icmpv6-type router-solicitation '!' --src ff00::/8 --in-interface cafe0 $ICMP_RATELIMIT
accept  ICMPv6_IN --protocol icmpv6 --icmpv6-type router-advertisement --src fe80::/10 --in-interface wlp3s0 $ICMP_RATELIMIT


# ping
# accept replies to my ping requests
accept  ICMPv6_IN --protocol icmpv6 --icmpv6-type echo-reply --match conntrack --ctstate ESTABLISHED,RELATED

# allow ping from my network(s)
accept  ICMPv6_IN --src $COUNTERMODE --protocol icmpv6 --icmpv6-type echo-request $ICMP_RATELIMIT

# allow link-local unicast ping
accept  ICMPv6_IN --dst fe80::/10 --protocol icmpv6 --icmpv6-type echo-request $ICMP_RATELIMIT

## allow multicast ping from local link
#accept  ICMPv6_IN --dst ff00::/8 --src fe80::/10 --protocol icmpv6 --icmpv6-type echo-request $ICMP_RATELIMIT

# multicast listener discovery v1
accept  ICMPv6_IN --protocol icmpv6 --icmpv6-type 130 --in-interface cafe0
accept  ICMPv6_IN --protocol icmpv6 --icmpv6-type 131 --in-interface cafe0
accept  ICMPv6_IN --protocol icmpv6 --icmpv6-type 132 --in-interface cafe0

# multicast listener discovery v2
accept  ICMPv6_IN --protocol icmpv6 --icmpv6-type 143 --in-interface cafe0


# drop everything else
drop ICMPv6_IN