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**
1. **Log into the remote server** (via SSH if possible or through some other existing access method):
```bash
ssh <username>@<remote_server_ip>
```
2. **Update the package list**:
```bash
sudo apt update
```
3. **Install `knockd` and `atd`**:
These two packages are essential:
- `knockd` handles port knocking.
- `atd` handles scheduling tasks (to close the port after a set period).
Run the following command:
```bash
sudo apt install knockd at
```
4. **Enable and start `atd`** (the scheduling service):
```bash
sudo systemctl enable atd
sudo systemctl start atd
```
5. **Enable and start `knockd`** (the port-knocking service):
```bash
sudo systemctl enable knockd
sudo systemctl start knockd
```
### **Step 2: Configure `knockd` for Port Knocking**
1. **Open the `knockd` configuration file**:
```bash
sudo nano /etc/knockd.conf
```
2. **Add 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 `ufw` command to allow/block SSH access on port `61000`.
3. **Save and close the file** (`Ctrl + O`, `Enter`, then `Ctrl + X`).
4. **Restart `knockd`** to apply the changes:
```bash
sudo systemctl restart knockd
```
### **Step 3: Configure UFW to Block SSH by Default**
1. **Set up UFW** to block port `61000` by default:
```bash
sudo ufw deny 61000/tcp
```
2. **Enable UFW** (if not already enabled):
```bash
sudo ufw enable
```
3. **Check the UFW status**:
```bash
sudo ufw status
```
This should show that port `61000` is 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**
1. **Update the package list** on your local machine:
```bash
sudo apt update
```
2. **Install `knockd` on the client machine** (to perform port knocking):
```bash
sudo apt install knockd
```
### **Step 2: Create a Port Knocking and SSH Script on the Client Machine**
1. **Create a script** to handle port knocking, SSH connection, and scheduling the reverse knock on the remote server.
```bash
nano ~/knock_and_ssh.sh
```
2. **Add the following content** to the script:
```bash
#!/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`.
3. **Save and close the script** (`Ctrl + O`, `Enter`, then `Ctrl + X`).
4. **Make the script executable**:
```bash
chmod +x ~/knock_and_ssh.sh
```
### **Step 3: Configure `~/.ssh/config` on the Client Machine**
1. **Open your SSH configuration file**:
```bash
nano ~/.ssh/config
```
2. **Add 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.
3. **Save and close the file** (`Ctrl + O`, `Enter`, then `Ctrl + X`).
---
## **Part 3: Testing the Setup**
### **Step 1: Test the SSH Connection**
1. **Initiate the SSH connection** by using the alias defined in the `~/.ssh/config` file:
```bash
ssh abc-server
```
- The `knock_and_ssh.sh` script 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**.
### **Step 2: Verify the Reverse Knock on the Remote Server**
1. **Check if the reverse knock is scheduled** on the remote server by listing the scheduled `at` jobs:
```bash
atq
```
You should see an entry showing the reverse knock scheduled to run after 10 minutes.
2. **Verify UFW rules** by running:
```bash
sudo ufw status
```
This will show that port `61000` is open while the SSH session is active, and it will be closed once the `at` job runs.
---
## **Part 4: Monitoring and Logs**
1. **Monitor `knockd`** to verify knock attempts on the remote server:
```bash
tail -f /var/log/syslog
```
This log will show any knock attempts, both for opening and closing the SSH port.
2. **Check `at` jobs** 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](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.
```bash
sudo nano /usr/local/bin/ufw-remove-ssh-rule.sh
```
```bash
#!/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
fi
```
### Make the script executable
```bash
sudo chmod +x /usr/local/bin/ufw-remove-ssh-rule.sh
```
### Test the Script
Run the script manually to test it:
```bash
sudo /usr/local/bin/ufw-remove-ssh-rule.sh
```
After running the script, check the UFW status to ensure the rule has been deleted:
```bash
sudo ufw status numbered
```
### Automate 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
1. **Edit the `root` crontab** (since UFW changes require root privileges):
```bash
sudo crontab -e
```
This opens the cron configuration for the `root` user, ensuring the script runs with the necessary permissions.
### Step 2: Add the Cron Job
1. **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**:
```bash
*/10 * * * * /usr/local/bin/ufw-remove-ssh-rule.sh >> /var/log/ufw-remove-ssh.log 2>&1
```
- **Once a day at midnight**:
```bash
0 0 * * * /usr/local/bin/ufw-remove-ssh-rule.sh >> /var/log/ufw-remove-ssh.log 2>&1
```
- **Every 5 minutes**:
```bash
*/5 * * * * /usr/local/bin/ufw-remove-ssh-rule.sh >> /var/log/ufw-remove-ssh.log 2>&1
```
- **At 3:00 AM every day**:
```bash
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 is `minute hour day month weekday`, so `/10` means "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
1. **Save the crontab** by pressing `Ctrl + O`, then press `Enter`, and exit with `Ctrl + X`.
### Step 4: Verify the Cron Job
1. **Verify that the cron job is scheduled**:
After saving the crontab, you can list the cron jobs to confirm that it has been added:
```bash
sudo crontab -l
```
This should display your new cron job.
### Step 5: Check the Logs (Optional)
1. **Check the log file** to see the output of your script:
```bash
sudo tail -f /var/log/ufw-remove-ssh.log
```
This 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.