I just set some firewalling rules on a VyOS instance, and have a remaining question...
So, it is possible to set one IN, one OUT and one LOCAL group-rules for each interface.
But in fact, in state related mode, OUT rules of an interface are supposed to be the same that the IN rules of the others, isn't it ?
For example, if we want to allow a http network connection from a client connected to interface eth0, to a server connected to interface eth1, are the two following rules the same ?
in the OUT rules set for eth1: source=client; destination=server; protocol=http; action=accept
in the IN rules set for eth0: source=client; destination=server; protocol=http ; action=accept
So theses are two similar rules.
Is that true ? Is it necessary to configure both ? Or only one of them is enough ? If the two are set-up, will this take two times more CPU usage ?
More generally, are the IN rules processed firstly, before the OUT rules are processed ?
Thanks for your help,
When you make use of the global firewall state policy it will evaluate configured ESTABLISHED, RELATED, and INVALID state matches before traversing a policy that has been applied to an interface and direction (either via interface-level or zone-policy configuration).
In terms of performance considerations, since it (correctly) performs state matching early on, it is efficient in bypassing rule traversal for established and related connections.
It is important to understand, that using this global level of state matching, while easy to use, does limit the amount of control you have (e.g. it is a tradeoff of control vs. simple configuration).
One such example is that if you wish to block a connection that is already established, there is no place to configure a rule to drop that traffic. Instead you will need to remove that connection from the conntrack table (e.g. perform a lookup using show conntrack ipv4 to determine the connection ID followed by delete conntrack table ipv4 conn-id <ID> to remove it).
Alternatively, if you are performing state matching on a per-policy basis you have the ability to insert drop rules before the state check. A practice I recommend is to maintain a NET-BLACKLIST network-group that you can use to quickly drop problem traffic and apply as a pair of drop rules (one for matching source, one for matching destination) before your state check rules.
There is no practical performance difference between defining state match rules globally or individually per-policy provided that you perform the match within the first few rules.
If you were to move your state match rules to the end of a long ruleset, on the other hand, then you would see a performance penalty since each rule would need to be evaluated before reaching the state check. This is why you should always have your rules to permit ESTABLISHED and RELATED traffic at the start of your policy as opposed to the end of it.
It's very hard to see a performance difference between 1 and 10 rules being evaluated on Linux. You would see a difference between 1 and 100 or 1000 rules, though (depending on PPS and resources available). So it really depends on what scale you're working with. I say there is no practical difference because you would typically be talking fractions of a millisecond in terms of the difference between 10 and 1000.
Also note that in terms of connection tracking, for a connection to be considered established, there must be bi-directional communication observed. Likewise, for traffic to be related, it must first have an entry in the expect table.
As an aside entries in the expect table are only created by conntrack helper modules observing established connections for specific protocols that make additional connections using different 5-tuple (protocol, source address, destination address, source port, and destination port or type if applicable) information than the original connection. The most common example of this is Active-mode FTP, where the server will respond with an additional source port to the one used to initiate the connection.
Here is an example:
Provided you have two network interfaces on a firewall, and you have a default drop policy in place on traffic in both directions, on each interface (e.g. 4 firewall rulesets to traverse), the global permit for related and established traffic will not apply until bi-directional communication has been observed. Even if you allow the traffic coming into the interface of the first network, it must still be explicitly permitted in the policy for outgoing traffic of the second network before the destination host will be able to receive and respond to move the connection into an established state. Until that response happens, the connection remains NEW (or rather a sub-state of UNREPLIED but that detail only matters to netfilter developers).
Also note that conntrack is always described in terms of the traffic initiator. What permitting established state traffic does is allow the return traffic without the need to create a specific rule to do so. So, generally, no if you are allowing established and related traffic, once you have the rules to permit the initial connection, you do not need rules to match the return traffic. This is the essence to a stateful firewall. It allows you to control the flow of communication by introducing the concept of direction to traffic (as hinted by the initiator and responder). For example, you might allow connections to your web server on TCP port 80 for HTTP, and allowing established traffic would permit the return traffic for each connection specifically, while maintaining the ability to prevent your web server from making outbound connections on its own, even if using a source port of 80. This is the difference between a stateful firewall and an access control list. Using a simple router ACL, all you would need to do is define your source port to be 80 and you could make any outgoing connection you want. Using a stateful firewall, that attempt would fail unless it were in response to a specific connection that was initiated remotely (and in the case of TCP, still open in terms of the TCP state machine).
That's a long way of getting to your question (sorry). I hope I made it less confusing.
So while you don't need to make a rule specifically for return traffic in your example, if you're traversing two different firewall rulesets, one applied to inbound traffic on interface A and the other applied to outbound traffic on interface B, then yes, you will need rules that match new connections on each of those for the traffic to flow (but not for the other two rulesets for traffic in to interface B and out of interface A, since allowing established and related traffic covers that).
Thank you for these detailed explanations !
Thus I have a remaining question: if I don't need IN ruleset, and implement only OUT ruleset (with for example eth1: source=client; destination=server; protocol=http; action=accept), do I have the same security level that implementing both IN and OUT rulesets ?
Or, if I don't need OUT ruleset but ONLY implement IN ruleset (which will be eth0: source=client; destination=server; protocol=http ; action=accept for this example), will this work and be enough secured too ?
Thank you very much !
I may have confused you. My reference to only needing a firewall rule in one direction was in respect to making a specific rule to permit return traffic as opposed to an overly broad one in the case where a stateful firewall didn't exist.
VyOS is a stateful firewall. That means return traffic can be allowed by matching and allowing traffic by an established state.
You should pick the boundary where you want to filter traffic. This can be on your "outside" or "WAN" interface, or on your "inside" or "LAN" interface.
If you have more than one inside network I recommend filtering on each LAN interface rather than your outside interface since this allows you to create firewall policy that will apply to traffic between inside networks as well as to and from the outside. It that way does mean more rules if you have multiple LAN networks but you also have better control.
For the interface where you choose to filter you should create both an IN and OUT policy.
The way you write this policy will be swapped depending on whether you choose to filter at your WAN or your LAN. In this example I'll assume you have more than one LAN and would like to filter between them so we will apply a filter on your LAN and leave WAN without IN or OUT filtering.
The reason we only establish the border on either WAN or LAN is that if you do both you will be forced to configure policy twice. Once for the traffic coming in to the LAN port and once for the traffic going out the WAN port.
There are of course other ways you can do this such as only filtering IN or only filtering OUT but applying the filter to both WAN and LAN but my recommendation is to always filter on your client-facing interfaces (e.g. LAN).
In your example I'll call the client the remote user in the Internet coming over the WAN port (eth0) and the server the local host behind your firewall on the LAN port (eth1).
The LAN-IN firewall would be for traffic initiated from the server going to the Internet. For a simple setup let's say this is OK and we don't want to restrict that traffic. In that case we would create a LAN-IN policy that:
- Allows ESTABLISHED and RELATED traffic (so the server is able to respond to NEW connections from clients)
- Drop INVALID state traffic (in Linux it must be explicitly dropped or it will leak through)
- Allows any NEW connections from the LAN
- DROP everything else (usually just implemented as setting the default policy to drop for the ruleset)
That's pretty much it. If we didn't want the server to be able to make any outgoing connections we would leave out rule 3.
For the LAN-OUT firewall (traffic leaving the LAN interface to the server) we would use a policy like this:
- Allows ESTABLISHED and RELATED traffic (so the response traffic to NEW connections initiated by the server would be allowed)
- Drop INVALID state traffic
- Allow any NEW connections to the SERVER that use protocol TCP and port 80 (HTTP)
- DROP everything else (usually just implemented as setting the default policy to drop for the ruleset)
The LAN-OUT policy will only allow WAN traffic to the LAN if it was an already established connection (as permitted in LAN-IN) or if it's an HTTP connection to the SERVER. All other traffic will be dropped.
Allowing ESTABLISHED and RELATED traffic is what permits return traffic for connections that were permitted to be made in the rest of the policy.
When you enable this globally in VyOS it has the same effect as including it in every firewall policy and makes configuration more simple.
In a stateful firewall you only need to create rules to allow the initial connection and state matching takes care of exceptions for return traffic dynamically. That's what I was getting at. A LAN-IN policy like above where everything from the LAN is allowed anyway is almost identical to not having a LAN-IN policy at all but I still recommend using an explicit policy so that you can filter traffic if you need to.
If you have more than one "inside" network (e.g. LAN-1 and LAN-2) you would create an IN and OUT policy for each of them. If you wanted to allow them to connect to one another you would then need to create rules to allow that traffic.
Finally while you don't need to define IN or OUT policy on your WAN interface in this example (because you've done so on your LAN interface) you should make sure that you have a LOCAL firewall policy (which filters traffic destined to the firewall itself) to restrict things like SSH or NTP or SNMP to your VyOS system. This can be a single policy that you apply to each interface or you can create a different policy based on "inside" and "outside" interfaces which is my preference.
This stuff is really more under the umbrella of firewall basics than anything specific to VyOS, by the way.
I have a few draft documents that try to make this more clear but I haven't gotten around to finishing them. You might still find them useful: