Alive: How to test a port on a remote host

Introduction

When I create a VM on my infrastructure, I sometimes open a port but can’t use the service behind it. There can be multiple causes:

  • I need to ping the server to ensure the network is properly configured.
  • Connect to it.
  • Check that the service is running.
  • Install and use netstat to ensure the expected port is listening.
  • Verify that the firewall isn’t blocking anything.
  • Check if fail2ban has blocked me due to too many attempts.
  • Sometimes check if the port is routed on my pfSense…

In short, it’s a lot of steps, and I might forget some of them. That’s why tools like telnet can simplify things. But I found that netcat (a.k.a. nc) can make it even easier by providing a ping-like repetition logic. Let me share my tool with you!

Introducing Netcat

Netcat is a command-line tool used to read and write data over network connections using TCP or UDP protocols. It’s often called the “Swiss Army knife” of networking tools for its flexibility and many use cases. We’re dealing with a simple but powerful CLI that can be used to transfer files between machines or even create simple TCP/UDP servers and clients.

To check if a TCP port is open on a remote host, Netcat can be used to attempt a connection to that port. If the connection is established, it means that the port is accessible from your IP.

Using Netcat to test a TCP port

To test if a TCP port is open on a remote host, you can use the following command:

nc -z <host> <port>
  • -z: Tells Netcat to scan without sending any data.
  • <host>: The IP address or domain name of the host to test.
  • <port>: The port number to test.

Examples:

Let’s test the HTTPS port (443) of the blog:

$ nc -z blog.talma.cloud 443
Connection to blog.talma.cloud (212.83.143.1) 443 port [tcp/https] succeeded!

But if we test a port that isn’t open:

$ nc -z blog.talma.cloud 21

🧌 Nothing happens.
🤓 Actually, Netcat is trying to connect to the FTP port (21), but it’s not responding, and the `nc` command is waiting. So, this is technically an acceptable response. But since I mentioned making a tool that responds like a ping, we need to exit this state. For that, there’s the `timeout` parameter, which defines how long `nc` should wait before concluding that the port isn’t reachable.

$ nc -w 1 -z blog.talma.cloud 21

$

🧌 Okay! But still nothing happens. We only get the command prompt back!
🤓 Not exactly. nc exited after 1 second, and when a program ends, it “dies” with an exit code. This code equals 0 if everything is fine and some other value otherwise. In our case, let’s revisit those examples and check the exit code:

$ nc -z blog.talma.cloud 443
Connection to blog.talma.cloud (212.83.143.1) 443 port [tcp/https] succeeded!

$ echo $?
0

$ nc -w 1 -z blog.talma.cloud 21

$ echo $?
1

If the port isn’t accessible, we get an exit code (in the $? variable) that is 1. That’s all we need!

alive : a Bash Script to test a port

Here’s a commented bash script that will loop through the command we just discussed. For each attempt, it will display whether the remote host is reachable on the specified port.

#!/bin/bash

# Check if the necessary arguments are provided. If no port is defined, I choose to test the SSH port (22).
if [ -z "$1" ]; then
        # If the host isn’t defined, display a message and usage instructions, then exit with exit code "1".
        echo "No host defined"
        echo "Usage: alive <host>"
        echo "<host>: IPv4 | IPv6 | Hostname"
        echo "<port>: port 22 by default (Optional)"
        exit 1
else
        # If the host is defined, continue.
        REMOTEHOST=$1
        TIMEOUT=3

        if [ -z "$2" ]; then
                REMOTEPORT=22
        else
                REMOTEPORT=$2
        fi
fi

# Set up color coding for terminal returns. This is optional but makes the tool much more visual.
Red='\033[0;31m'
Green='\033[0;32m'
Cyan='\033[0;36m'
Disable_Color='\033[0m'

# Infinite loop on the test.
while [[ true ]]; do
        # Add a timestamp.
        LASTDATE=$(date +"%H:%M:%S")
        # Test the exit code of the netcat command. "&> /dev/null" prevents netcat's returns from being displayed; we'll make our own.
        if $(nc -w $TIMEOUT -z $REMOTEHOST $REMOTEPORT &> /dev/null ); then
                # If the host is reachable on the port, indicate it in green.
                printf "${Cyan}[${LASTDATE}] ${Green} ${REMOTEHOST} is alive on port ${REMOTEPORT}"
        else
                # If the port isn’t reachable on the remote host, indicate it in red.
                printf "${Cyan}[${LASTDATE}] ${Red} ${REMOTEHOST} connection failed on port ${REMOTEPORT}"
        fi
        # Don’t forget to disable the color. Then wait 1 second and repeat.
        printf "${Disable_Color} \n"
        sleep 1
done

exit 0

Script usage

To use this script, save it in a file, for example, alive.sh, and make it executable:

chmod +x alive.sh

Just in case, install netcat 🤓, without it, it won’t work (but you probably already have it by default):

sudo apt install netcat-openbsd

Then, you can run the script by providing the IP address or domain name of the host and the port number to test:

./alive.sh <host> <port>

I move the script to a directory where I won’t need to navigate to (and I don’t need to reference when running the command):

sudo mv alive.sh /usr/local/bin/alive

So when you want to set up a new web server, for example, you start by doing:

alive mysite.com 80

Then install your server, set up routing, the firewall, DNS pointing, install your web server (nginx, apache, tomcat…) and at some point, magically, you’ll see this:

[13:51:15]  mysite.com connection failed on port 80 
[13:51:16]  mysite.com connection failed on port 80 
[13:51:17]  mysite.com connection failed on port 80 
[13:51:18]  mysite.com is alive on port 80 
[13:51:19]  mysite.com is alive on port 80 
[13:51:20]  mysite.com is alive on port 80

Other use cases

I also use alive when I need to shut down (or restart) a critical server (like a SPOF) while testing the service port. Sure, I always have a Zabbix probe nearby, but here we’re precise to the second! It’s always nice to know that we had to take down a service but that it only lasted 5 seconds, proof in hand!

When rebooting a bare metal server (or dedicated, as the old-timers say 😛), we know that EFI (the old BIOS!) performs way more tests than on a PC, and that can take 5 minutes. My servers generally don’t respond to ping, so I use alive on the SSH port. As soon as it’s green, I know I can connect. Depending on the models, I also have my little personal stats on reboot times so I don’t worry each time.

Finally, alive is also a script to place on your VM templates! When I see that a server is “alive” from server A, but it doesn’t respond to my server B, I conclude that the server itself isn’t the issue, and it’s probably a network or firewall story.

Other use cases are probably possible (and I’m sure I’m forgetting some). Personally, I use alive EVERY day for several years. I’ve shared it with my colleagues, and I see it regularly on my friends’ screens.

Note that it’s also possible to test UDP ports by adding the -u option in the parameters as follows:

nc -w $TIMEOUT -z $REMOTEHOST -u $REMOTEPORT