Port Knocking
Below is a detailed, step-by-step guide to setting up a port-knocking mechanism that automatically closes the SSH port after use, ensuring that the port is closed even in the event of unexpected disconnections. The setup involves both the remote server and the client machine.
Assumptions
- You have a remote Ubuntu server where you want to secure SSH access using port knocking.
- You have a client machine from which you’ll connect to the remote server using SSH.
- SSH will be configured to use port
61000, and you will use a specific knock sequence to open this port.
Goal
- Implement port knocking to open the SSH port (
61000). - Automatically schedule the closing of the SSH port after 10 minutes, even if the connection is lost.
- Ensure the reverse knock is handled by the remote server, so it will still close the port even if your client machine loses power or network.
Part 1: Configuration on the Remote Server
Step 1: Install Required Packages on the Remote Server
Log into the remote server (via SSH if possible or through some other existing access method):
ssh <username>@<remote_server_ip>Update the package list:
sudo apt updateInstall
knockdandatd:These two packages are essential:
knockdhandles port knocking.atdhandles scheduling tasks (to close the port after a set period).
Run the following command:
sudo apt install knockd atEnable and start
atd(the scheduling service):sudo systemctl enable atd sudo systemctl start atdEnable and start
knockd(the port-knocking service):sudo systemctl enable knockd sudo systemctl start knockd
Step 2: Configure knockd for Port Knocking
Open the
knockdconfiguration file:sudo nano /etc/knockd.confAdd the following configuration to define the knock sequence to open and close SSH port
61000:[options] UseSyslog interface = <correct_network_interface> # Replace with correct interface name [openSSH] sequence = 7654,8765,9876 seq_timeout = 5 command = /usr/sbin/ufw allow from %IP% to any port 61000 tcpflags = syn [closeSSH] sequence = 9876,8765,7654 seq_timeout = 5 command = /usr/sbin/ufw delete allow from %IP% to any port 61000 tcpflags = syn- sequence: Defines the knock sequence (ports
7654,8765,9876) for opening SSH. - seq_timeout: Time (in seconds) within which the sequence must be completed.
- command: The
ufwcommand to allow/block SSH access on port61000.
- sequence: Defines the knock sequence (ports
Save and close the file (
Ctrl + O,Enter, thenCtrl + X).Restart
knockdto apply the changes:sudo systemctl restart knockd
Step 3: Configure UFW to Block SSH by Default
Set up UFW to block port
61000by default:sudo ufw deny 61000/tcpEnable UFW (if not already enabled):
sudo ufw enableCheck the UFW status:
sudo ufw statusThis should show that port
61000is blocked, ensuring that SSH access is only allowed after the correct knock sequence.
Part 2: Configuration on the Client Machine
Step 1: Install Required Packages on the Client Machine
Update the package list on your local machine:
sudo apt updateInstall
knockdon the client machine (to perform port knocking):sudo apt install knockd
Step 2: Create a Port Knocking and SSH Script on the Client Machine
Create a script to handle port knocking, SSH connection, and scheduling the reverse knock on the remote server.
nano ~/knock_and_ssh.shAdd the following content to the script:
#!/bin/bash # Knock the ports to open SSH on the remote server knock $1 7654 8765 9876 -d 500 # Wait for 1 second to ensure the knock sequence is processed sleep 1 # Establish SSH connection and schedule the reverse knock on the remote server /usr/bin/ssh -p 61000 $2@$1 "echo 'knock $1 9876 8765 7654 -d 500' | at now + 10 minutes" # Connect via SSH on port 61000 /usr/bin/ssh -p 61000 $2@$1- knock: Opens the SSH port by knocking the specified sequence (
7654,8765,9876). - at: Schedules the reverse knock to close the port after 10 minutes, but this command is run on the remote server.
- ssh: Establishes the SSH connection using port
61000.
- knock: Opens the SSH port by knocking the specified sequence (
Save and close the script (
Ctrl + O,Enter, thenCtrl + X).Make the script executable:
chmod +x ~/knock_and_ssh.sh
Step 3: Configure ~/.ssh/config on the Client Machine
Open your SSH configuration file:
nano ~/.ssh/configAdd the following configuration block for the remote server:
Host abc-server HostName <remote_server_ip> User <username> ProxyCommand ~/knock_and_ssh.sh %h %r- Host: Alias for the server (e.g.,
abc-server). - HostName: The IP address of the remote server (
<remote_server_ip>). - User: The SSH username to connect to the server (
<username>). - ProxyCommand: Uses the script to handle port-knocking and SSH connection.
- Host: Alias for the server (e.g.,
Save and close the file (
Ctrl + O,Enter, thenCtrl + X).
Part 3: Testing the Setup
Step 1: Test the SSH Connection
Initiate the SSH connection by using the alias defined in the
~/.ssh/configfile:ssh abc-server- The
knock_and_ssh.shscript will knock the sequence to open the SSH port. - It will then establish the SSH connection using port
61000. - Finally, it will schedule the reverse knock to close the port after 10 minutes on the remote server.
- The
Step 2: Verify the Reverse Knock on the Remote Server
Check if the reverse knock is scheduled on the remote server by listing the scheduled
atjobs:atqYou should see an entry showing the reverse knock scheduled to run after 10 minutes.
Verify UFW rules by running:
sudo ufw statusThis will show that port
61000is open while the SSH session is active, and it will be closed once theatjob runs.
Part 4: Monitoring and Logs
Monitor
knockdto verify knock attempts on the remote server:tail -f /var/log/syslogThis log will show any knock attempts, both for opening and closing the SSH port.
Check
atjobs to ensure the reverse knock is correctly scheduled.
Conclusion:
- You have now set up a secure SSH access mechanism using port knocking.
- The SSH port (
61000) will only be opened after the correct knock sequence is received. - The reverse knock is scheduled on the remote server to close the port after a set time (10 minutes), ensuring security even if your client machine crashes or loses network connectivity.
- The solution ensures that the port is always closed securely after a session, regardless of unexpected events.
Reference: https://chatgpt.com/share/82e0ba92-28bd-4807-ae8f-b309d3479ded
Block SSH port on Remote (periodic run) for Additional Safety
The script will now remove all UFW rules that allow traffic on port 61000, without checking for active SSH connections. Existing SSH sessions will remain active, but new connections will be blocked once the rule is removed.
sudo nano /usr/local/bin/ufw-remove-ssh-rule.sh#!/bin/bash
# Define the SSH port to monitor
SSH_PORT="61000"
# Remove all UFW rules that allow traffic on the specified port
echo "Removing UFW rule for port $SSH_PORT..."
# Use a loop to delete all rules matching the port, as UFW might have multiple rules for the same port
RULE_NUMBERS=$(sudo ufw status numbered | grep "$SSH_PORT" | awk -F'[][]' '{print $2}')
if [ -z "$RULE_NUMBERS" ]; then
echo "No UFW rule found for port $SSH_PORT."
else
for RULE_NUM in $RULE_NUMBERS; do
echo "Deleting UFW rule #$RULE_NUM..."
sudo ufw --force delete "$RULE_NUM"
logger "UFW rule #$RULE_NUM removed for port $SSH_PORT"
done
fiMake the script executable
sudo chmod +x /usr/local/bin/ufw-remove-ssh-rule.shTest the Script
Run the script manually to test it:
sudo /usr/local/bin/ufw-remove-ssh-rule.shAfter running the script, check the UFW status to ensure the rule has been deleted:
sudo ufw status numberedAutomate with systemd (Optional)
Once you’ve confirmed the script works as expected, you can automate it with a systemd timer or cron job
Step-by-Step Guide for Automating the Script with Cron
Step 1: Open the Cron Configuration
Edit the
rootcrontab (since UFW changes require root privileges):sudo crontab -eThis opens the cron configuration for the
rootuser, ensuring the script runs with the necessary permissions.
Step 2: Add the Cron Job
Add a line to the crontab that runs your script at the desired interval. Here are a few examples of how to schedule it:
Every 10 minutes:
*/10 * * * * /usr/local/bin/ufw-remove-ssh-rule.sh >> /var/log/ufw-remove-ssh.log 2>&1Once a day at midnight:
0 0 * * * /usr/local/bin/ufw-remove-ssh-rule.sh >> /var/log/ufw-remove-ssh.log 2>&1Every 5 minutes:
*/5 * * * * /usr/local/bin/ufw-remove-ssh-rule.sh >> /var/log/ufw-remove-ssh.log 2>&1At 3:00 AM every day:
0 3 * * * /usr/local/bin/ufw-remove-ssh-rule.sh >> /var/log/ufw-remove-ssh.log 2>&1
Explanation:
/10 * * * *: This cron job runs every 10 minutes. The format isminute hour day month weekday, so/10means “every 10 minutes.”>> /var/log/ufw-remove-ssh.log 2>&1: This redirects the output and errors of the script to a log file (/var/log/ufw-remove-ssh.log) so that you can review what the script has done.
Step 3: Save and Exit the Crontab
- Save the crontab by pressing
Ctrl + O, then pressEnter, and exit withCtrl + X.
Step 4: Verify the Cron Job
Verify that the cron job is scheduled:
After saving the crontab, you can list the cron jobs to confirm that it has been added:
sudo crontab -lThis should display your new cron job.
Step 5: Check the Logs (Optional)
Check the log file to see the output of your script:
sudo tail -f /var/log/ufw-remove-ssh.logThis will show any output from the script, including UFW rule deletion actions.
Conclusion
Now your script is automated with cron, and it will run at the interval you set (e.g., every 10 minutes). The cron job will trigger the script to remove the UFW rule for port 61000 without needing manual intervention.