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!