2
0
nft-edit-ruleset/nft-edit-ruleset
2021-11-24 02:13:45 +01:00

245 lines
5.3 KiB
Bash
Executable File

#!/bin/bash
# Summary: Interactively edit the current nftables ruleset.
# Author: Tilman Kranz
# Authoremail: tilt@linuxfoo.de
# License: MIT License (https://opensource.org/licenses/MIT)
##
# Configuration
if test -w "/etc/nftables.conf" ; then
default_config_file="/etc/nftables.conf"
elif test -w "/etc/sysconfig/nftables.conf" ; then
default_config_file="/etc/sysconfig/nftables.conf"
else
default_config_file=""
fi
##
# Functions
cleanup() {
! "$timeout" && test -n "$tmp_old" && rm -f "$tmp_old"
test -n "$tmp" && rm -f "$tmp"
}
at_int() {
cleanup
exit 1
}
at_exit() {
cleanup
}
set_timeout() {
local ruleset=$1
local seconds=$2
systemd-run \
--description="Reverting nftables ruleset changes by $0 (PID=$$)" \
--on-active="$seconds" \
--timer-property=AccuracySec=100ms \
--quiet \
bash -c "nft -f '$ruleset' ; rm -f '$ruleset'"
}
store_config() {
local ruleset=$1
local conf=$2
if ! printf "#!/usr/sbin/nft -f\n# Generated at %s by $0\n\n" "$(date -R)" > "$conf" ; then
return 1
elif ! cat "$ruleset" >> "$conf" ; then
return 1
fi
return 0
}
##
# Arguments
config=false
fail=false
timeout=false
yes=false
while true ; do
case "$1" in
-h|--help)
# shellcheck disable=SC2016
backticks='```'
cat << EOF
# SYNOPSIS
$backticks
nft-edit-ruleset [--config [CONFIG_FILE]] [--fail] [--timeout SECONDS] [--yes]
$backticks
# DESCRIPTION
Interactively edit the current nftables ruleset using the editor specified by
environment variable EDITOR (defaulting to vim). Optionally, revert changes
after a timeout given in seconds.
# OPTIONS
- \`-c\`, \`--config [CONFIG_FILE]\`:
On success, save ruleset to \`CONFIG_FILE\` (default: $default_config_file).
- \`-f\`, \`--fail\`:
Exit unsuccessfully if changes fail to apply.
- \`-h\`, \`--help\`:
Display this message and exit.
- \`-t NUM\`, \`--timeout NUM\`:
Revert changes after NUM seconds.
- \`-y\`, \`--yes\`:
No confirmation before applying changes.
# EXIT CODES
- 0: Success: No changes to apply or changes applied successfully.
- 1: Error: Usage error or failed or aborted changes.
# AUTHOR AND LICENSE
Copyleft 2021 Tilman Kranz <tilt@linuxfoo.de>
This software is distributed on the terms and conditions of the
MIT License [https://opensource.org/licenses/MIT](https://opensource.org/licenses/MIT)
EOF
exit 0
;;
-f|--fail)
fail=true
;;
-y|--yes)
yes=true
;;
-t|--timeout)
if test "$#" -gt 0 ; then
shift 1
timeout=true
timeout_secs=$1
else
echo "ERROR: Missing argument for option \`--timeout\`, aborted." >&2
exit 1
fi
;;
-c|--config)
config=true
if test "$#" -gt 0 && { test -w "$2" || test -w "$(dirname "$2")" ; } ; then
shift 1
config_file=$1
elif test -z "$default_config_file" ; then
echo \
"ERROR: Option \`--config\` was used without an argument," \
"but no default location of a file \"nftables.conf\" could be found" \
"(use \`--config CONFIG_FILE\` to specify a location); aborted." >&2
exit 1
else
config_file=$default_config_file
fi
;;
'')
:
;;
*)
echo "ERROR: Unknown or unexpected argument \"$1\"; aborted." >&2
exit 1
;;
esac
if test "$#" -gt 0 ; then
shift 1
else
break
fi
done
##
# Main Program
if ! test -t ; then
echo "ERROR: Not connected to a terminal; aborted." >&2
exit 1
fi
tmp_old=$(mktemp)
tmp=$(mktemp)
trap at_int INT
trap at_exit EXIT
printf 'flush ruleset\n\n' > "$tmp"
nft list ruleset >> "$tmp"
cat "$tmp" > "$tmp_old"
while true ; do
editor=${EDITOR:-vim}
"$editor" "$tmp"
if diff "$tmp_old" "$tmp" ; then
echo "No changes."
if "$config" ; then
if store_config "$tmp" "$config_file" ; then
echo "Stored unchanged ruleset to config_file=\"$config_file\"."
else
echo "ERROR: Storing unchanged ruleset to config_file=\"$config_file\" failed." >&2
exit 1
fi
fi
exit 0
else
if ! "$yes" ; then
read -p "Apply these changes? (Y|n) " -r answer
case "$answer" in
''|y|Y)
:
;;
*)
echo "Aborting (user request)."
exit 1
;;
esac
fi
if ! nft -c -f "$tmp" ; then
if "$fail" ; then
exit 1
else
read -p "Errors in changes detected (Ctrl-c to abort, ENTER to continue editing)" -r answer
fi
elif ! nft -f "$tmp" ; then
if "$fail" ; then
exit 1
else
read -p "Errors when applying changes (Ctrl-c to abort, ENTER to continue editing)" -r answer
fi
else
if "$timeout" ; then
set_timeout "$tmp_old" "$timeout_secs"
fi
echo "Changes applied successfully."
if "$config" ; then
if store_config "$tmp" "$config_file" ; then
echo "Stored changed ruleset to config_file=\"$config_file\"."
else
echo "ERROR: Storing changed ruleset to config_file=\"$config_file\" failed." >&2
exit 1
fi
fi
break
fi
fi
done
##
# Exit Codes
# - 0: Success: No changes to apply or changes applied successfully.
# - 1: Error: Usage error, failed or aborted changes or failureto store configfile.