Howto: iptables and MAC-based access control
Last updated 9 August 2023
In many ad-hoc networks it is relatively simple to gain access to the physical network. In order to find what IP ranges are required one merely needs to sniff a bit (tcpdump), or even better, set your network card to use dhcp. This allows practically anyone to obtain access to an ad-hoc network.
The question now arrises what do I mean with an ad-hoc network. Well, an ad-hoc network from my point of view is a network that is expanded by any and all means by anyone with physical access to the network in order to add more machines to the network. This is a very bad situation.
Whilst the concepts in this article has been developed due to a need that has arrisen from such an ad-hoc network the concepts should be equally applicable to other environments too where specific machines should be allowed certain accesses and others denied it. This is especially useful if users cannot be trusted to play by the rules (always).
MAC-based access control, or hardware address access control means to use the hardware address instead of the IP addresses to base one's firewall rules upon. In other words, we will not be using hostnames (that maps to IP addresses) to base our access control on.
There are two aproaches, the first I like to call "no unprivileged packets beyond this point" whilst I refer to the second as the "MAC check on service request". The first of these methods are less flexible but equally effective and is probably easier to use in cases where users are clasified into either allowed or disallowed whilst the second is much more flexible and allows for different sets of MACs for different services.
Initially I developed the first of these methods on request of our gateway admin who wanted to be a BOFH but didn't know how. Anyway, what we have now is a combination of his previously ineffective firewall, but with at least only certain users being able to abuse that. The second was developed when I myself needed some way to split that same group of users into those who are allowed to use me as a gateway, another group (that happens to include the first) of legitimate network users who is allowed to access services on my machine, and the rest (those who are not allowed to use my machine at all - except for dhcp).
In the "no unprivileged packets" method I basically implemented a pre-emptive strike, implementing a chain that only returns should the MAC be a valid legitimate user. The second implements a chain that accepts the packet should the MAC be legit, or else return. The return part may be argued, the alternative where the packet is dropped where it does not match may also be legit but is slightly less flexible (also less errorprone).
Building the MAC list
To keep things simple, I'm going to assume a list of 3 privileged users that are allowed to use services that cause internet traffic to be generated:
00:0C:F1:6C:CC:7D 00:E0:29:17:7E:78 00:A0:CC:D4:FE:A7
What I show below is a small part of the output for iptables -L -v -n, making use of the "MAC check on request" method:
Chain PRIV_MACS (2 references) pkts bytes target prot opt in out source destination 0 0 ACCEPT all -- * * 0.0.0.0/0 0.0.0.0/0 MAC 00:0C:F1:6C:CC:7D 0 0 ACCEPT all -- * * 0.0.0.0/0 0.0.0.0/0 MAC 00:E0:29:17:7E:78 0 0 ACCEPT all -- * * 0.0.0.0/0 0.0.0.0/0 MAC 00:A0:CC:D4:FE:A7
This shows what the chain should look like that does the MAC-check once a service has been requested. The following commands can be used to construct the above ruleset:
iptables -N PRIV_MACS iptables -A PRIV_MACS -m mac --mac-source 00:0C:F1:6C:CC:7D -j ACCEPT iptables -A PRIV_MACS -m mac --mac-source 00:E0:29:17:7E:78 -j ACCEPT iptables -A PRIV_MACS -m mac --mac-source 00:A0:CC:D4:FE:A7 -j ACCEPT
In order to use the "no unprivileged packets" method one would change the ACCEPT to RETURN and also add the following command to the previous set:
iptables -A PRIV_MACS -j DROP
DENY can also be used but I like being a BOFH.
Using the MAC list
That should be sufficient to illustrate how to create a single chain with allowable MACs. How to use this is not so obvious. A typical ruleset would look like:
Chain INPUT (policy DROP 37324 packets, 3753K bytes) pkts bytes target prot opt in out source destination 3491K 676M ACCEPT all -- * * 0.0.0.0/0 0.0.0.0/0 state RELATED,ESTABLISHED 27425 1737K ACCEPT all -- lo * 0.0.0.0/0 0.0.0.0/0 3245 174K ACCEPT tcp -- eth0 * 0.0.0.0/0 0.0.0.0/0 tcp flags:0x16/0x02 multiport dports 22,80,53,139,445 250 15000 PRIV_MACS tcp -- eth0 * 0.0.0.0/0 0.0.0.0/0 tcp dpt:3128 flags:0x16/0x02 59695 6496K ACCEPT udp -- eth0 * 0.0.0.0/0 0.0.0.0/0 multiport dports 53,67,123,137,138 15803 957K ACCEPT all -- eth0 * 0.0.0.0/0 0.0.0.0/0 MAC 00:48:54:89:BC:1D
Walking through these rules:
Since I prefer the "check on request method" I'm only going to describe what would be done differently for the "no unprivileged packets" method.
Modifications to use the other scheme
The only thing that would really change are the order of the rules. One would need to first have all the non-MAC related stuff (ie, rule 4 needs to move to the end) and then one would call the PRIV_MACS rule before that.
To see why this is more restrictive than the method described above, consider what happens when you have two MAC lists that need different accesses. The method here would require that the more restrictive MAC list be a subset of the bigger list. Not always the case. Both methods allow for sub-groups by simply calling another MAC list - this should be the last call in the MAC list. The "no unprivileged packets" however does not allow for aggregation of two seperate MAC lists.