411

I am hosting special HTTP and HTTPS services on the ports 8006 and 8007 respectively. I use iptables to "activate" the server; i.e. to route the incoming HTTP and HTTPS ports:

iptables -A INPUT -i eth0 -p tcp --dport 80 -j ACCEPT
iptables -A INPUT -i eth0 -p tcp --dport 443 -j ACCEPT
iptables -A INPUT -i eth0 -p tcp --dport 8006 -j ACCEPT
iptables -A INPUT -i eth0 -p tcp --dport 8007 -j ACCEPT
iptables -A PREROUTING -t nat -i eth0 -p tcp --dport 80 -j REDIRECT --to-port 8006 
iptables -A PREROUTING -t nat -i eth0 -p tcp --dport 443 -j REDIRECT --to-port 8007  
iptables -A OUTPUT -t nat -d 127.0.0.1 -p tcp --dport 80 -j REDIRECT --to-ports 8006
iptables -A OUTPUT -t nat -d 127.0.0.1 -p tcp --dport 443 -j REDIRECT --to-ports 8007 

This works like a charm. However I would like to create another script that disables my server again; i.e. restore iptables to the state it was in before running the lines above. However I am having a hard time figuring out the syntax to remove these rules. The only thing that seems to work is a complete flush:

iptables -F
iptables -X
iptables -t nat -F
iptables -t nat -X
iptables -t mangle -F
iptables -t mangle -X
iptables -P INPUT ACCEPT
iptables -P FORWARD ACCEPT
iptables -P OUTPUT ACCEPT

But that will also delete other iptables rules which is undesired.

alexwlchan
  • 5,699
  • 7
  • 38
  • 49
Jeroen Ooms
  • 31,998
  • 35
  • 134
  • 207
  • 5
    I've found that it is better to use `-I` instead of `-A` for `ACCEPT` lines. This is because typically, the last line (for `INPUT` chain for example) is a `DROP` or `REJECT` and you want your rule to come before that. `-A` puts the new rule after the last rule, while `-I` puts it at the start. – Mark Lakata Jul 30 '14 at 20:30

8 Answers8

580

Execute the same commands but replace the "-A" with "-D". For example:

iptables -A ...

becomes

iptables -D ...
T.Todua
  • 53,146
  • 19
  • 236
  • 237
Eli Rosencruft
  • 7,232
  • 1
  • 19
  • 17
  • 11
    If you have several rules of a kind, it will not remove all of them. – ETech Mar 27 '14 at 08:22
  • 8
    try to execute this -D command multiple times, and it will delete all of them. – Zhenyu Li Sep 17 '14 at 11:09
  • 5
    i executed the same command, but with -D instead of -I. But i get BAD RULE (does a matching rule exists)... – Ben Feb 27 '15 at 11:33
  • 5
    If you added a rule with `-I` or `-R`, you can still delete it with `-D`. – David Xia Mar 06 '15 at 04:03
  • Just a note. I create a rule with 'iptables -A ...' and the rule appeared but was not effective. Upon using 'iptables -D ...' the response was **Bad rule**. Even so the rule was deleted. The rule was visible with 'sudo iptables -nvL'. The chain I used was 'INPUT'. – BenDavid Aug 06 '19 at 14:36
  • 1
    Downvoting because other answers explain how to remove from specific tables, which is pretty important if the rule you're trying to remove is in a specific table. – Andrew Koster Mar 19 '20 at 02:40
  • Its not that easy. If there is a comment you cant just remove the same value, you have to adjust the command. – Dave Aug 30 '22 at 20:31
537

You may also use the rule's number (--line-numbers):

iptables -L INPUT --line-numbers

Example output :

Chain INPUT (policy ACCEPT) 
    num  target prot opt source destination
    1    ACCEPT     udp  --  anywhere  anywhere             udp dpt:domain 
    2    ACCEPT     tcp  --  anywhere  anywhere             tcp dpt:domain 
    3    ACCEPT     udp  --  anywhere  anywhere             udp dpt:bootps 
    4    ACCEPT     tcp  --  anywhere  anywhere             tcp dpt:bootps

So if you would like to delete second rule :

iptables -D INPUT 2

Update

If you use(d) a specific table (eg nat), you have to add it to the delete command (thx to @ThorSummoner for the comment)

sudo iptables -t nat -D PREROUTING 1
domi27
  • 6,903
  • 2
  • 21
  • 33
  • I Like this better than the chosen solution because it gives line numbers and it's easier to use. Thanks! – hsanders Oct 23 '13 at 16:46
  • 6
    Both solutions are nice, but this one won't work in a scripted setting when the line number is unknown. So the other solution is more general, and therefore more correct, IMO. – Jeroen Ooms Nov 17 '13 at 05:14
  • 2
    Well if you don't know the line you may use a comment (like answer among) or do a grep for your rule : `iptables -L INPUT --line-numbers | grep -oP "([0-9]{1,3}).*tcp.*domain" | cut -d" " -f1` – domi27 Nov 24 '13 at 18:44
  • 8
    This is fine only if the table cannot have rules inserted at any point in time. Otherwise the line numbers could change between observing them and executing the delete rule. In such a case, it is unsafe to assume that the time window is so short that "it's unlikely ever to happen". – Nick May 13 '14 at 10:18
  • 22
    Remember that if you delete one rule, the line numbers of the remainder change. So, if you need to delete rule 5, 10, and 12... delete them 12, 10, then 5. – TomOnTime Oct 02 '14 at 16:58
  • When you created the rule yourself, it make sense to delete it in the same way as the [choosen answer](http://stackoverflow.com/a/10197461/1023341) but when it's a rule that was created by someone else, this is a very nice way. – gkephorus Feb 11 '15 at 09:05
  • 5
    When trying to delete PREROUTING rules I had to specify `-t nat`, eg: `sudo iptables -t nat --line-numbers -L`, and delete them with `-t nat` too, eg: `sudo iptables -t nat -D PREROUTING 1` (May be worth adding to the answer?) – ThorSummoner Jun 12 '15 at 17:31
  • Very well put together answer. I just made a question on iptables too, any chance you could give me your thoughts/wisdom on it? http://stackoverflow.com/questions/43508741/iptables-script-to-block-all-internet-access-except-for-desired-applications – Webeng Apr 20 '17 at 01:40
  • How to delete a **range** of numbers by this syntax? – Suncatcher Aug 06 '17 at 05:39
39

The best solution that works for me without any problems looks this way:
1. Add temporary rule with some comment:

comment=$(cat /proc/sys/kernel/random/uuid | sed 's/\-//g')
iptables -A ..... -m comment --comment "${comment}" -j REQUIRED_ACTION

2. When the rule added and you wish to remove it (or everything with this comment), do:

iptables-save | grep -v "${comment}" | iptables-restore

So, you'll 100% delete all rules that match the $comment and leave other lines untouched. This solution works for last 2 months with about 100 changes of rules per day - no issues.Hope, it helps

ETech
  • 1,613
  • 16
  • 17
  • 2
    In case you don't have iptables-save/restore: `iptables -S | grep "${comment}" | sed 's/^-A //' | while read rule; do iptables -D $rule; done` – Mansour Aug 05 '14 at 15:45
  • For a real-life usage: CRON 1) delete old `spamhaus` iptables bans, 2) grab http://www.spamhaus.org/drop/, 3) grep for CIDR IP's and `iptables -A INPUT -s $ip_cidr -j -m comment --comment "spamhaus"` – Xeoncross Jan 15 '15 at 20:26
  • 2
    @Mansour Or `iptables -S | sed "/$comment/s/-A/iptables -D/e"` ;) like [this](http://stackoverflow.com/a/29058984/171318) – hek2mgl Mar 15 '15 at 18:41
  • 1
    @hek2mgl sed doesn't give up, does it? =] Thanks, I'll keep this capability in mind (I'll be forgetting the syntax in a day). – Mansour Mar 15 '15 at 21:52
  • sed is used just to remove "-" from uuid. In any case, once you got sed syntax - it will be never forgotten. )) – ETech Sep 08 '15 at 21:18
36

First list all iptables rules with this command:

iptables -S

it lists like:

-A XYZ -p tcp -m state --state NEW -m tcp --dport 22 -j ACCEPT

Then copy the desired line, and just replace -A with -D to delete that:

iptables -D XYZ -p ...
T.Todua
  • 53,146
  • 19
  • 236
  • 237
Wladdy Lopez
  • 485
  • 4
  • 5
  • 2
    Be aware! This is incomplete answer. From manual about "-S": "Like every other iptables command, it applies to the specified table (filter is the default). ". So, in case of using this switch - should be repeated for all tables: nat, mangle, etc – pmod Feb 01 '16 at 10:28
  • 1
    In my case, I get `iptables: Bad rule (does a matching rule exist in that chain?).` What now? – Abdull Feb 16 '16 at 12:59
  • 1
    Ah, figured it out - my delete command was missing the table specification. So in case you list all rules from table `nat` with `sudo iptables -S -t nat` and you want to delete one of the returned rules, copying is not enough. You have to add `-t nat`, e.g. `sudo iptables -D ... -t nat`. – Abdull Feb 16 '16 at 13:30
  • Easy to understand! – sknight Nov 13 '19 at 02:13
18

Use -D command, this is how man page explains it:

-D, --delete chain rule-specification
-D, --delete chain rulenum
    Delete  one  or more rules from the selected chain.  
    There are two versions of this command: 
    the rule can be specified as a number in the chain (starting at 1 for the first rule) or a rule to match.

Do realize this command, like all other command(-A, -I) works on certain table. If you'are not working on the default table(filter table), use -t TABLENAME to specify that target table.

Delete a rule to match

iptables -D INPUT -i eth0 -p tcp --dport 443 -j ACCEPT

Note: This only deletes the first rule matched. If you have many rules matched(this can happen in iptables), run this several times.

Delete a rule specified as a number

iptables -D INPUT 2

Other than counting the number you can list the line-number with --line-number parameter, for example:

iptables -t nat -nL --line-number
cizixs
  • 12,931
  • 6
  • 48
  • 60
7

Assume that, if you want to remove NAT rules,

List the appended IPtables using the command below,

# sudo iptables -L -t nat -v

Chain PREROUTING (policy ACCEPT 18 packets, 1382 bytes)
 pkts bytes target     prot opt in     out     source               destination         
    7   420 DNAT       tcp  --  any    any     anywhere             saltmaster           tcp dpt:http to:172.31.5.207:80
    0     0 DNAT       tcp  --  eth0   any     anywhere             anywhere             tcp dpt:http to:172.31.5.207:8080

If you would like to remove the nat rule from the IPtables, just execute the command,

# sudo iptables -F -t nat -v

Flushing chain `PREROUTING'
Flushing chain `INPUT'
Flushing chain `OUTPUT'
Flushing chain `POSTROUTING'

Then, you can verify that,

# sudo iptables -L -t nat -v
Lakshmikandan
  • 4,301
  • 3
  • 28
  • 37
4

You can also use the following syntax

 iptables -D <chain name> <rule number>

For example

Chain HTTPS 
    target     prot opt source               destination
    ACCEPT     all  --  anywhere             anywhere
    ACCEPT     all  --  10.0.0.0/8           anywhere
    ACCEPT     all  --  182.162.0.0/16       anywhere

To delete the rule

ACCEPT all -- 10.0.0.0/8 anywhere

iptables -D HTTPS 2
1

Here is a one liner that deletes the iptables rules that match a search. This example searches for all the rules that match the IP address 192.168.1.27 and removes all of them. You would edit in your own search criteria in place of that IP address.

eval `iptables --list-rules | grep '192.168.1.27' | sed 's/^-A /iptables -D /g;s/$/;/g'`

How it works:

It uses the accepted answer to this question and to run rules with -D rather than -A.

  • iptables --list-rules lists all the existing rules. Even if you added them with -I or -R, this list shows them all with -A
  • | grep '192.168.1.27' filters the list to just the rules that you want removed (in this case the rules for some specific IP address.)
  • | sed does a search and replace
    • s/^-A /iptables -D /g replaces the -A at the start of each rule with iptables -D so that it becomes an executable command to remove the rule.
    • s/$/;/g replaces the end of each rule with a semi-colon to separate multiple commands when they are run
  • eval ... takes all that output and runs it as a script.
Stephen Ostermiller
  • 23,933
  • 14
  • 88
  • 109