Skip to content

Instantly share code, notes, and snippets.

@amard33p
Created August 31, 2025 16:35
Show Gist options
  • Select an option

  • Save amard33p/2d218b1d9ded5cfa478633f93c362ee2 to your computer and use it in GitHub Desktop.

Select an option

Save amard33p/2d218b1d9ded5cfa478633f93c362ee2 to your computer and use it in GitHub Desktop.
#!/bin/bash
function usage() {
echo "\
Usage: $0 <firewall name> <command>
Example:
$0 block-ssh-firewall ssh -i ~/.ssh/your_host holu@your_host\
" 1>&2
exit 1
}
function handle_api_error() {
if [ "$(echo "$1" | jq .error)" != "null" ]
then
echo $1 1>&2
exit 2
fi
}
if [ $# -lt 2 ]; then
usage
fi
# Exit on error.
set -e
# Determine my public IPv4 and IPv6 addresses.
my_ipv4=$(curl -4 https://ip.hetzner.com)
my_ipv6=$(curl -6 https://ip.hetzner.com)
# Get the ID and the current rules of the passed firewall.
response=$(curl \
--silent \
-H "Authorization: Bearer $HETZNER_API_TOKEN" \
"https://api.hetzner.cloud/v1/firewalls?name=$1")
handle_api_error "$response"
firewall_id=$(echo $response | jq ".firewalls[0].id")
original_rules=$(echo $response | jq ".firewalls[0].rules")
# Add to the current rules a new rule that allows incoming SSH traffic from my IP addresses.
allow_ssh_rule=$(cat << END
{
"direction": "in",
"port": "22",
"protocol": "tcp",
"source_ips": ["$my_ipv4/32", "$my_ipv6/128"]
}
END
)
new_rules=$(echo $original_rules | jq ". = . + [$allow_ssh_rule]")
# Make the firewall adopt the new ruleset.
response=$(curl \
--silent \
-X POST \
-H "Authorization: Bearer $HETZNER_API_TOKEN" \
-H "Content-Type: application/json" \
-d "{\"rules\":$new_rules}" \
"https://api.hetzner.cloud/v1/firewalls/$firewall_id/actions/set_rules")
handle_api_error "$response"
# Reset ruleset when the script exits (even upon receiving a SIGTERM or SIGINT signal).
function cleanup() {
response=$(curl \
--silent \
-X POST \
-H "Authorization: Bearer $HETZNER_API_TOKEN" \
-H "Content-Type: application/json" \
-d "{\"rules\":$original_rules}" \
"https://api.hetzner.cloud/v1/firewalls/$firewall_id/actions/set_rules")
handle_api_error "$response"
}
trap "cleanup" EXIT
# Remove first argument, which is the firewall name.
shift
# Execute the other arguments as shell command.
$@
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment