2

I have an iptables configuration problem that I cannot seem to solve.

I have a list of known, bad ip address that I block access to my server via ipset and iptables. The original approach has been to simply drop the connection via iptables if an IP matches the ipset list. And this worked just fine.

Now I decided to show a short server message instead of silently dropping the connection. I want to inform the visitor that access was blocked and provide instructions to resolve this, if he/she feels the block is unwarranted.

My revised appraoch is as follows, but please comment if you have a better solution:

since, AFAIK, iptables cannot simply return a custom response for the request (please correct if wrong, I know that reject can return some fixed response codes, but not custom messages), I want to redirect the matched/identified IP address to a dedicated 8911 port on the Nginx server, so that I can then handle the request in Nginx and return the error message.

The Nginx instance is hosted in a Docker container.

These are the two (2) iptables rules that I inserted into the NAT table to re-route http and https requests to the dedciated 8911 port if the IP got matched:

sudo iptables -t nat -I PREROUTING -p tcp --dport 80 -m set --match-set badips src,dst -j DNAT --to-destination 127.0.0.1:8911
sudo iptables -t nat -I PREROUTING -p tcp --dport 443 -m set --match-set badips src,dst -j DNAT --to-destination 127.0.0.1:8911

this is the NAT table after I inserted the rules:

Chain PREROUTING (policy ACCEPT)
num  target     prot opt source               destination
1    DNAT       tcp  --  anywhere             anywhere             tcp dpt:https match-set badips src,dst to:127.0.0.1:8911
2    DNAT       tcp  --  anywhere             anywhere             tcp dpt:http match-set badips src,dst to:127.0.0.1:8911
3    DOCKER     all  --  anywhere             anywhere             ADDRTYPE match dst-type LOCAL

Chain INPUT (policy ACCEPT) num target prot opt source destination

Chain OUTPUT (policy ACCEPT) num target prot opt source destination 1 DOCKER all -- anywhere !localhost/8 ADDRTYPE match dst-type LOCAL

Chain POSTROUTING (policy ACCEPT) num target prot opt source destination 1 MASQUERADE all -- 172.23.0.0/16 anywhere 2 MASQUERADE all -- 172.16.0.0/16 anywhere 3 MASQUERADE tcp -- 172.23.0.14 172.23.0.14 tcp dpt:8911 4 MASQUERADE tcp -- 172.23.0.14 172.23.0.14 tcp dpt:https 5 MASQUERADE tcp -- 172.23.0.14 172.23.0.14 tcp dpt:http

Chain DOCKER (2 references) num target prot opt source destination 1 RETURN all -- anywhere anywhere 2 RETURN all -- anywhere anywhere 3 DNAT tcp -- anywhere anywhere tcp dpt:8911 to:172.23.0.14:8911 4 DNAT tcp -- anywhere anywhere tcp dpt:https to:172.23.0.14:443 5 DNAT tcp -- anywhere anywhere tcp dpt:http to:172.23.0.14:80

As you can see the dedicated 8911 port is open on the Nginx Docker container (port is currently open to the outside world for testing, this will then later be made only accessible to the server host).

this is the Nginx server block config for the dedicated 8911 port:

server {
    listen 0.0.0.0:8911;
    server_name _;
    add_header X-Frame-Options "SAMEORIGIN" always;
    error_page 403 /e403.html;
location /e403.html{
    root /usr/share/nginx/html/error;
    access_log off;
    internal;
}

location / {
    access_log off;
    return 403;
}

}

The Docker and Nginx config itself for the dedicatd 8911 port works, b/c when I directly access the dedicated 8911 port via curl or web browser from a good (non-matched) IP, the correct error response is returned with status 403.

Unfortunately, when accessing the server with a blocked IP, iptables does NOT redirect the request to the dedicated 8911 port. The connection attempt times out (does not return anything), same as if the connection was simply dropped by iptables.

I have also changed the --to-destination IP (in the iptables rules) from 127.0.0.1 to either 0.0.0.0 or 172.23.0.14 (Docker interface) - it does not work.

My questions are:

(1) What must I change in my iptables rules to correctly redirect the IP matched request to the dedicated 8911 port?

(2) once iptables finally redirects the IP matched request correctly, would a https request that was IP matched (1st PREROUTING rule above) be re-directed to http with the dedicated 8911 port? e.g. https://foo.bar -> http://foo.bar:8911

(3) do you have any suggestions/recommendations for other approaches how to quick and efficiently serve an error response for IP matched requests?

UPDATE 2024.04.20:

removed the previous NAT prerouting rules and added these rules as suggested:

sudo iptables -t nat -I PREROUTING -p tcp --dport 80 -m set --match-set badips src,dst -j REDIRECT --to-ports 8911
sudo iptables -t nat -I PREROUTING -p tcp --dport 443 -m set --match-set badips src,dst -j REDIRECT --to-ports 8912

the NAT table now looks like this:

Chain PREROUTING (policy ACCEPT)
num  target     prot opt source               destination
1    REDIRECT   tcp  --  anywhere             anywhere             tcp dpt:https match-set badips src,dst redir ports 8912
2    REDIRECT   tcp  --  anywhere             anywhere             tcp dpt:http match-set badips src,dst redir ports 8911
3    DOCKER     all  --  anywhere             anywhere             ADDRTYPE match dst-type LOCAL

Chain INPUT (policy ACCEPT) num target prot opt source destination

Chain OUTPUT (policy ACCEPT) num target prot opt source destination 1 DOCKER all -- anywhere !localhost/8 ADDRTYPE match dst-type LOCAL

Chain POSTROUTING (policy ACCEPT) num target prot opt source destination 1 MASQUERADE all -- 172.25.0.0/16 anywhere 2 MASQUERADE all -- 172.16.0.0/16 anywhere 6 MASQUERADE tcp -- 172.25.0.13 172.25.0.13 tcp dpt:8912 7 MASQUERADE tcp -- 172.25.0.13 172.25.0.13 tcp dpt:8911 8 MASQUERADE tcp -- 172.25.0.13 172.25.0.13 tcp dpt:https 9 MASQUERADE tcp -- 172.25.0.13 172.25.0.13 tcp dpt:http

Chain DOCKER (2 references) num target prot opt source destination 1 RETURN all -- anywhere anywhere 2 RETURN all -- anywhere anywhere 6 DNAT tcp -- anywhere anywhere tcp dpt:8912 to:172.25.0.13:8912 7 DNAT tcp -- anywhere anywhere tcp dpt:8911 to:172.25.0.13:8911 8 DNAT tcp -- anywhere anywhere tcp dpt:https to:172.25.0.13:443 9 DNAT tcp -- anywhere anywhere tcp dpt:http to:172.25.0.13:80

also added an additional nginx server block for the port 8912 https redirect that loads the correct SSL cert...

server {
    listen 0.0.0.0:8912 ssl http2;
    server_name _;
    include conf.d/my_server.cf;
    default_type text/plain;
    return 403 "IP address blocked for security reasons.";
}

this is the iptables filter table cfg:

Chain INPUT (policy DROP)
num  target     prot opt source               destination
1    ufw-before-logging-input  all  --  anywhere             anywhere
2    ufw-before-input  all  --  anywhere             anywhere
3    ufw-after-input  all  --  anywhere             anywhere
4    ufw-after-logging-input  all  --  anywhere             anywhere
5    ufw-reject-input  all  --  anywhere             anywhere
6    ufw-track-input  all  --  anywhere             anywhere
7    ACCEPT     all  --  anywhere             anywhere
8    ACCEPT     all  --  anywhere             anywhere
9    ACCEPT     all  --  anywhere             anywhere

Chain FORWARD (policy ACCEPT) num target prot opt source destination 1 DOCKER-USER all -- anywhere anywhere 2 DOCKER-ISOLATION-STAGE-1 all -- anywhere anywhere 3 ACCEPT all -- anywhere anywhere ctstate RELATED,ESTABLISHED 4 DOCKER all -- anywhere anywhere 5 ACCEPT all -- anywhere anywhere 6 ACCEPT all -- anywhere anywhere 7 ACCEPT all -- anywhere anywhere ctstate RELATED,ESTABLISHED 8 DOCKER all -- anywhere anywhere 9 ACCEPT all -- anywhere anywhere 10 ACCEPT all -- anywhere anywhere 11 ufw-before-logging-forward all -- anywhere anywhere 12 ufw-before-forward all -- anywhere anywhere 13 ufw-after-forward all -- anywhere anywhere 14 ufw-after-logging-forward all -- anywhere anywhere 15 ufw-reject-forward all -- anywhere anywhere 16 ufw-track-forward all -- anywhere anywhere

Chain OUTPUT (policy ACCEPT) num target prot opt source destination 1 ufw-before-logging-output all -- anywhere anywhere 2 ufw-before-output all -- anywhere anywhere 3 ufw-after-output all -- anywhere anywhere 4 ufw-after-logging-output all -- anywhere anywhere 5 ufw-reject-output all -- anywhere anywhere 6 ufw-track-output all -- anywhere anywhere

Chain DOCKER (2 references) num target prot opt source destination 4 ACCEPT tcp -- anywhere 172.29.0.13 tcp dpt:8912 5 ACCEPT tcp -- anywhere 172.29.0.13 tcp dpt:8911 6 ACCEPT tcp -- anywhere 172.29.0.13 tcp dpt:https 7 ACCEPT tcp -- anywhere 172.29.0.13 tcp dpt:http

Chain DOCKER-ISOLATION-STAGE-1 (1 references) num target prot opt source destination 1 DOCKER-ISOLATION-STAGE-2 all -- anywhere anywhere 2 DOCKER-ISOLATION-STAGE-2 all -- anywhere anywhere 3 RETURN all -- anywhere anywhere

Chain DOCKER-ISOLATION-STAGE-2 (2 references) num target prot opt source destination 1 DROP all -- anywhere anywhere 2 DROP all -- anywhere anywhere 3 RETURN all -- anywhere anywhere

Chain DOCKER-USER (1 references) num target prot opt source destination 1 RETURN all -- anywhere anywhere

Chain ufw-after-forward (1 references) num target prot opt source destination

Chain ufw-after-input (1 references) num target prot opt source destination 1 ufw-skip-to-policy-input udp -- anywhere anywhere udp dpt:netbios-ns 2 ufw-skip-to-policy-input udp -- anywhere anywhere udp dpt:netbios-dgm 3 ufw-skip-to-policy-input tcp -- anywhere anywhere tcp dpt:netbios-ssn 4 ufw-skip-to-policy-input tcp -- anywhere anywhere tcp dpt:microsoft-ds 5 ufw-skip-to-policy-input udp -- anywhere anywhere udp dpt:bootps 6 ufw-skip-to-policy-input udp -- anywhere anywhere udp dpt:bootpc 7 ufw-skip-to-policy-input all -- anywhere anywhere ADDRTYPE match dst-type BROADCAST

Chain ufw-after-logging-forward (1 references) num target prot opt source destination

Chain ufw-after-logging-input (1 references) num target prot opt source destination 1 LOG all -- anywhere anywhere limit: avg 3/min burst 10 LOG level warning prefix "[UFW BLOCK] "

Chain ufw-after-logging-output (1 references) num target prot opt source destination

Chain ufw-after-output (1 references) num target prot opt source destination

Chain ufw-before-forward (1 references) num target prot opt source destination 1 ACCEPT all -- anywhere anywhere ctstate RELATED,ESTABLISHED 2 ACCEPT icmp -- anywhere anywhere icmp destination-unreachable 3 ACCEPT icmp -- anywhere anywhere icmp source-quench 4 ACCEPT icmp -- anywhere anywhere icmp time-exceeded 5 ACCEPT icmp -- anywhere anywhere icmp parameter-problem 6 ACCEPT icmp -- anywhere anywhere icmp echo-request 7 ufw-user-forward all -- anywhere anywhere

Chain ufw-before-input (1 references) num target prot opt source destination 1 ACCEPT all -- anywhere anywhere 2 ACCEPT all -- anywhere anywhere ctstate RELATED,ESTABLISHED 3 ufw-logging-deny all -- anywhere anywhere ctstate INVALID 4 DROP all -- anywhere anywhere ctstate INVALID 5 ACCEPT icmp -- anywhere anywhere icmp destination-unreachable 6 ACCEPT icmp -- anywhere anywhere icmp source-quench 7 ACCEPT icmp -- anywhere anywhere icmp time-exceeded 8 ACCEPT icmp -- anywhere anywhere icmp parameter-problem 9 ACCEPT icmp -- anywhere anywhere icmp echo-request 10 ACCEPT udp -- anywhere anywhere udp spt:bootps dpt:bootpc 11 ufw-not-local all -- anywhere anywhere 12 ACCEPT udp -- anywhere mdns.mcast.net udp dpt:mdns 13 ACCEPT udp -- anywhere 239.255.255.250 udp dpt:1900 14 ufw-user-input all -- anywhere anywhere

Chain ufw-before-logging-forward (1 references) num target prot opt source destination

Chain ufw-before-logging-input (1 references) num target prot opt source destination

Chain ufw-before-logging-output (1 references) num target prot opt source destination

Chain ufw-before-output (1 references) num target prot opt source destination 1 ACCEPT all -- anywhere anywhere 2 ACCEPT all -- anywhere anywhere ctstate RELATED,ESTABLISHED 3 ufw-user-output all -- anywhere anywhere

Chain ufw-logging-allow (0 references) num target prot opt source destination 1 LOG all -- anywhere anywhere limit: avg 3/min burst 10 LOG level warning prefix "[UFW ALLOW] "

Chain ufw-logging-deny (2 references) num target prot opt source destination 1 RETURN all -- anywhere anywhere ctstate INVALID limit: avg 3/min burst 10 2 LOG all -- anywhere anywhere limit: avg 3/min burst 10 LOG level warning prefix "[UFW BLOCK] "

Chain ufw-not-local (1 references) num target prot opt source destination 1 RETURN all -- anywhere anywhere ADDRTYPE match dst-type LOCAL 2 RETURN all -- anywhere anywhere ADDRTYPE match dst-type MULTICAST 3 RETURN all -- anywhere anywhere ADDRTYPE match dst-type BROADCAST 4 ufw-logging-deny all -- anywhere anywhere limit: avg 3/min burst 10 5 DROP all -- anywhere anywhere

Chain ufw-reject-forward (1 references) num target prot opt source destination

Chain ufw-reject-input (1 references) num target prot opt source destination

Chain ufw-reject-output (1 references) num target prot opt source destination

Chain ufw-skip-to-policy-forward (0 references) num target prot opt source destination 1 ACCEPT all -- anywhere anywhere

Chain ufw-skip-to-policy-input (7 references) num target prot opt source destination 1 DROP all -- anywhere anywhere

Chain ufw-skip-to-policy-output (0 references) num target prot opt source destination 1 ACCEPT all -- anywhere anywhere

Chain ufw-track-forward (1 references) num target prot opt source destination 1 ACCEPT tcp -- anywhere anywhere ctstate NEW 2 ACCEPT udp -- anywhere anywhere ctstate NEW

Chain ufw-track-input (1 references) num target prot opt source destination

Chain ufw-track-output (1 references) num target prot opt source destination 1 ACCEPT tcp -- anywhere anywhere ctstate NEW 2 ACCEPT udp -- anywhere anywhere ctstate NEW

Chain ufw-user-forward (1 references) num target prot opt source destination

Chain ufw-user-input (1 references) num target prot opt source destination 1 ACCEPT tcp -- anywhere anywhere tcp dpt:9933 2 ACCEPT udp -- anywhere anywhere udp dpt:9933 3 tcp -- anywhere anywhere tcp dpt:ssh ctstate NEW recent: SET name: DEFAULT side: source mask: 255.255.255.255 4 ufw-user-limit tcp -- anywhere anywhere tcp dpt:ssh ctstate NEW recent: UPDATE seconds: 30 hit_count: 6 name: DEFAULT side: source mask: 255.255.255.255 5 ufw-user-limit-accept tcp -- anywhere anywhere tcp dpt:ssh 6 ACCEPT tcp -- anywhere anywhere tcp dpt:2375 7 ACCEPT tcp -- anywhere anywhere tcp dpt:2376 8 ACCEPT tcp -- anywhere anywhere tcp dpt:ssh /* 'dapp_OpenSSH' */ 9 ACCEPT tcp -- anywhere anywhere tcp dpt:https

Chain ufw-user-limit (1 references) num target prot opt source destination 1 LOG all -- anywhere anywhere limit: avg 3/min burst 5 LOG level warning prefix "[UFW LIMIT BLOCK] " 2 REJECT all -- anywhere anywhere reject-with icmp-port-unreachable

Chain ufw-user-limit-accept (1 references) num target prot opt source destination 1 ACCEPT all -- anywhere anywhere

Chain ufw-user-logging-forward (0 references) num target prot opt source destination

Chain ufw-user-logging-input (0 references) num target prot opt source destination

Chain ufw-user-logging-output (0 references) num target prot opt source destination

Chain ufw-user-output (1 references) num target prot opt source destination

but:

(1) the iptables re-direct to port 8911/8912 does still NOT work

Thanks!

pete19
  • 33

1 Answers1

0

DNAT target should be used when the target host is in the network behind the router where iptables is running.

However, in your case it is in the same machine. Therefore I suggest you use the REDIRECT target as follows:

iptables -t nat -I PREROUTING -p tcp --dport 80 -m set --match-set badips src,dst -j REDIRECT --to-ports 8911

Also, you need a separate https port in your nginx. HTTP and TLS are two different protocols and you cannot run them on the same port.

Therefore you need to set up another virtual host with proper certificates on port 8912 for example, and then have the rule:

iptables -t nat -I PREROUTING -p tcp --dport 443 -m set --match-set badips src,dst -j REDIRECT --to-ports 8912

Your firewall rules seem to block incoming connections to ports 8911 and 8912.

You need to allow those ports using UFW.

Tero Kilkanen
  • 38,887