ipset
ipsets are a powerful way of making a dynamic firewall on a normal Linux machine. A feature of the netfilter code, an ipset is an in-memory list of IPs, that can be referenced in any fireawall rules.
Knocknoc can add and remove IPs from an ipset, thereby allowing an arbitrary application of dynamic allow-listing to any linux box.
IPsets need to exist before you can update them, so the overall process is:
- Create an ipset by name and type using the ipset utility
- Use a systemd or similar script to ensure the ipset is created on startup, or at least on firewall start
- Integrate the IPset into your firewall software scripting, example for UFW is here, and Shorewall is here.
- Install the Knocknoc agent on your linux machine and enrol it into the Knocknoc server
- Configure Knocknoc to run an ipset script as a backend. The agent includes a basic script, and further customisation is easy.
- Some applications work better if you also add the user to a second blocklist ipset once the access is revoked. An example of how to do this is at the bottom of the page.
Sudoers first
To create a custom sudoers file in theĀ /etc/sudoers.d/
directory for the user knocknoc-agent
, allowing them to run the command /usr/sbin/ipset
with any arguments, follow these steps:
-
Create a New File in
/etc/sudoers.d/
:- Choose a meaningful name for the file, such as
knocknoc-agent
. - The command would be
sudo visudo -f /etc/sudoers.d/knocknoc-agent
. - This opens a new file in the
sudoers.d
directory for editing with proper syntax checking.
- Choose a meaningful name for the file, such as
-
Add the Necessary Rule:
- In the editor that opens, add the following line:
knocknoc-agent ALL=(ALL) NOPASSWD: /usr/sbin/ipset *
- This line follows the same syntax and meaning as described previously.
- In the editor that opens, add the following line:
-
Save and Exit:
- Save the file and exit the editor.
visudo
will automatically check the syntax.
- Save the file and exit the editor.
-
Set Correct Permissions:
- Ensure that the file has the correct permissions. It should be readable by root only and should not be writable by any other user.
- You can set the appropriate permissions using:
sudo chmod 0440 /etc/sudoers.d/knocknoc-agent
.
-
Verify the Configuration:
- To check if your configuration works, switch to the
knocknoc-agent
user (if possible) and try executing theipset
command withsudo
without a password.
- To check if your configuration works, switch to the
Important Notes:
- Always use
visudo
to edit sudoers files to prevent syntax errors. - Ensure that the files in
/etc/sudoers.d/
have strict permissions (like 0440) to maintain security. - Be cautious with
NOPASSWD:
as it allows executing the specified command without a password, which can be a security risk if not properly managed.
Example script with ipset add and blocking
Paste this script into /opt/knocknoc-agent/scripts/ipset_block.sh, make it executable and update your Knocknoc server backend config to have this path. Note this script assumes you have given the knocknoc-agent sudo rights to run the ipset command.
#!/bin/bash
# Wrapper script to allow safe parsing of an ipset command line from sudo
# sudo is run in this scrip itself, so you need to enable the ipset command in sudoers
# like so:
# make a file in /etc/sudoers.d/knocknoc-agent with this contents:
# knocknoc-agent ALL=(ALL:ALL) NOPASSWD: /usr/sbin/ipset *
set -e -o pipefail
# Define the general_blocked ipset name
general_blocked="knocknoc_blocked"
# Validate IP address
function validate_ip() {
local ip=$1
if [[ $ip =~ ^[0-9]{1,3}(\.[0-9]{1,3}){3}$ ]]; then
return 0
else
echo "Invalid IP address"
exit 1
fi
}
# Validate setname
function validate_setname() {
local setname=$1
if [[ $setname =~ ^[A-Za-z0-9_]+$ ]]; then
return 0
else
echo "Invalid setname"
exit 1
fi
}
# Validate operation
function validate_op() {
local op=$1
if [[ $op =~ ^(add|del|flush)$ ]]; then
return 0
else
echo "Invalid operation"
exit 1
fi
}
# Validate and assign operation
validate_op "$1"
op="$1"
# Validate and assign setname
validate_setname "$2"
setname="$2"
# Execute ipset command
if [[ "$op" = "flush" ]]; then
sudo /usr/sbin/ipset "$op" "$setname"
else
# Validate and assign IP address
validate_ip "$3"
ip="$3"
# If the operation is to delete an IP, also add it to the general_blocked set
if [[ "$op" = "del" ]]; then
# Add IP to general_blocked set
sudo /usr/sbin/ipset add "$general_blocked" "$ip" ||true
fi
if [[ "$op" = "add" ]]; then
# Add IP to general_blocked set
sudo /usr/sbin/ipset del "$general_blocked" "$ip" ||true
fi
# Execute the original ipset command
sudo /usr/sbin/ipset "$op" "$setname" "$ip"
fi