Tutorial

How To Configure Port Knocking Using Only Iptables on an Ubuntu VPS

Published on January 18, 2014
How To Configure Port Knocking Using Only Iptables on an Ubuntu VPS

Status: Deprecated

This article covers a version of Ubuntu that is no longer supported. If you are currently operate a server running Ubuntu 12.04, we highly recommend upgrading or migrating to a supported version of Ubuntu:

Reason: Ubuntu 12.04 reached end of life (EOL) on April 28, 2017 and no longer receives security patches or updates. This guide is no longer maintained.

See Instead: This guide might still be useful as a reference, but may not work on other Ubuntu releases. If available, we strongly recommend using a guide written for the version of Ubuntu you are using. You can use the search functionality at the top of the page to find a more recent version.

Introduction


Servers that are connected to the internet are subjected to all manners of attacks and probes by malicious users, scripts, and automated bots. It is sometimes a balancing act to secure your server from attacks without affecting legitimate access to your services and resources.

Certain types of services are meant to be visible and consumable to the public internet. An example of this is a web server. Other types of services are typically used by only the system administrator or a select number of individuals and are not meant to be a public resource.

A concept known as port knocking is a way of shielding processes that fit into the latter description. Port knocking works by covering the ports associated with a process behind a firewall until a specific, predetermined sequence of network activity occurs. At this point, the port knocking service reconfigures the firewall to allow access to the protected application.

In a previous article, we discussed how to enable port knocking through a specially designed port knocking service. In this article, we will discuss an alternative method of configuring port knocking.

This method does not rely on an external application to alter the firewall rules. Instead, the iptables firewall can take advantage of a state-tracking module called “recent” to do all of this within the firewall rules themselves.

We will be configuring this on an Ubuntu 12.04 droplet, but any kind of Linux server should operate in a similar manner.

Note: This tutorial covers IPv4 security. In Linux, IPv6 security is maintained separately from IPv4. For example, “iptables” only maintains firewall rules for IPv4 addresses but it has an IPv6 counterpart called “ip6tables”, which can be used to maintain firewall rules for IPv6 network addresses.

If your VPS is configured for IPv6, please remember to secure both your IPv4 and IPv6 network interfaces with the appropriate tools. For more information about IPv6 tools, refer to this guide: How To Configure Tools to Use IPv6 on a Linux VPS

Iptables Port Knocking Overview


Before we get into the actual configuration, we will describe how the recent module works and how it allows us to create a port knocking system.

Iptables can load modules with the -m flag. The recent module can track connection states. We can use this to funnel our port connection attempts through a sequence of chains based on if the connecting user had hit each previously needed port.

For our example, we will be blocking our SSH daemon from the internet, represented by our eth0 interface. We will dynamically reconfigure our firewall to allow SSH connections from our computer temporarily once a sequence of ports have been “knocked on” in sequence.

The sequence we will use for this tutorial is:

  • 1111
  • 2222
  • 3333

These values should not be used in a real configuration.

In order for a user to authenticate correctly and cause iptables to expose the SSH daemon to their IP address, they must hit each of these ports in sequence without sending non-sequence traffic in between. If we implement this, we will have successfully created a port knocking system.

Iptables Configuration Strategy


In order to implement the above design, we will use a few different chains.

First, we will accept all of the traffic that we do not wish to be subjected to the port knocking. This includes any public resources, like a web server, as well as established connections and connection coming from the local interface.

Directly afterwards, we will redirect all incoming traffic not handled by the previous rules into a new chain, where we will put the bulk of our rules. This is always a good idea when implementing a large, self-contained rule set because it provides a really easy way to enable or disable the functionality, simply by funneling traffic into or bypassing the new chain.

We will call this chain KNOCKING.

We will also be using some other chains. The recent module allows us to categorize different types of traffic and then check if a connection matches a category that was previously set.

We will use this strategy to flag IP addresses that sent a packet to the first knock target. We will have a rule that checks for that first flag and checks if the second packet is being sent to the second knock target. If it is, it sets another flag indicating that it’s gotten two answers right so far. If the second packet doesn’t match the second target, it is dropped and the flag is reset.

The additional chains work using this same strategy of checking the appropriate flag and passing it off if it continues on the right path. In the end, once the final packet has been requested in sequence, the SSH daemon is exposed briefly, only for the IP address that knocked successfully. It is then hidden again automatically.

So basically, if we think of our port knocking rules as different gates to get into our service, we will have three gates for our three knocks. This means that there are four different positions that a requesting IP address could be in:

  • initial state: This is the state that all IP addresses are in until they successfully send a packet to the first knock target. This will not be set by the recent module and is simply a way of referring to a client that has no flags set.

  • auth1 state: Addresses that have successfully knocked on the first knock target are flagged as “auth1”. From here, the next packet from this host determines if the address will be placed back in the initial state or moved to the “auth2” state.

  • auth2 state: Addresses flagged with this state have successfully knocked on the first and second targets in sequence. The next packet from this host determines whether the host will be set back to the initial state or set to the “auth3” state.

  • auth3 state: Addresses that have been flagged as “auth3” have successfully knocked on all three ports, in order, in the allotted amount of time. If an address flagged as “auth3” attempts to access the service now (in window that is provided), the connection will be accepted. If any other traffic is received, the address will be thrown back to the initial state.

In addition to these states, which will be flags set by the recent module, each gate, or decision point, will be implemented as a chain. The chains can be summarized as follows:

  • GATE1: Determines whether an address in the initial state should be flagged as “auth1”.

  • GATE2: Determines whether an address in the “auth1” state should be processed to “auth2” or reset to the “initial” state.

  • GATE3: Determines whether an address in the “auth2” state should be flagged as “auth3” to allow an SSH connection, or reset to the “initial” state.

  • PASSED: This chain is used to briefly open the port for the SSH daemon for clients that have knocked successfully. Any traffic from clients in this chain that is not destined for the SSH daemon is dropped, causing the state to be reset to “initial” again.

As you can see, we have a lot of decision points here. Every kind of traffic in the KNOCKING chain and its sub-chains should be dropped (except the traffic for the SSH daemon from successful knock clients), regardless of whether it matched the correct port. The internal flagging is the only thing that will keep track of successful attempts and this logic is not exposed to the client.

This is necessary for the port knocking implementation to be valid. A person attempting to connect should receive no feedback as to what stage of the process they are in or even if such a mechanism is in place.

Configure Regular Firewall Framework


We will begin by laying down a basic framework for our connections. The strategy we talked about above will be applied to the INPUT chain, which handles all incoming connections.

We will begin by flushing the existing firewall rules so that we can start with a clean slate. Before flushing rules, it is always a good idea to reassert that the default policies in an empty table are “ACCEPT” in order to maintain your current connection:

sudo iptables -P INPUT ACCEPT
sudo iptables -P FORWARD ACCEPT
sudo iptables -P OUTPUT ACCEPT
sudo iptables -F

This will ensure that we are starting with a completely open firewall, which we can begin to restrict.

Before we begin restricting though, we want to add the additional chains that we will be using:

sudo iptables -N KNOCKING
sudo iptables -N GATE1
sudo iptables -N GATE2
sudo iptables -N GATE3
sudo iptables -N PASSED

At this point, we should have eight different chains! We will use all of these except the OUTPUT and FORWARD chains, which do not concern us in this context.

First, we should add the traffic that we don’t want to handle with port knocking to the INPUT chain. We can start by accepting all current connections, which will allow our current SSH connection to remain unaffected:

sudo iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT

We should also accept all connections from the local machine, since services often need to communicate with one another:

sudo iptables -A INPUT -i lo -j ACCEPT

If you have services that should remain externally and publicly accessible, like a web server, add a rule to allow this type of connection using this format:

<pre> sudo iptables -A INPUT -p <span class=“highlight”>protocol</span> --dport <span class=“highlight”>port</span> -j ACCEPT </pre>

In our example, we’ll assume that we have a web server running on the default port 80, and we will allow this traffic:

sudo iptables -A INPUT -p tcp --dport 80 -j ACCEPT

Now that we have our basic allowed connections configured, we can transfer all traffic that was not handled in the above rules to our KNOCKING chain to do the actual knocking logic:

sudo iptables -A INPUT -j KNOCKING

Configure First Gate


When we are finished configuring our “gates”, we will hook them all into the KNOCKING chain to direct the traffic through our logic tests. Before we do that though, we should develop our individual logic units.

We will begin by defining our initial knocking test. When a client connects, we simply need to see if they are sending a packet to our first target. If they are, we flag the client as passing the first test and drop the connection. If they are not, we simply drop the connection.

We will first set the flag on the correct port attempt:

sudo iptables -A GATE1 -p tcp --dport 1111 -m recent --name AUTH1 --set -j DROP

This line does a number of things. To begin with, it is appending a rule to the GATE1 chain. This rule will match when the protocol being used is “tcp” and when the port it is trying to access is “1111”, our first knock target.

If this is true, the recent module (called with -m recent), flags the requesting IP address with the name AUTH1 (with the --name AUTH1 --set rule). This is the flag we will use to see if the second knock matches. The packet is dropped after the flag is set, so that the client does not know if anything happened.

Next, we will just drop all other packets, because any information that is sent to this chain is only looking for the matching first packet at this point:

sudo iptables -A GATE1 -j DROP

We now have our first port covered. The rules either match and flag the address if the correct port is requested, or no action is taken and the packet is simply dropped.

Configure Second Gate


The second gate is configured in much the same way as the first. It is slightly more complex though.

First of all, the logic to send the traffic into this chain will exist in the main KNOCKING chain. This chain does not have to check whether the flag set by the first gate matches, because that will have already happened.

However, it does have to remove any flags before it begins to process. If it did not remove the flag, then an address could be flagged with different names, which could result in the address successfully knocking by just scanning your ports three times. This is definitely not what we want.

We will use the recent module again in a preliminary rule, this time just to clear the name:

sudo iptables -A GATE2 -m recent --name AUTH1 --remove

This is a processing rule, so we don’t make any decisions or jumps. We simply strip the current flag (the one that would have allowed the traffic into this chain) and send it to the next rule.

Now that the address is past our first rule in this chain, it is in a clean state with no flags. At this point, we check whether this connection attempt is a correct match for the next port target:

sudo iptables -A GATE2 -p tcp --dport 2222 -m recent --name AUTH2 --set -j DROP

This is handled in much the same way as the first gate. We simply set the AUTH2 flag, indicating that the requesting address passed the second test, if the correct port was knocked on. The flag is set and the packet is again dropped, giving the client no indication of their progress.

The next rule may seem a bit strange at first.

sudo iptables -A GATE2 -j GATE1

You may assume that just dropping the packet at this point is the logical thing to do. However, this would lead to an awkward situation in one specific case.

If we had a “drop all” rule here, and the packet sent at this point matches the first knock target, it would not be registered as a beginning of the knock sequence.

For instance, if our client accidentally hits the first port twice, it won’t register as correct because the firewall would see the sequence like this:

  • First port hit. Firewall marks first test passed. Will check for second port next.
  • First port hit. Does not match second port rule. Sequence is reset. Will check for first port next.
  • Second port hit. Does not match first port rule. Sequence is reset. Will check for first port next.
  • Third port hit. Does not match first port rule. Sequence is reset. Will check for first port next.

As you can see, the first, second, third sequence has been completed, but the firewall has gotten confused about what rules it should be checking. In this scenario, the client would have to send a dummy request before beginning the real sequence just to reset the chain if a mistake was made.

To avoid this situation, we will not just drop the packet at this point and be done with it. Instead, we will leverage the GATE1 chain that we already configured. As we demonstrated above, we can simply send the traffic that did not match the second knock target to the first gate again:

sudo iptables -A GATE2 -j GATE1

This will cause the client’s position in the sequence to be reset in one of two ways. If the request is for the first port, The sequence is restarted to as a successful first knock. If it is not the first port, it is dropped as usual. This avoids the above situation.

Configure Third Gate


We can use what we learned from the second gate to implement the third gate in the same exact way.

First, we want to clear all flags that have been given to our address so that this run through the chains will set the correct state without stale flags from before:

sudo iptables -A GATE3 -m recent --name AUTH2 --remove

Next, we will test whether the connection attempt matches the third knock target. If it does, we set the AUTH3 flag, which indicates that the client successfully completed all required knocks. As per usual, we drop the packet afterwards.

sudo iptables -A GATE3 -p tcp --dport 3333 -m recent --name AUTH3 --set -j DROP

From here, we once again send the traffic that did not match the third knock target back to the first gate to see if it should count as a successful first knock to restart the sequence:

sudo iptables -A GATE3 -j GATE1

At this point, clients who’ve completed the correct knocking sequence should be flagged with AUTH3, which will let us easily open the service for them in the PASSED chain.

Configure Passed Chain


This chain is used to open the SSH daemon for 30 seconds to the client that successfully knocked the correct sequence.

We start this in the same way as the others. The SSH daemon will only be available if the next packet sent from the client is requesting it. This forces people who are randomly attempting to get past the knocking to attempt an SSH connection in between each attempt.

First, we do the usual flag resets:

sudo iptables -A PASSED -m recent --name AUTH3 --remove

Next, we accept SSH connections from the users who have made it into this chain:

sudo iptables -A PASSED -p tcp --dport 22 -j ACCEPT

Once again, we send all traffic that does not match back through our first chain to see if it matches the first port knock target:

sudo iptables -A PASSED -j GATE1

Now, we have all of our sub-chains configured but our general KNOCKING chain, which will pass traffic into these individual chains.

Configure the Knocking Chain


Now that we have all of our sub-chains configured, we can hook them into our general KNOCKING chain and create the logic for how to pass off the traffic.

First, we will pass traffic from clients that have successfully completed all the knocks directly into the PASSED chain.

We have some options though. We can implement a time limit to only give the successful client a 30 second window to connect to the daemon. After that, the rule will no longer match successfully.

sudo iptables -A KNOCKING -m recent --rcheck --seconds 30 --name AUTH3 -j PASSED

Next, we will test for each of the other flags from the most restrictive to the least. We can add a 10 second time limit before the previous knock expires also. This will require our clients to complete the next stage of the knock within 10 seconds, and then connect to the SSH daemon in another 30 seconds.

sudo iptables -A KNOCKING -m recent --rcheck --seconds 10 --name AUTH2 -j GATE3
sudo iptables -A KNOCKING -m recent --rcheck --seconds 10 --name AUTH1 -j GATE2

Now, we want to send all traffic that has not matched so far back to GATE1, as usual. This will catch any attempt for the first knock:

sudo iptables -A KNOCKING -j GATE1

This will basically set a default drop policy for our KNOCKING chain by implicitly adding the GATE1 logic to the end of the KNOCKING chain.

At this point, we have all of our knocking chains in place. We have added some additional structure to compartmentalize our logic. The whole KNOCKING chain structure and its sub-chains are hooked up to our regular input chain.

At this point, our port knocking mechanism is configured. It’s time to test it out.

Test Our Port Knocking


There are a number of utilities that can be used to generate the TCP packets that we are requiring for our port knocking configuration. We will use the nmap command, because it is present on most systems by default.

Nmap uses TCP packets by default. We need to tell it to drop to host discovery portion of its default behavior though so that those packets do not interfere with our knocking. In addition, we want our connection to time out after only a second, so that we can continue on to the next knock.

With these requirements, a single knock may look like this. We will use our first knock target as an example:

<pre> nmap -Pn --host_timeout 201 --max-retries 0 -p 1111 <span class=“highlight”>your_server</span> </pre>

Therefore, our entire knock sequence could be represented by these commands:

<pre> nmap -Pn --host_timeout 201 --max-retries 0 -p 1111 <span class=“highlight”>your_server</span> nmap -Pn --host_timeout 201 --max-retries 0 -p 2222 <span class=“highlight”>your_server</span> nmap -Pn --host_timeout 201 --max-retries 0 -p 3333 <span class=“highlight”>your_server</span> </pre>

We would then have 30 seconds to connect with our SSH client.

We can take advantage of some really basic bash scripting to automate this a bit. We can use a “for” loop to iterate through our port sequence and then pass it off to the SSH client:

<pre> for x in 1111 2222 3333; do nmap -Pn --host_timeout 201 --max-retries 0 -p $x <span class=“highlight”>your_server</span> && sleep 1; done && ssh <span class=“highlight”>user</span>@<span class=“highlight”>your_server</span> </pre>

This will knock on the first port, wait for a second, knock on the next, and so on until the sequence is complete. It will then attempt to connect the the server’s SSH daemon.

We can put this in a file to clean it up a bit. We’ll call it knock_client. Create it in a text editor:

<pre> nano knock_client </pre> <pre> #!/bin/bah

ports=“1111 2222 3333” host=“<span class=“highlight”>your_server</span>”

for x in $ports do nmap -Pn --host_timeout 201 --max-retries 0 -p $x $host sleep 1 done ssh <span class=“highlight”>user</span>@${host} </pre>

Save and close the file.

Make the file executable with this command:

chmod 755 knock_client

Now, we can connect to our server by typing:

./knock_client

Now that we have verified that our rules work as intended, we can make our firewall rules persistent by downloading a separate package on our server:

sudo apt-get install iptables-persistent

We can have our rules applied at boot by enabling this service:

sudo service iptables-persistent start

Conclusion


By now, you should have a fully operational port knocking system in place using nothing but the functionality included in the iptables firewall. This has a few advantages. First of all, iptables is very commonly used and is audited for security problems often. This means that as long as your rules do what you think they do, it should be pretty secure.

Another advantage this configuration offers over a separate knocking daemon is that there is no opportunity of the knocking service to fail and leave you locked out of your server. If the iptables service fails, you at least will be able to enter your server to repair.

<div class=“author”>By Justin Ellingwood</div>

Thanks for learning with the DigitalOcean Community. Check out our offerings for compute, storage, networking, and managed databases.

Learn more about our products


Tutorial Series: How To Implement Port Knocking to Obscure your SSH Daemon

Port knocking is a security concept that involves dynamically altering firewall rules to expose access to an otherwise protected service. This is done by sending a pre-configured special packet, or a pattern of packets that the port knocking software is listening for. In this series, we will discuss a variety of ways to configure port knocking to add an extra layer of security around your SSH daemon.

About the authors

Still looking for an answer?

Ask a questionSearch for more help

Was this helpful?
 
10 Comments


This textbox defaults to using Markdown to format your answer.

You can type !ref in this text area to quickly search our full set of tutorials, documentation & marketplace offerings and insert the link!

I have register an username on digital ocean just to comment this tutorial . This is indeed one of the best tutorials i have read and implemented so far in some servers i have . However , in my iptables there are also rules to limit denial of service attacks by using “packet limit” and “burst limit” on opened ports on the server . I want to thank you personally for this excellent article , thank you very much . My best regards also to all of you here commenting and giving ideas in how to make it better .

Great post! Especially that only iptables is required. But i found that the knocking packets were somehow sent like this in our enviroment:

knock port 1
knock port 1
...knock port 1 several times ...
knock port 2
knock port 2
...knock port 2 several times ...
knock port 3
knock port 3
...knock port 3 several times ...

It’s very hard to see a exact sequence of 1 2 3 and the SSH port is not opened.

So i modify the rules like this and it seems works quite well.

-A GATE1 -p tcp --dport $PORT1 -m recent --name AUTH1 --set -j DROP
-A GATE1 -j DROP

-A GATE2 -p tcp --dport $PORT1 -j DROP               # add to deal with the repeated port 1 knocking
-A GATE2 -m recent --name AUTH1 --remove
-A GATE2 -p tcp --dport $PORT2 -m recent --name AUTH2 --set -j DROP
-A GATE2 -j GATE1

-A GATE3 -p tcp --dport $PORT2 -j DROP              # add to deal with the repeated port 2 knocking
-A GATE3 -m recent --name AUTH2 --remove
-A GATE3 -p tcp --dport $PORT3 -m recent --name AUTH3 --set -j DROP
-A GATE3 -j GATE1

-A PASSED -p tcp --dport $PORT3 -j DROP            # add to deal with the repeated port 3 knocking
#-A PASSED -m recent --name AUTH3 --remove   # comment out so that i can establish several ssh connections
-A PASSED -p tcp --dport 22 -j ACCEPT
-A PASSED -j GATE1

# add --reap to purge entries automaticly  
-A KNOCKING -m recent --reap --rcheck --seconds 30 --name AUTH3 -j PASSED
-A KNOCKING -m recent --reap --rcheck --seconds 10 --name AUTH2 -j GATE3
-A KNOCKING -m recent --reap --rcheck --seconds 10 --name AUTH1 -j GATE2
-A KNOCKING -j GATE1

Is it ok?

Digital Ocean has really great tutorials.

I have a question. Is there any advantage to configuring IP tables over using knockd? It seems easier. Does it use a lot of resources?

Any idea why mine isn’t working? Please do help. In the meantime, I’ve just set port 22 to be accessible… if I don’t, my connection to 22 times out, whether I try the knocking sequence first or not at all.

EDIT: If GATE1 sets name AUTH3, it works. AUTH1 and AUTH2 gives the above issue, even if the 10 second timeouts are extended to 30. P.S.: I literally do have the 1111 2222 3333 ports set. Intending to change them once this works correctly.

Chain INPUT (policy ACCEPT)
target     prot opt source               destination         
ACCEPT     all  --  0.0.0.0/0            0.0.0.0/0           ctstate RELATED,ESTABLISHED 
ACCEPT     all  --  0.0.0.0/0            0.0.0.0/0 
ACCEPT     tcp  --  0.0.0.0/0            0.0.0.0/0           tcp dpt:25565 
KNOCKING   all  --  0.0.0.0/0            0.0.0.0/0           

Chain FORWARD (policy ACCEPT)
target     prot opt source               destination         

Chain OUTPUT (policy ACCEPT)
target     prot opt source               destination         

Chain GATE1 (4 references)
target     prot opt source               destination         
DROP       tcp  --  0.0.0.0/0            0.0.0.0/0           tcp dpt:1111 recent: SET name: AUTH1 side: source 
DROP       all  --  0.0.0.0/0            0.0.0.0/0           

Chain GATE2 (1 references)
target     prot opt source               destination         
           all  --  0.0.0.0/0            0.0.0.0/0           recent: REMOVE name: AUTH1 side: source 
DROP       tcp  --  0.0.0.0/0            0.0.0.0/0           tcp dpt:2222 recent: SET name: AUTH2 side: source 
GATE1      all  --  0.0.0.0/0            0.0.0.0/0           

Chain GATE3 (1 references)
target     prot opt source               destination         
           all  --  0.0.0.0/0            0.0.0.0/0           recent: REMOVE name: AUTH2 side: source 
DROP       tcp  --  0.0.0.0/0            0.0.0.0/0           tcp dpt:3333 recent: SET name: AUTH3 side: source 
GATE1      all  --  0.0.0.0/0            0.0.0.0/0           

Chain KNOCKING (1 references)
target     prot opt source               destination         
PASSED     all  --  0.0.0.0/0            0.0.0.0/0           recent: CHECK seconds: 30 name: AUTH3 side: source 
GATE3      all  --  0.0.0.0/0            0.0.0.0/0           recent: CHECK seconds: 10 name: AUTH2 side: source 
GATE2      all  --  0.0.0.0/0            0.0.0.0/0           recent: CHECK seconds: 10 name: AUTH1 side: source 
GATE1      all  --  0.0.0.0/0            0.0.0.0/0           

Chain PASSED (1 references)
target     prot opt source               destination         
           all  --  0.0.0.0/0            0.0.0.0/0           recent: REMOVE name: AUTH3 side: source 
ACCEPT     tcp  --  0.0.0.0/0            0.0.0.0/0           tcp dpt:22 
GATE1      all  --  0.0.0.0/0            0.0.0.0/0           

I am running the following (though uncensored) to attempt knocking in:

for x in 1111 2222 3333; do nmap -Pn --host_timeout 201 --max-retries 0 -p $x XXX.XXX.XXX.XXX && sleep 1; done && ssh username@XXX.XXX.XXX.XXX

Bravo! Excelent post. Thanks you for your dedication.

Also, Justin, there is a small typo in your knocking script: namp -> nmap.

Very nice tutorial. Can you tell me it is possible add log every knock to /var/log ?

Justin, great series of tutorials. Any chance you might do the same for Moxie Marlinspike’s port knocking tool?

Justin Ellingwood
DigitalOcean Employee
DigitalOcean Employee badge
June 23, 2014

zwzserver:

I appreciate you taking the time to leave such good feedback!

While using a DROP on special ports could allow an attacker to distinguish between the important ports and legitimately unused ports, I don’t think the rules in this article put you in that situation.

Here is a gist of the finished ruleset:

https://gist.github.com/imchairmanm/af3816fdc288796f019f

If I’m following the flow of the packets correctly, they would enter through the INPUT chain and all packets that do not match the first three rules (established, local interface, or web server) will jump to the KNOCKING chain.

From here, packets are sent to other chains depending on whether they have flags indicating that they have successfully passed a certain stage of the knocking pattern. The last jump is to GATE1, which is where we start our knock filtering.

The GATE1 chain drops packets that target the correct port, but it <em>also</em> drops all other packets. Since the only difference is an internal one (flagging the source IP as having passed the first port test), the same information will be returned to the user in either case.

The other gates drop packets that match the next rules and then send them back to GATE1 otherwise, resulting in the same situation.

The only user-added chain that results with anything other than a drop is the PASSED chain.

Let me know if I’m mistaken in this assessment. I’m pretty sure that the packets are dropped regardless (with a rule, not a policy). If you think any of this is incorrect though, feel free to get back to me.

Thanks again!

I believe you have a vulnerability in this tutorial that would allow an attacker to discover your secret port numbers. The problem is the -j DROP part. The default behavior would be to forward the SYN to the machine behind the firewall and it would issue a tcp-reset packet that would be sent back to the attacker signifying that the port is truly unused. So the dropping can be used to detect that this port is “special.” This means that the option is to either leave it unchanged with a -j RETURN option and leverage the natural mechanisms (the port will get refused exactly like an unbound port), or imitate the final server’s behavior using -j REJECT --reject-with tcp-reset . The RETURN keyword is needed to prevent the rules in the chain from being processed further as opposed to not giving any -j target. EDIT: Now that I think of it, the -j RETURN thing is actually superior to the tcp-reset because not only will it mimic any custom port scanning refusal you may implement separately, but it will also let the port be used as an ephemeral port by any application! If the port was just blocked, the attacker could analyze the traffic of any ephemeral-port using service and notice which ports never got used, or worse (in case of FTP), which ports got RESET (!) despite being negotiated over the FTP command channel.

Try DigitalOcean for free

Click below to sign up and get $200 of credit to try our products over 60 days!

Sign up

Join the Tech Talk
Success! Thank you! Please check your email for further details.

Please complete your information!

Become a contributor for community

Get paid to write technical tutorials and select a tech-focused charity to receive a matching donation.

DigitalOcean Documentation

Full documentation for every DigitalOcean product.

Resources for startups and SMBs

The Wave has everything you need to know about building a business, from raising funding to marketing your product.

Get our newsletter

Stay up to date by signing up for DigitalOcean’s Infrastructure as a Newsletter.

New accounts only. By submitting your email you agree to our Privacy Policy

The developer cloud

Scale up as you grow — whether you're running one virtual machine or ten thousand.

Get started for free

Sign up and get $200 in credit for your first 60 days with DigitalOcean.*

*This promotional offer applies to new accounts only.