Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use conntrack to allow incoming responses for outbound connections #115

Open
vodasams57 opened this issue Dec 6, 2023 · 3 comments
Open

Comments

@vodasams57
Copy link

To allow responses for outbound connections initiated by internal containers you suggest the following rules:

-A DOCKER-USER -j DROP -p tcp -m tcp --tcp-flags FIN,SYN,RST,ACK SYN -d 192.168.0.0/16
-A DOCKER-USER -j DROP -p tcp -m tcp --tcp-flags FIN,SYN,RST,ACK SYN -d 10.0.0.0/8
-A DOCKER-USER -j DROP -p tcp -m tcp --tcp-flags FIN,SYN,RST,ACK SYN -d 172.16.0.0/12
-A DOCKER-USER -j DROP -p udp -m udp --dport 0:32767 -d 192.168.0.0/16
-A DOCKER-USER -j DROP -p udp -m udp --dport 0:32767 -d 10.0.0.0/8
-A DOCKER-USER -j DROP -p udp -m udp --dport 0:32767 -d 172.16.0.0/12

There are some flaws with this approach:

  • TCP flags can be tampered (bypasses the firewall, destined application must handle tampered connections)
  • UDP is generally allowed for all ports above the range 0:32767 (i.e. /proc/sys/net/ipv4/ip_local_port_range) -> causes Wireguard on UDP 51820 accessible without rule => Wrong #64 and causes problems for apps that choose a source port within the range 0:32767 (e.g. dnsmasq)

Albeit being a stateless approach it would be safer to use the conntrack table (stateful) to allow responses for established connections:

-A DOCKER-USER --ctstate RELATED,ESTABLISHED -j ACCEPT -d 192.168.0.0/16
-A DOCKER-USER --ctstate RELATED,ESTABLISHED -j ACCEPT -d 10.0.0.0/8
-A DOCKER-USER --ctstate RELATED,ESTABLISHED -j ACCEPT -d 172.16.0.0/12

Even UFW is using conntrack to allow incoming responses initiated by local applications (see /etc/ufw/before.rules):

# quickly process packets for which we already have a connection
-A ufw-before-input -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
-A ufw-before-output -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
-A ufw-before-forward -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
  1. Did you consider using conntrack state?
  2. If so, why did you not use it?
@chaifeng
Copy link
Owner

chaifeng commented Dec 9, 2023

Hi @vodasams57

Thank you for your advice. It's helpful especially in handling UDP traffic. My original thought was to stop new connections from outside networks and can also defense against SYN Flood attacks. But you're right about the issues, especially with how we handle UDP.

If using conntrack, I'm considering these new rules to block new connections by default:

-A DOCKER-USER --ctstate NEW -j DROP -d 192.168.0.0/16
-A DOCKER-USER --ctstate NEW -j DROP -d 10.0.0.0/8
-A DOCKER-USER --ctstate NEW -j DROP -d 172.16.0.0/12

What do you think about these rules?

Only one concern I have is about the original rules, which have been widely deployed and tested in various environments. It works in most of cases. So, I'm thinking to update the documentation to include both sets of rules. Let users to choose which one they want to use.

@vodasams57
Copy link
Author

hi @chaifeng

thanks for your feedback.

What do you think about these rules?

I would prefer the following approach "allow rules first, block anything else".

# BEGIN UFW AND DOCKER
*filter
:ufw-user-forward - [0:0]
:ufw-docker-logging-deny - [0:0]
:DOCKER-USER - [0:0]
-A DOCKER-USER -j ufw-user-forward

# allow communication between containers
-A DOCKER-USER -j RETURN -s 10.0.0.0/8
-A DOCKER-USER -j RETURN -s 172.16.0.0/12
-A DOCKER-USER -j RETURN -s 192.168.0.0/16

# allow established connections (e.g. initiated by a container)
-A DOCKER-USER -m conntrack --ctstate RELATED,ESTABLISHED -j RETURN -d 10.0.0.0/8
-A DOCKER-USER -m conntrack --ctstate RELATED,ESTABLISHED -j RETURN -d 172.16.0.0/12
-A DOCKER-USER -m conntrack --ctstate RELATED,ESTABLISHED -j RETURN -d 192.168.0.0/16

# block anything else
-A DOCKER-USER -j ufw-docker-logging-deny

-A ufw-docker-logging-deny -m limit --limit 3/min --limit-burst 10 -j LOG --log-prefix "[UFW DOCKER BLOCK] "
-A ufw-docker-logging-deny -j DROP

COMMIT
# END UFW AND DOCKER

Only one concern I have is about the original rules, which have been widely deployed and tested in various environments.

I see your point. Nevertheless, in terms of security/firewalling it would make sense to default to the new rules. In addition, this approach makes the rules much more understandable and feels less hacky.

So, I'm thinking to update the documentation to include both sets of rules. Let users to choose which one they want to use.

Good point. I would add a deprecation mark for the old approach and highlight the drawbacks when using it.

@vodasams57
Copy link
Author

vodasams57 commented Jan 9, 2024

hi @chaifeng

did you have time to have a look at my last comment?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants