iptables egress firewall works with IPv4 but not with IPv6

TommyPeanuts asked:

I have a server running Ubuntu 16.04 with an application that only needs outgoing connections for package updates and NTP time syncing. It has a dynamic IPv6 address on a separate network interface for this purpose. All other connections are via the LAN on another interface, which has no gateway to the WAN.

I’d like to secure this machine by disallowing any outgoing connections other than for package updates and NTP time syncing.

However, when I try the following rules, requests to the allowed hosts are blocked:

ip6tables -A OUTPUT -o lo -p all -j ACCEPT
ip6tables -A OUTPUT -p icmpv6 -j ACCEPT
ip6tables -A OUTPUT -m state --state NEW,RELATED,ESTABLISHED -j ACCEPT
ip6tables -A OUTPUT -p udp -m owner --uid-owner systemd-timesync -j ACCEPT
ip6tables -A OUTPUT -p tcp --dport 53 -j ACCEPT
ip6tables -A OUTPUT -p udp --dport 53 -j ACCEPT

while read p; do
        ip6tables -A OUTPUT -d $p -j ACCEPT
done < firewall/hosts-to-allow.list

ip6tables -A OUTPUT -o ens18 -j REJECT

Note that incoming icmpv6 requests are allowed, but all other incoming ports are blocked.

It seems the blocked entries are for packets going to the DNS resolvers (2001:0ba8:0000:2c02::):

Mar 24 08:16:19 violet kernel: [585601.187999] IPTables-Dropped: IN= OUT=ens18 SRC=2001:0ba8:0000:2c38:2841:0fff:fe53:20eb DST=2001:0ba8:0000:2c02:0000:0000:0000:0000 LEN=98 TC=0 HOPLIMIT=64 FLOWLBL=457722 PROTO=UDP SPT=46870 DPT=53 LEN=58

I’ve tried explicitly adding the resolver addresses to the allowed hosts file in the above script, but it makes no difference.

When I tried temporarily adding a fixed IPv4 address to the WAN interface (and adding identical IPv4 rules) it does work. So I assume this is something to do with IPv6.


EDIT: Here’s the fully applied rules

Chain INPUT (policy ACCEPT 1 packets, 104 bytes)
 pkts bytes target     prot opt in     out     source               destination         

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

Chain OUTPUT (policy ACCEPT 0 packets, 0 bytes)
 pkts bytes target     prot opt in     out     source               destination         
    0     0 LOGGING    all      *      ens18   ::/0                 ::/0                
    0     0 ACCEPT     all      *      lo      ::/0                 ::/0                
    0     0 ACCEPT     all      *      ens19   ::/0                 ::/0                
    0     0 ACCEPT     icmpv6    *      *       ::/0                 ::/0                
    0     0 ACCEPT     icmpv6    *      *       ::/0                 ::/0                
    0     0 ACCEPT     all      *      *       ::/0                 ::/0                 ctstate NEW,RELATED,ESTABLISHED
    0     0 ACCEPT     tcp      *      *       ::/0                 ::/0                 tcp dpt:53
    0     0 ACCEPT     udp      *      *       ::/0                 ::/0                 udp dpt:53
    0     0 ACCEPT     udp      *      *       ::/0                 ::/0                 owner UID match 100
    0     0 ACCEPT     tcp      *      *       ::/0                 2001:67c:1560:8001::14 
    0     0 ACCEPT     tcp      *      *       ::/0                 2001:67c:1360:8001::17 
    0     0 ACCEPT     tcp      *      *       ::/0                 2001:67c:1360:8001::21 
    0     0 ACCEPT     tcp      *      *       ::/0                 2001:67c:1560:8001::11 
    0     0 ACCEPT     udp      *      *       ::/0                 2001:67c:1560:8001::14 
    0     0 ACCEPT     udp      *      *       ::/0                 2001:67c:1360:8001::17 
    0     0 ACCEPT     udp      *      *       ::/0                 2001:67c:1360:8001::21 
    0     0 ACCEPT     udp      *      *       ::/0                 2001:67c:1560:8001::11 
    0     0 ACCEPT     tcp      *      *       ::/0                 2001:67c:1562::19   
    0     0 ACCEPT     tcp      *      *       ::/0                 2001:67c:1560:8001::14 
    0     0 ACCEPT     tcp      *      *       ::/0                 2001:67c:1562::16   
    0     0 ACCEPT     tcp      *      *       ::/0                 2001:67c:1360:8001::21 
    0     0 ACCEPT     tcp      *      *       ::/0                 2001:67c:1360:8001::17 
    0     0 ACCEPT     tcp      *      *       ::/0                 2001:67c:1560:8001::11 
    0     0 ACCEPT     udp      *      *       ::/0                 2001:67c:1562::19   
    0     0 ACCEPT     udp      *      *       ::/0                 2001:67c:1560:8001::14 
    0     0 ACCEPT     udp      *      *       ::/0                 2001:67c:1562::16   
    0     0 ACCEPT     udp      *      *       ::/0                 2001:67c:1360:8001::21 
    0     0 ACCEPT     udp      *      *       ::/0                 2001:67c:1360:8001::17 
    0     0 ACCEPT     udp      *      *       ::/0                 2001:67c:1560:8001::11 
    0     0 ACCEPT     tcp      *      *       ::/0                 2001:ba8:0:2c02::   
    0     0 ACCEPT     udp      *      *       ::/0                 2001:ba8:0:2c02::   
    0     0 ACCEPT     tcp      *      *       ::/0                 2001:ba8:0:2c04::   
    0     0 ACCEPT     udp      *      *       ::/0                 2001:ba8:0:2c04::   
    0     0 REJECT     all      *      ens18   ::/0                 ::/0                 reject-with icmp6-port-unreachable

Chain LOGGING (1 references)
 pkts bytes target     prot opt in     out     source               destination         
    0     0 LOG        all      *      *       ::/0                 ::/0                 limit: avg 2/min burst 5 LOG flags 0 level 4 prefix "IPTables-Dropped: "
    0     0 DROP       all      *      *       ::/0                 ::/0  

My answer:


Your OUTPUT table immediately begins by sending all traffic for ens18 to the LOGGING table, which proceeds to log and then drop all the traffic.

Remember that iptables rules are interpreted in order. Because you have already dropped all the traffic for ens18, no other rules in OUTPUT get evaluated for that traffic.

I suspect that a logging rule should be at the end of the table, rather than at the beginning. Of course you already put a REJECT target rule there, so you should decide which one of those you want.

I also suspect you would have noticed a lot sooner if you had tried to connect to your server via IPv6 on the affected interface, as you would have immediately noticed that no traffic was flowing…


View the full question and any other answers on Server Fault.

Creative Commons License
This work is licensed under a Creative Commons Attribution-ShareAlike 3.0 Unported License.