Let's say that, for example, I want to allow connections only to subdomain.mydomain.com; I have it partially working, but it sometimes gets in a freaky loop with the client key exchange once the Client Hello is allowed. Ah, to make it even more annoying, it's a self-signed certificate, and the page requires authentication, and HTTPS is listening on a non-standard port... So the TCP/SSL Handshake experience will differ greatly for many users.

Is -m recent the right route? Is there a more graceful method to allow the complete TCP stream once the string is seen?

Here's what I have so far:

#iptables -N SSL
#iptables -A INPUT -i eth0 -p tcp -j SSL
#iptables -A SSL -m recent --set -p tcp --syn --dport 400
#iptables -A SSL -m recent --update -p tcp --tcp-flags PSH,SYN,ACK SYN,ACK --sport 400
#iptables -A SSL -m recent --update -p tcp --tcp-flags PSH,SYN,ACK ACK --dport 400
#iptables -A SSL -m recent --remove -p tcp --tcp-flags PSH,ACK PSH,ACK --dport 400 -m string --algo kmp --string "subdomain.mydomain.com" -j ACCEPT

Yes, I have tried to get around this with nginx tweaks, but I can't get nginx to return a 444 or abrupt disconnect before the client hello, if you can think of a way to achieve this instead, I'm all ears, err, eyes.

(As suggested by a user, bringing this inquiry over from https://stackoverflow.com/questions/4628157/allow-connections-to-only-a-specific-url-via-https-with-iptables-m-recent-pote)

2 Answers 2


This seems like an awfully complex way of solving the problem. What if you just put subdomain.mydomain.com on a unique IP address and then use that in your iptables rules?

I'm curious...what exactly are you trying to solve that can't be solved using the nginx allow/deny options? It's true that this requires the SSL negotiation to complete (because nginx doesn't know what the client is requesting until after the SSL connection is established), but that's okay in most cases.

  • Lars, you might be on to something. Unfortunately we've exhausted the public IPs that have been allocated to us (I know, I know, just buy some more), so we're forced to work with what we have, which, at this point, are virtual hosts. The reason that we only want to expose that we have HTTPS listening on that particular vhost is purely for security purposes, also to reduce overhead/probes, etc. Commented Jan 7, 2011 at 20:07
  • Understood. I'm just trying to understand why you can't rely on the normal nginx allow/deny machinery. If people are able to connect to this vhost, then they'll know that you listening on port 443 in any case...even using your iptables solution, the connection will still be accepted before SSL negotations start, so you're really not getting much with this iptables trickery. If this is the only virtual host using ssl, you ought to be able to configure other virtual hosts so that they're not available on port 443...or just return 403 forbidden.
    – larsks
    Commented Jan 7, 2011 at 20:23
  • I agree, security through partial obscurity isn't my preferred method either, but the policies we have in place are pretty strict and weird. The goal is not to totally hide that the host is listening for secure connections, but simply to drop all connections not destined for the vhost after the three-way handshake. A simple, "you're insane, this will not work" will suffice. Commented Jan 7, 2011 at 20:29
  • The thing is that you don't know what vhost the client is trying to contact after the three-way handshake. The SSL negotiation does not contain this information; only the HTTP requests to the server contain the target hostname...and you can't match these in iptables, because they're encrypted.
    – larsks
    Commented Jan 7, 2011 at 20:56
  • Incorrect, the FQDN is included in the SSL Client Hello in all modern browsers, in the clear. This is the packet I want to drop, just post the three-way handshake. Commented Jan 7, 2011 at 21:21

I think I've found a solution, and it was much simpler than my overly complex solution previously:

iptables -A INPUT -i eth0 -p tcp --tcp-flags FIN,PSH,ACK PSH,ACK --dport 400 -m connbytes --connbytes 3:3 --connbytes-dir original --connbytes-mode packets -m string --algo bm ! --string "subdomain.mydomain.com" -j REJECT --reject-with tcp-reset
iptables -A INPUT -i eth0 -p tcp --dport 400 -j ACCEPT

With this combination of the connbytes and string modules, I have achieved the desired results. If I manage to clean it up, I will add the revisions via edits.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .