Iptables
A firewall is a piece of hard- or software, that regulates traffic across the interface between networks with different levels of trust.
- iptables is more-or-less the default (software) firewall on Linux. It's the successor to ipchains and in some distributions, it seems to be succeeded by nftables
- iptables can filter (block) traffic, as well as throttle.
Filtering & OSI model
Firewalls, including iptables
, typically operate on layer 3 and layer 4 of the OSI network model, although they often can extend a bit beyond that. The reason why this isn't black-and-white, is because the OSI model is a theoretical framework. It's never implemented with such clear boundaries between its layers.
Let's have a look at the OSI model, how iptables can interact with the different layers, and where its limitations are - To make sure that iptables is the right tool for the job at hand.
OSI model
Layer | Remarks |
---|---|
1. Physical layer |
|
2. Data link layer |
Sublayers:
|
3. Network layer |
Determines the best physical path for data transmission and handles packet forwarding including routing through intermediate routers:
|
4. Transport layer |
End-to-end data transfer including error detection and correction:
|
5. Session Layer |
Manages sessions between applications. It establishes, manages, and terminates connections between local and remote applications:
|
6. Presentation layer | Translates data between the application layer and the network. It translates data from a format used by the application layer into a common format and vice versa. This layer also handles encryption and decryption
Maybe using protocols on top of other protocols, is an example of this layer. E.g.:
|
7. Application layer | Provides network services directly to applications. It interacts with software applications that implement a communicating component.
Examples of applications:
Examples of protocols:
|
Filtering at Layer 3: Network layer
The Network layer is responsible for packet forwarding including routing through intermediate routers. iptables can filter packets based on various Layer 3 attributes, such as:
Attribute | Example |
---|---|
Source IP address | sudo iptables -A INPUT -s 198.168.1.1 -j DROP
|
Destination IP address | sudo iptables -A OUTPUT -d 8.8.8.8 -j ACCEPT
|
Protocol (e.g., TCP, UDP, ICMP) | sudo iptables -A INPUT -p icmp -j ACCEPT
|
Filtering at Layer 4: Transport layer
The Transport layer is responsible for end-to-end communication and error handling. iptables can filter packets based on various Layer 4 attributes. E.g.:
Attribute | Example |
---|---|
Source port | sudo iptables -A INPUT -p tcp --sport 22 -j ACCEPT
|
Destination port | sudo iptables -A INPUT -p tcp --dport 80 -j ACCEPT
|
TCP flags | sudo iptables -A INPUT -p tcp --tcp-flags SYN,ACK SYN -j DROP
|
Combined filtering at Layer 3 & 4
You can combine attributes from Layer 3 & 4. E.g.:
sudo iptables -A INPUT -p tcp -s 192.168.1.1 --dport 22 -j ACCEPT
Filtering at Layer 2: Data link layer?
iptables doesn't really do any filtering using Layer 2 attributes, but related tools like ebtables
can filter ethernet frames at this layer.
Filtering at Layer 5: Session layer
Extending iptables' functionality 'down' to layer 2, wasn't very hopeful. Extending this in the other direction, goes much better: iptables can track the state of connections, allowing for stateful packet inspection, using keywords NEW
, ESTABLISHED
and RELATED
.
I guess this is actually critical, for example, to filter FTP traffic: The handshake is done over port 22, but the actual communication subsequently happens over an arbitrary port. Without some awareness of state, this would be very difficult for a firewall to manage.
Example:
sudo iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
BTW: Arguably, this is layer 3 and/or layer 4 stuff. I assumed it was layer 5 - Whatever: The OSI model is terribly theoretical anyway, so who cares?
Filtering at Layer 6: Presentation layer?
iptables can do some stuff at Layer 6, but like most stuff outside layer 3 & 4, these are rather extra-curricular activites, and require additional add-ons.
Examples:
Tracking FTP connections
Maybe a good example: The nf_conntrack_ftp
module that helps iptables to track FTP connections, which involves parsing commands and data trandfers, embedded in the payload, to track the control channels and data channels. E.g.:
sudo modprobe nf_conntrack_ftp sudo iptables -A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT
SSL/TLS interception
Use iptables to redirect HTTPS traffic to a local SSL interception proxy:
sudo iptables -t nat -A PREROUTING -p tcp --dport 443 -j REDIRECT --to-port 8443
Filtering at Layer 7: Application layer
Finally, we made it to what is probably "the big disappointment" for many a new firewall user, when they discover that what they actually want, is not what a firewall is designed for. And again, with a bit of force, quite nice results can be obtained. But iptables isn't the right tool for it.
User-agent filtering
For me, the "big disappointment" was, when I discovered that a firewall isn't the appropriate tool for filtering incoming traffic on a webserver, based on user-agent strings
- iptables can be more-or-less forced to do this, e.g.:
sudo iptables -A INPUT -p tcp --dport 80 -m string --string "User-Agent: MyCustomAgent" --algo bm -j DROP
, but it isn't unreliable at it, and not efficient either - nftables, usually seen as the successor to iptables, is a bit better at this, but it's still using the wrong tool.
More appropriate tools for application level filtering are e.g.:
- Reverse proxy servers or application firewalls: Squid, HAProxy, Nginx
- Web application firewalls (WAFs): ModSecurity, which can be integrated with Apache and Nginx.
Chains, rules & order
iptables applies rules in a specific order. It is important to thoroughly understand how that works
Chains
Rules in iptables are organized into chains, which are lists of rules that packets traverse as they pass through the firewall. The main chains are:
- INPUT: Handles incoming packets destined for the local machine.
- FORWARD: Handles packets being routed through the machine (i.e., packets not destined for the local machine but for another machine).
- OUTPUT: Handles packets originating from the local machine.
For packets that do not match any rule in a specific chain, they may be subject to additional chains or rules depending on the configuration:
- PREROUTING: Chain used for packets before routing decisions are made.
- POSTROUTING: Chain used for packets after routing decisions are made.
The order of these chains and their interactions can affect packet processing and should be considered when configuring complex firewall rules. Additional chains can be defined. E.g., fail2ban and ufw define additional chains including their place amonst the other chains
Incoming Packet Handling
- When a packet arrives, it is first processed by the INPUT chain if it is destined for the local machine
- It is processed by the FORWARD chain if it is being routed through the machine
- It is processed by the OUTPUT chain if it is originating from the local machine.
The remainder of this article is limited to the INPUT chain, unless stated otherwise.
Rule Matching
When a packet arrives:
- The packet is compared against each rule in the chain, starting from the top
- A rule is found that matches the packet
- The rule is executed with regards to this packet
- No further matching happens for this packet.
With other words: The first rule (starting from the top) that matches the packet, gets executed.
Rule matching: Rules & subchains
When a chain (INPUT chain in this case) contains a mix of rules and subchains, the same principle applies:
- Start from the top
- first rule that matches, gets executed, regardless whether this rule is included in a subchain or in the INPUT chain.
Example:
$ sudo iptables -L INPUT Chain INPUT (policy ACCEPT) target prot opt source dest comments --------------- ---- --- ------ -------- --------------------------- ACCEPT all -- 1.2.3.4 anywhere /* Office NL */ f2b-a2-get-dos tcp -- anywhere anywhere multiport dports http,https f2b-a2-noscript tcp -- anywhere anywhere multiport dports http,https f2b-a2-auth tcp -- anywhere anywhere multiport dports http,https ACCEPT tcp -- anywhere anywhere tcp dpt:ssh ACCEPT tcp -- anywhere anywhere tcp dpt:12345 ACCEPT all -- srv17 anywhere /* srv17 */ ACCEPT all -- 2.3.4.5 anywhere /* Office PL */
Someone at Office NL visits a site that is hosted on this server:
- The very first rule matches → It gets executed → Done
Someone at Office PL visits a site that is hosted on this server:
- When there is no matching rules in any of the
f2b
subchains, the last rule matches and will get executed - When there is a matching rule in any of the
f2b
subchains, it will get executed, becauce it comes earlier.
Actions
The action specified by the matching rule is applied to the packet. Actions can include ACCEPT, DROP, REJECT, LOG, or other targets. If a rule specifies an action (e.g., DROP), no further rules in the chain are processed for that packet. The packet is handled according to the action specified. Default Policy:
If no rules in the chain match the packet, the default policy for the chain is applied. The default policy is specified using iptables -P <CHAIN> <TARGET>. For example, if the default policy for the INPUT chain is DROP, packets that do not match any rules in the INPUT chain will be dropped.
Example
Consider the following iptables rules in the INPUT chain:
sudo iptables -A INPUT -p tcp --dport 22 -j ACCEPT sudo iptables -A INPUT -p tcp --dport 80 -j DROP sudo iptables -A INPUT -p tcp --dport 443 -j ACCEPT
- Rule 1: -p tcp --dport 22 -j ACCEPT – Allows incoming TCP traffic on port 22 (SSH).
- Rule 2: -p tcp --dport 80 -j DROP – Blocks incoming TCP traffic on port 80 (HTTP).
- Rule 3: -p tcp --dport 443 -j ACCEPT – Allows incoming TCP traffic on port 443 (HTTPS).
If a packet destined for port 80 arrives, it will be processed as follows:
- The packet is compared against Rule 1. It does not match (port 22), so the rule is not applied
- The packet is compared against Rule 2. It matches (port 80), so the packet is dropped, and no further rules are checked
- Rule 3 is not evaluated because Rule 2 has already handled the packet.
Frontends
- ufw and firewalld are command line front-ends to iptables (and in the case of firewalld, also for nftables), to make it a bit easier to interact with iptables
- gufw is a graphical frontend for ufw
- firewall-config is a graphical front-end for firewalld.
If you are comfortable with directly interacting with iptables and have a good understanding of its syntax and functionality, you can certainly manage your firewall rules directly using iptables commands without needing any of the frontends like ufw, firewalld, gufw, or firewall-config. Each approach has its pros and cons.
I do prefer to directly use iptables, without any of these frontends.
Directly using iptables
Pros
- Fine-Grained Control: Directly using iptables allows you to configure detailed and specific rules, as you have full control over all the options and parameters available
- Flexibility: You can create complex rules and chains tailored to specific needs that might be difficult to achieve with higher-level tools.
Cons
- Complexity: iptables can be complex and unintuitive, especially for advanced configurations. Errors in rules or syntax can potentially disrupt network traffic
- Manual Management: You need to manually save and restore rules, manage rule ordering, and ensure consistency across reboots.
Using Frontends
Pros
- Ease of Use: Frontends like ufw, firewalld, gufw, and firewall-config provide a more user-friendly interface for managing firewall rules. They often come with simplified commands or graphical interfaces
- Configuration Management: These tools often handle saving and restoring rules automatically, and some provide better integration with system services and configuration management.
Cons
- Limited Flexibility: While these tools cover most common use cases, they might not offer the same level of granularity and flexibility as direct iptables commands
- Abstraction Layer: They abstract away iptables complexity, which can be a disadvantage if you need to perform advanced configurations or troubleshoot detailed issues
- Added dependency: I don't like the idea of introducing new layers of tools, that can break down.
ufw (Uncomplicated Firewall)
- Aimed at simplifying iptables management for Ubuntu and Debian-based systems
- Provides a straightforward command-line interface and basic functionality for most users.
In case ufw is installed on a server, while also directly using iptables, is probably not a problem. Otherwise:
sudo ufw disable
su apt remove --purge ufw
firewalld
- A dynamic firewall management tool that provides zones and services to manage rules
- Supports nftables as well as iptables and is commonly used on Red Hat-based systems.
gufw
- A graphical frontend for ufw on Ubuntu and other Linux distributions
- Provides a user-friendly graphical interface to manage firewall rules
firewall-config
- A graphical interface for managing firewalld, available on Red Hat-based distributions
- Allows for detailed configuration through a GUI.
List all rules
Various ways to list current list:
sudo iptables -L
sudo iptables -L -v -n
- Show contents of all chainssudo iptables -L --line-numbers
sudo iptables -L INPUT
sudo iptables -L INPUT
sudo iptables -L INPUT --line-numbers
sudo iptables -L INPUT --line-numbers -v
Switches:
-L
- list. By default, it shows the rules for chainsINPUT
,FORWARD
andOUTPUT
-v
- verbose: Include additional information, including the number of packets and bytes matched by each rule as well as the target and options associated with the rule-n
- numeric: Display IP addresses and port numbers in numeric form, rather than trying to resolve these to hostnames or service names. This makes the output faster as it avoids DNS lookups--line-numbers
: Include a line number for each rule - Handy for e.g., deleting rules.
Lastly, INPUT
is not a switch, but a chain.
Examples - without rules
Kinda boring, but an easy place to get familiar with this stuff:
Just list all rules in all chains:
$ sudo iptables -L Chain INPUT (policy ACCEPT) target prot opt source destination Chain FORWARD (policy ACCEPT) target prot opt source destination Chain OUTPUT (policy ACCEPT) target prot opt source destination
Only list rules in the INPUT list:
$ sudo iptables -L INPUT Chain INPUT (policy ACCEPT) target prot opt source destination strompf:src$ sudo iptables -L Chain INPUT (policy ACCEPT) target prot opt source destination Chain FORWARD (policy ACCEPT) target prot opt source destination Chain OUTPUT (policy ACCEPT) target prot opt source destination
Add some verbose info:
$ sudo iptables -L -v Chain INPUT (policy ACCEPT 1928 packets, 534K 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 1787 packets, 2525K bytes) pkts bytes target prot opt in out source destination
Default policies
Example of an INPUT chain with default policy DROP:
sudo iptables -L INPUT -n --line-numbers Chain INPUT (policy DROP) num target prot opt source destination 1 ufw-before-logging-input all -- 0.0.0.0/0 0.0.0.0/0 2 ufw-before-input all -- 0.0.0.0/0 0.0.0.0/0 3 ufw-after-input all -- 0.0.0.0/0 0.0.0.0/0 4 ufw-after-logging-input all -- 0.0.0.0/0 0.0.0.0/0 5 ufw-reject-input all -- 0.0.0.0/0 0.0.0.0/0 6 ufw-track-input all -- 0.0.0.0/0 0.0.0.0/0
Let's change that to ACCEPT:
sudo iptables -P INPUT ACCEPT
First line of the output above, now reads
Chain INPUT (policy ACCEPT)
Drop rules
E.g.:
- Display rules including line numbers:
sudo iptables -L --line-numbers
- Delete rules, referring to chain and line number:
sudo iptables -D INPUT 12
Flush (delete) all rules
Use -F
or --flush
to flush or delete rules - Not to flush chains.
To get rid of all rules in the INPUT chain:
sudo iptables -F INPUT
To get rid of all rules:
sudo iptables -F
Flush (delete) chains
Use -X
or --delete-chain
to remove a chain. It is only possible to flush empty chains.
Reorder rules
It's critical to have the rules in the correct order, as they are executed in order. However, there is no direct way for doing so. To reorder rules in iptables, you typically need to delete and re-add the rules in the desired order:
View current rules
First, list the current rules to see their order:
sudo iptables -L --line-numbers
Delete the rules
Note the line numbers of the rules you want to reorder. You’ll need to delete them using their line numbers. For example, to delete the first rule in the INPUT chain:
sudo iptables -D INPUT 1
Repeat this for all the rules you want to reorder.
Add the rules in the desired order
After deleting the rules, re-add them in the order you desire. For example, to add a rule allowing SSH traffic:
sudo iptables -A INPUT -p tcp --dport 22 -j ACCEPT
Save the new configuration
Finally, save the iptables configuration to ensure it persists across reboots.
On systems using iptables-persistent
:
sudo netfilter-persistent save
On systems using systemd
to manage iptables:
sudo service iptables save
Reorder chains
It's quite easy to reorder chains.
Rules are not automatically saved
It seems that by default, newly added rules are just kept in memory. When the server reboots or otherwise is on for a long time, these rules seem to disappear.
Location & files
The usual location for storing these rules would be:
/etc/iptables/rules.v4
/etc/iptables/rules.v6
There seem to be several ways to make the rules persistent:
iptables-save & iptables-restore
One way to make these rules permanent, is through iptables-save
and iptables-restore
.
Export the rules from memory to storage:
sudo iptables-save > /etc/iptables/rules.v4
sudo ip6tables-save > /etc/iptables/rules.v6
Subsequently, restore these rules at startup:
sudo iptables-restore < /etc/iptables/rules.v4
sudo ip6tables-restore < /etc/iptables/rules.v6
iptables-persistent
On systems using iptables-persistent
:
sudo netfilter-persistent save
systemd
On some systems using systemd
:
sudo service iptables save
Interference with ufw?
Still not ready to accept that these rules are not stored by themselves? Than maybe check that ufw doesn't hijack these rules. Its rules are typically saved at /etc/ufw/
.
Sources
- https://stackoverflow.com/questions/19961394/where-iptables-store-rules-on-ubuntu-server
- https://stackoverflow.com/questions/9330694/how-to-permanently-update-iptables
- https://help.ubuntu.com/community/IptablesHowTo
CIDR
In specifying IP addresses, CIDR (Classless Inter-Domain Routing) notation can be used to specify ranges of IP addresses. This is the common formulation using
<IP address>/<number of fixed bits> <=> <Subnet base address>/<prefix length>
E.g.: 192.168.100.0/24
Subnets
To have an impression of the significance of the prefix length:
Prefix | Network mask | Available hosts |
---|---|---|
/16 | 255.255.0.0 | 65,536 - 2 = 65,534 |
/17 | 255.255.128.0 | 32,768 - 2 = 32,766 |
/18 | 255.255.192.0 | 16,384 - 2 = 16,382 |
/19 | 255.255.224.0 | 8,192 - 2 = 8,190 |
/20 | 255.255.240.0 | 4,096 - 2 = 4,094 |
/21 | 255.255.248.0 | 2,048 - 2 = 2,046 |
/22 | 255.255.252.0 | 1,024 -2 = 1,022 |
/23 | 255.255.254.0 | 512 - 2 = 510 |
Prefix | Network mask | Available hosts |
---|---|---|
/24 | 255.255.255.0 | 256 - 2 = 254 |
/25 | 255.255.255.128 | 128 - 2 = 126 |
/26 | 255.255.255.192 | 64 - 2 = 62 |
/27 | 255.255.255.224 | 32 - 2 = 30 |
/28 | 255.255.255.240 | 16 - 2 = 14 |
/29 | 255.255.255.248 | 8 - 2 = 6 |
/30 | 255.255.255.252 | 4 - 2 = 2 |
Bits, numbers & prefixes
Bits | Numbers |
---|---|
1 | {0, 1) ⇒ 2 = 2^1 |
2 | {00, 01, 10, 11} ⇒ 4 = 2^2 |
3 | {000, 001, 010, 011, 100, 101, 110, 111} ⇒ 8 = 2^3 |
4 | 2^4 = 16 |
5 | 2^5 = 32 |
6 | 2^6 = 64 |
7 | 2^7 = 128 |
8 | 2^8 = 256 |
Example: 192.168.1.0/24
- Base address:
192.168.1.0
- Prefix:
/24
- The prefix indicates that the first 24 bits of the IP address are the network part, and the remaining bits are for hosts within that network
- Subnet mask:
11111111.11111111.11111111.00000000
-255.255.255.0
-ff.ff.ff.0
- The subnet mask is straightforwardly derived from the prefix
- A
/24
prefix, leaves 32-24 = 8 bits = 2^8 = 256 addresses for the subnet - Substract 2 addresses from these (1 for the network address and 1 for broadcast), and there are '254 usuable addresses in this subnet.
- Network address:
192.168.1.0
- First usable address:
192.168.1.1
- Last usable address:
192.168.1.254
- Broadcast address:
192.168.1.255
.
Example: 192.168.1.15/24
- Let's now change the base address to make it look a bit less binary:
192.168.1.15
- The rest stays she same
/24
indicates that the first 24 bits are fixed ⇒ The base address is still192.168.1.0
So, it's less confusing to notate such a network as 192.168.1.0/24
, rather than 192.168.1.15/24
, because the 15 part will be stripped anyway.
Example: [168.119.64.245 - 168.119.64.254]
How to define in CIDR notation, the range of IP addresses from [168.119.64.245 - 168.119.64.254]?
Prefix
- 254-245 = 9 addresses ⇒ add network address + broadcast address ⇒ 11 addresses ⇒ round up to closest power of two: 16 = 4 bits ⇒
/28
/28
<=>11111111.11111111.11111111.11110000
.
Network address
- The first three bytes are easy:
168.119.64
- Concerning the last byte: It's like an AND operation: (245 AND 0b11110000)
- 245 = 0b11110101
- 0b11110101 AND 0b11110000 = 0b11110000 = 240
Putting it together
168.119.64.240/28
- Network address:
168.119.64.240
- First available address:
168.119.64.241
- Last available address:
168.119.64.254
- Broadcast address:
168.119.64.255
.
Example: [168.119.68.117 - 168.119.68.254]
- Total range: 137 hosts ⇒ 139 addresses ⇒ rounded up to closest power of 2: 256 ⇒ 8 bits ⇒
/24
- 8 bits left for submask - Network address:
168.119.68.64
-168.119.68.0b01000000
⇒ That's not possible: It has to be full1
from the left + it doesn't fit with the prefix - Network address:
168.119.68.0
.
Together: 168.119.68.0/24
.
Example: [168.119.65.44 - 168.119.65.123]
- Hosts: 123-44=79 hosts ⇒ Add 2 ⇒ 81 ⇒ round up to closest power of 2: 128 = 7 bits ⇒
/25
- Subnet mask:
11111111.11111111.11111111.10000000
- Network address: (168.119.65.44 AND 0xff.ff.ff.f0) <=> (168.119.65.0b00101100) = 168.119.65.0
Together: 168.119.65.0/25
:
- Subnet: [168.119.65.0 - 168.119.65.127]
- First host:
168.119.65.1
- Last host:
168.119.65.126
What may be confusing about this example, is that the network address doesn't start with an empty last byte.
Network address: Full bits on the left
One of the strange thangs about CIDR is, that the network address has to have full 1s at the left, because of the logical AND it performs with the subnet mask:
Binary | Decimal |
---|---|
00000000 | 0 |
10000000 | 128 |
11000000 | 192 |
11100000 | 224 |
11110000 | 240 |
11111000 | 248 |
11111100 | 252 |
11111110 | 254 |
Example: [168.119.65.254 - 168.119.65.255]
- Only 2 IP addresses ⇒ Add 2 ⇒ 4. This requires 2 bits ⇒
/30
- Network address:
168.119.65.254
- Together:
168.119.65.254/30
⇒ Not correct, as the network address itself, cannot be used - To expand, the network address becomes
168.119.0.0
with 12 bits for the mask - It's not practical to insist on having .255 as a usefull address, as it is broadcast address of a
/26
block.
Get the names of all chains
To get the names of only the chains:
sudo iptables -L -n | grep '^Chain' | awk '{print $2}'
Example of a server that appearantly, also uses ufw
:
sudo iptables -L -n | grep '^Chain' | awk '{print $2}' INPUT FORWARD OUTPUT ufw-after-forward ufw-after-input ufw-after-logging-forward ufw-after-logging-input ufw-after-logging-output ufw-after-output ufw-before-forward ufw-before-input ufw-before-logging-forward ufw-before-logging-input ufw-before-logging-output ufw-before-output ufw-logging-allow ufw-logging-deny ufw-not-local ufw-reject-forward ufw-reject-input ufw-reject-output ufw-skip-to-policy-forward ufw-skip-to-policy-input ufw-skip-to-policy-output ufw-track-forward ufw-track-input ufw-track-output ufw-user-forward ufw-user-input ufw-user-limit ufw-user-limit-accept ufw-user-logging-forward ufw-user-logging-input ufw-user-logging-output ufw-user-output
Throttling using 'limit'
Some examples:
Filter by frequency
Filtering by frequency - number of requests per time:
sudo iptables -A INPUT -p tcp --dport 80 -m limit --limit 10/s -j ACCEPT sudo iptables -A INPUT -p tcp --dport 80 -j DROP
Filter by bandwidth
To throttle traffic based on bandwidth rather than connection rate, you need to use the iptables hashlimit module or a combination of iptables and tc
(Traffic Control). For simple rate limiting with hashlimit, you can use:
sudo iptables -A INPUT -p tcp --dport 80 -m hashlimit --hashlimit 1/sec --hashlimit-burst 5 -j ACCEPT sudo iptables -A INPUT -p tcp --dport 80 -j DROP
Connections per IP
To limit the number of simultaneous connections per IP address, you can use the connlimit module:
sudo iptables -A INPUT -p tcp --dport 80 -m connlimit --connlimit-above 10 -j REJECT
Redundant rules
What happens if you twice introduce the same rule, e.g. sudo iptables -A INPUT -s 101.32.115.96 -j DROP
Well, it just being added twice:
- iptables doesn't have some tool to remove duplicate rules
- Maybe export rules and use e.g., Calc, for identifying redundancies.
Include a comment
Create a new rule including a comment:
sudo iptables -A INPUT -p tcp --dport 80 -j ACCEPT -m comment --comment "Allow HTTP traffic"
-m comment
: match (or load) the comment module--comment "..."
: Option for the comment module to add the supplied string as a comment
To add a comment to an existing rule: Remove the line and add it again.
Conditionally adding a rule
By default, you can add the same rule as often as you can. Quite inconvenient, as it makes script not being repeat-safe. An example of fixing this (assuming the INPUT
chain):
rule="-p tcp --dport 22 -j ACCEPT" # sudo iptables -C INPUT $rule >/dev/null 2>&1 && sudo echo "Rule exists already" sudo iptables -C INPUT $rule >/dev/null 2>&1 || sudo echo "Rule doesn't exists already"
or in action:
rule="-p tcp --dport 22 -j ACCEPT" sudo iptables -C INPUT $rule >/dev/null 2>&1 || sudo iptables -A INPUT $rule
$rule
in the second line, doesn't have to be quoted: It's aready formatted as expected by iptables.
Including comments
rule="-s 12.34.56.78 -j ACCEPT -m comment --comment 'server12'" sudo sh -c "iptables -C INPUT $rule >/dev/null 2>&1 || sudo iptables -A INPUT $rule"
Create a new chain
- Use
-N
or--new-chain
to create a new chain - It probably only makes sense to create subchains within an existing chain:
INPUT
,FORWARD
orOUTPUT
- However, AFAIK, there is no command to create a subchain within a specific chain. You have to do this in two steps: (1) Create the chain; (2) Move the chain to somewhere
- Use
-I
or--insert
to insert this chain at a specific place, rather than using-A
or--append
to add it at the end of theINPUT
chain - Use
-j
or--jump
to 'jump' or add this subchain to the existingINPUT
chain. Like the execution or cursor 'jumps' to to the subchain and continues execution there. This flag is required.
Example:
1. Create chain whitelist
and move it to the first line of the INPUT
chain:
sudo iptables -N whitelist sudo iptables -I INPUT 1 -j whilelist sudo iptables -L Chain INPUT (policy ACCEPT) target prot opt source destination whitelist all -- anywhere anywhere Chain FORWARD (policy ACCEPT) target prot opt source destination Chain OUTPUT (policy ACCEPT) target prot opt source destination Chain whitelist (1 references) target prot opt source destination
2. Add a chain whatever
and append it to INPUT
:
sudo iptables -N whatever sudo iptables -A INPUT -j whatever sudo iptables -L Chain INPUT (policy ACCEPT) target prot opt source destination whitelist all -- anywhere anywhere whatever all -- anywhere anywhere Chain FORWARD (policy ACCEPT) target prot opt source destination Chain OUTPUT (policy ACCEPT) target prot opt source destination Chain whatever (1 references) target prot opt source destination Chain whitelist (1 references) target prot opt source destination
3. Copy whatever
to the first line in INPUT
- AFAIK, you can't move a chain, only copy it
- After copying it, you delete the original chain. But be careful: There will now be two chains with the same name.
sudo iptables -I INPUT 1 -j whatever sudo iptables -L Chain INPUT (policy ACCEPT) target prot opt source destination whatever all -- anywhere anywhere whitelist all -- anywhere anywhere whatever all -- anywhere anywhere Chain FORWARD (policy ACCEPT) target prot opt source destination Chain OUTPUT (policy ACCEPT) target prot opt source destination Chain whatever (2 references) target prot opt source destination Chain whitelist (1 references) target prot opt source destination
Example: Block incoming traffic from a subnet
Example:
sudo iptables -A INPUT -p tcp -s 52.230.152.0/24 --dport 80 -j DROP sudo iptables -A INPUT -p tcp -s 52.230.152.0/24 --dport 443 -j DROP
-A INPUT
: Appends this rule to the INPUT chain-p tcp
: tcp protocol-s 52.230.252.0/24
: Source subnet--dport 80
: Destination port 80--dport 443
: Destination port 443-j DROP
: Matching packages should be dropped (blocked)
However, maybe more practical:
sudo iptables -A INPUT -p tcp -s 52.230.152.0/24 -j DROP
In this case, everything is dropped from this source, regardless of protocol. That seems just fine, since I'm filtering spam accounts.
Example: filter for your own IP address?
One day, I came across a strange rule where 123.45.67.89
is actually the IP address of the server on which iptables is running:
sudo iptables -A INPUT -s 123.45.67.89 -j DROP
- This was most likely a mistake: There is probably no reason to filter on your own IP address
- In theory, this wouldn't do much, as internal traffic doesn't go through the
INPUT
chain - However, it caused at least one problem:
curl
couldn't check sites on the same server anymore.
See also
Sources
- https://en.wikipedia.org/wiki/Iptables
- https://chatgpt.com/c/4e6c2aae-6f71-45a9-88bc-7a387118e436
- https://stackoverflow.com/questions/19961394/where-iptables-store-rules-on-ubuntu-server
- https://stackoverflow.com/questions/9330694/how-to-permanently-update-iptables
- https://help.ubuntu.com/community/IptablesHowTo