|
|
74182f |
#!/bin/sh
|
|
|
a0fbec |
#
|
|
|
a0fbec |
# ipset Start and stop ipset firewall sets
|
|
|
a0fbec |
#
|
|
|
74182f |
# config: /etc/sysconfig/ipset-config
|
|
|
74182f |
|
|
|
74182f |
IPSET_BIN=/usr/sbin/ipset
|
|
|
74182f |
IPSET_CONFIG=/etc/sysconfig/ipset-config
|
|
|
74182f |
IPSET_DATA_COMPAT=/etc/sysconfig/ipset
|
|
|
74182f |
IPSET_DATA_COMPAT_BACKUP=${IPSET_DATA_COMPAT}.save
|
|
|
74182f |
IPSET_DATA_DIR=/etc/sysconfig/ipset.d
|
|
|
74182f |
IPSET_DATA_DIR_BACKUP=${IPSET_DATA_DIR}.save
|
|
|
74182f |
IPSET_DATA_SAVED_FLAG=${IPSET_DATA_DIR}/.saved
|
|
|
74182f |
IPSET_LOCK=/run/ipset.lock
|
|
|
74182f |
IPSET_RUN=/run/ipset.run
|
|
|
74182f |
CLEAN_FILES=""
|
|
|
74182f |
|
|
|
74182f |
trap "rm -rf \${CLEAN_FILES}" EXIT
|
|
|
74182f |
|
|
|
74182f |
info() {
|
|
|
74182f |
echo "ipset: ${*}" >&2
|
|
|
74182f |
}
|
|
|
a0fbec |
|
|
|
74182f |
warn() {
|
|
|
74182f |
echo "<4>ipset: ${*}" >&2
|
|
|
74182f |
}
|
|
|
a0fbec |
|
|
|
74182f |
err() {
|
|
|
74182f |
echo "<3>ipset: ${*}" >&2
|
|
|
74182f |
}
|
|
|
a0fbec |
|
|
|
74182f |
[ -x ${IPSET_BIN} ] || { err "Cannot execute ${IPSET_BIN}"; exit 1; }
|
|
|
a0fbec |
|
|
|
74182f |
# Source ipset configuration
|
|
|
74182f |
# shellcheck source=ipset-config
|
|
|
74182f |
[ -f ${IPSET_CONFIG} ] && . ${IPSET_CONFIG}
|
|
|
a0fbec |
|
|
|
74182f |
set -f
|
|
|
a0fbec |
|
|
|
74182f |
lock() {
|
|
|
74182f |
CLEAN_FILES="${CLEAN_FILES} ${IPSET_LOCK}"
|
|
|
74182f |
until mkdir ${IPSET_LOCK} 2>/dev/null; do :; done
|
|
|
a0fbec |
}
|
|
|
a0fbec |
|
|
|
74182f |
save() {
|
|
|
74182f |
fail=0
|
|
|
74182f |
|
|
|
74182f |
# Make backups of existing configuration first, if any
|
|
|
74182f |
[ -d ${IPSET_DATA_DIR} ] && mv -Tf ${IPSET_DATA_DIR} ${IPSET_DATA_DIR_BACKUP}
|
|
|
74182f |
[ -f ${IPSET_DATA_COMPAT} ] && mv -Tf ${IPSET_DATA_COMPAT} ${IPSET_DATA_COMPAT_BACKUP}
|
|
|
74182f |
|
|
|
74182f |
rm -f ${IPSET_DATA_SAVED_FLAG}
|
|
|
74182f |
|
|
|
74182f |
# Save each set in a separate file
|
|
|
74182f |
mkdir -p ${IPSET_DATA_DIR}
|
|
|
74182f |
chmod 0700 ${IPSET_DATA_DIR}
|
|
|
74182f |
IFS="
|
|
|
74182f |
"
|
|
|
74182f |
for set in $(${IPSET_BIN} list -n -t); do
|
|
|
74182f |
# Empty name allowed, use ".set" as suffix. 'ipset save' doesn't
|
|
|
74182f |
# quote set names with spaces: if we have a space in the name,
|
|
|
74182f |
# work around this by quoting it ourselves in the output.
|
|
|
74182f |
# shellcheck disable=SC2003 # No POSIX equivalent to expr index
|
|
|
74182f |
if expr index "${set}" " " >/dev/null; then
|
|
|
74182f |
:> "${IPSET_DATA_DIR}/${set}.set"
|
|
|
74182f |
for line in $(${IPSET_BIN} save "${set}"); do
|
|
|
74182f |
create=0
|
|
|
74182f |
echo "${line}" | grep -q "^create " && create=1
|
|
|
74182f |
if [ $create -eq 1 ]; then
|
|
|
74182f |
line=${line#create *}
|
|
|
74182f |
else
|
|
|
74182f |
line=${line#add *}
|
|
|
74182f |
fi
|
|
|
74182f |
line=${line#${set} *}
|
|
|
74182f |
set="$(echo "${set}" | sed 's/"/\\"/g')"
|
|
|
74182f |
if [ $create -eq 1 ]; then
|
|
|
74182f |
echo "create \"${set}\" ${line}" >> "${IPSET_DATA_DIR}/${set}.set"
|
|
|
74182f |
else
|
|
|
74182f |
echo "add \"${set}\" ${line}" >> "${IPSET_DATA_DIR}/${set}.set"
|
|
|
74182f |
fi
|
|
|
74182f |
done
|
|
|
74182f |
else
|
|
|
74182f |
${IPSET_BIN} save "${set}" > "${IPSET_DATA_DIR}/${set}.set" || fail=1
|
|
|
74182f |
fi
|
|
|
74182f |
[ -f "${IPSET_DATA_DIR}/${set}.set" ] && chmod 600 "${IPSET_DATA_DIR}/${set}.set"
|
|
|
74182f |
[ $fail -eq 1 ] && err "Cannot save set ${set}" && unset IFS && return 1
|
|
|
74182f |
done
|
|
|
74182f |
touch ${IPSET_DATA_SAVED_FLAG} || { unset IFS; return 1; }
|
|
|
74182f |
unset IFS
|
|
|
a0fbec |
|
|
|
74182f |
# Done: remove backups
|
|
|
74182f |
rm -rf ${IPSET_DATA_DIR_BACKUP}
|
|
|
74182f |
rm -rf ${IPSET_DATA_COMPAT_BACKUP}
|
|
|
a0fbec |
|
|
|
74182f |
return 0
|
|
|
a0fbec |
}
|
|
|
a0fbec |
|
|
|
74182f |
# Generate a grep regexp matching abbreviated command forms. E.g., for create:
|
|
|
74182f |
# \(c\|cr\|cre\|crea\|creat\|create\)
|
|
|
74182f |
cmd_short_expr() {
|
|
|
74182f |
out=
|
|
|
74182f |
cmd_len=1
|
|
|
74182f |
while [ "${cmd_len}" -le "${#1}" ]; do
|
|
|
74182f |
[ -z "${out}" ] && out='\(' || out="${out}"'\|'
|
|
|
74182f |
# shellcheck disable=SC2003 # No POSIX equivalent to expr substr
|
|
|
74182f |
out="${out}$(expr substr "${1}" 1 "${cmd_len}")"
|
|
|
74182f |
cmd_len=$((cmd_len + 1))
|
|
|
74182f |
done
|
|
|
74182f |
echo "${out}"'\)'
|
|
|
74182f |
}
|
|
|
a0fbec |
|
|
|
74182f |
ipset_restore() {
|
|
|
74182f |
file="${1}"
|
|
|
74182f |
|
|
|
74182f |
retfile="$(mktemp -q /tmp/ipset.XXXXXX)"
|
|
|
74182f |
CLEAN_FILES="${CLEAN_FILES} ${retfile}"
|
|
|
74182f |
|
|
|
74182f |
# If restore fails due to invalid lines, remove them and retry
|
|
|
74182f |
while ! restore_err="$( (${IPSET_BIN} -f "${file}" -! restore 2>&1; echo $? >"${retfile}") | head -n1; exit "$(cat "${retfile}")" )"; do
|
|
|
74182f |
warn "${restore_err}"
|
|
|
74182f |
case ${restore_err#*: } in
|
|
|
74182f |
"No command specified"*)
|
|
|
74182f |
line="$(grep -m1 -n "^${restore_err##* }" "${file}")"
|
|
|
74182f |
line="${line%:*}"
|
|
|
74182f |
;;
|
|
|
74182f |
"Missing second mandatory argument to command "*)
|
|
|
74182f |
cmd="${restore_err##* }"
|
|
|
74182f |
cmd_expr="$(cmd_short_expr "${cmd}")"
|
|
|
74182f |
line="$(grep -n '^'"${cmd_expr}" "${file}" | grep -m1 -v '^[0-9]\+\:'"${cmd_expr}"'[[:blank:]]\+[^[:blank:]]\+[[:blank:]]\+[^[:blank:]]\+')"
|
|
|
74182f |
line="${line%:*}"
|
|
|
74182f |
;;
|
|
|
74182f |
"Missing mandatory argument to command "*)
|
|
|
74182f |
cmd="${restore_err##* }"
|
|
|
74182f |
cmd_expr="$(cmd_short_expr "${cmd}")"
|
|
|
74182f |
line="$(grep -n '^'"${cmd_expr}" "${file}" | grep -m1 -v '^[0-9]\+\:'"${cmd_expr}"'[[:blank:]]\+[^[:blank:]]\+')"
|
|
|
74182f |
line="${line%:*}"
|
|
|
74182f |
;;
|
|
|
74182f |
"Command "*"is invalid in restore mode"*)
|
|
|
74182f |
restore_err_cmd="${restore_err#*: }"
|
|
|
74182f |
restore_err_cmd="${restore_err_cmd#*\`}"
|
|
|
74182f |
restore_err_cmd="${restore_err_cmd%%\'*}"
|
|
|
74182f |
cmd="${restore_err_cmd##* }"
|
|
|
74182f |
cmd_expr="$(cmd_short_expr "${cmd}")"
|
|
|
74182f |
line="$(grep -m1 -ne '^'"${cmd_expr}"'[[:blank:]]\+' -e '^'"${restore_err_cmd}"'$' "${file}")"
|
|
|
74182f |
line="${line%:*}"
|
|
|
74182f |
;;
|
|
|
74182f |
"Error in line "*)
|
|
|
74182f |
line="${restore_err%: *}"
|
|
|
74182f |
line="${line##* }"
|
|
|
74182f |
;;
|
|
|
74182f |
*)
|
|
|
74182f |
rm "${retfile}"
|
|
|
74182f |
CLEAN_FILES="${CLEAN_FILES%* ${retfile}}"
|
|
|
74182f |
return 1
|
|
|
74182f |
;;
|
|
|
74182f |
esac
|
|
|
74182f |
|
|
|
74182f |
[ -z "${line}" ] && return 1
|
|
|
74182f |
|
|
|
74182f |
warn "Skipped invalid entry: $(sed "${line}q;d" "${file}")"
|
|
|
74182f |
sed -i -e "${line}d" "${file}"
|
|
|
74182f |
|
|
|
74182f |
[ -s "${file}" ] || return 1
|
|
|
74182f |
done
|
|
|
74182f |
|
|
|
74182f |
rm "${retfile}"
|
|
|
74182f |
CLEAN_FILES="${CLEAN_FILES%* ${retfile}}"
|
|
|
74182f |
}
|
|
|
a0fbec |
|
|
|
74182f |
load() {
|
|
|
74182f |
if [ -f ${IPSET_DATA_SAVED_FLAG} ]; then
|
|
|
74182f |
# If we have a cleanly saved directory with all sets, we can
|
|
|
74182f |
# delete any left-overs and use it
|
|
|
74182f |
rm -rf ${IPSET_DATA_DIR_BACKUP}
|
|
|
74182f |
rm -f ${IPSET_DATA_COMPAT_BACKUP}
|
|
|
74182f |
else
|
|
|
74182f |
# If sets weren't cleanly saved, restore from backups
|
|
|
74182f |
[ -d ${IPSET_DATA_DIR_BACKUP} ] && rm -rf ${IPSET_DATA_DIR} && mv -Tf ${IPSET_DATA_DIR_BACKUP} ${IPSET_DATA_DIR}
|
|
|
74182f |
[ -f ${IPSET_DATA_COMPAT_BACKUP} ] && rm -f ${IPSET_DATA_COMPAT} && mv -Tf ${IPSET_DATA_COMPAT_BACKUP} ${IPSET_DATA_COMPAT}
|
|
|
74182f |
fi
|
|
|
a0fbec |
|
|
|
74182f |
if [ ! -d ${IPSET_DATA_DIR} ] && [ ! -f ${IPSET_DATA_COMPAT} ]; then
|
|
|
74182f |
info "No existing configuration available, none loaded"
|
|
|
74182f |
touch ${IPSET_RUN}
|
|
|
74182f |
return
|
|
|
74182f |
fi
|
|
|
a0fbec |
|
|
|
74182f |
# Merge all sets into temporary file
|
|
|
74182f |
merged="$(mktemp -q /tmp/ipset.XXXXXX)"
|
|
|
74182f |
CLEAN_FILES="${CLEAN_FILES} ${merged}"
|
|
|
74182f |
chmod 600 "${merged}"
|
|
|
74182f |
set +f
|
|
|
74182f |
if [ -d ${IPSET_DATA_DIR} ]; then
|
|
|
74182f |
# Copy create commands from each saved set first, then the rest:
|
|
|
74182f |
# list:set entries depend on other sets, so make sure they all
|
|
|
74182f |
# get created first
|
|
|
74182f |
for f in "${IPSET_DATA_DIR}"/*; do
|
|
|
74182f |
[ "${f}" = "${IPSET_DATA_DIR}/*" ] && break
|
|
|
74182f |
[ -f "${f}" ] || continue
|
|
|
74182f |
grep '^c' "${f}" >> "${merged}"
|
|
|
74182f |
done
|
|
|
74182f |
for f in "${IPSET_DATA_DIR}"/*; do
|
|
|
74182f |
[ "${f}" = "${IPSET_DATA_DIR}/*" ] && break
|
|
|
74182f |
[ -f "${f}" ] || continue
|
|
|
74182f |
grep -v '^c' "${f}" >> "${merged}"
|
|
|
74182f |
done
|
|
|
74182f |
fi
|
|
|
74182f |
set -f
|
|
|
74182f |
[ -f ${IPSET_DATA_COMPAT} ] && cat ${IPSET_DATA_COMPAT} >> "${merged}"
|
|
|
74182f |
|
|
|
74182f |
# Drop sets that aren't in saved data, mark conflicts with existing sets
|
|
|
74182f |
conflicts=""
|
|
|
74182f |
IFS="
|
|
|
74182f |
"
|
|
|
74182f |
for set in $(${IPSET_BIN} list -n -t); do
|
|
|
74182f |
grep -q "^create ${set} " "${merged}" && conflicts="${conflicts}|${set}" && continue
|
|
|
74182f |
|
|
|
74182f |
# We can't destroy the set if it's in use, flush it instead
|
|
|
74182f |
if ! ${IPSET_BIN} destroy "${set}" 2>/dev/null; then
|
|
|
74182f |
${IPSET_BIN} flush "${set}"
|
|
|
74182f |
fi
|
|
|
74182f |
done
|
|
|
74182f |
unset IFS
|
|
|
74182f |
conflicts="${conflicts#|*}"
|
|
|
74182f |
|
|
|
74182f |
# Common case: if we have no conflicts, just restore in one shot
|
|
|
74182f |
if [ -z "${conflicts}" ]; then
|
|
|
74182f |
if ! ipset_restore "${merged}"; then
|
|
|
74182f |
err "Failed to restore configured sets"
|
|
|
74182f |
exit 1
|
|
|
74182f |
fi
|
|
|
74182f |
rm "${merged}"
|
|
|
74182f |
CLEAN_FILES="${CLEAN_FILES%* ${merged}}"
|
|
|
74182f |
touch ${IPSET_RUN}
|
|
|
74182f |
return
|
|
|
74182f |
fi
|
|
|
a0fbec |
|
|
|
74182f |
# Find a salt for md5sum that makes names of saved sets unique
|
|
|
74182f |
salt=0
|
|
|
74182f |
while true; do
|
|
|
74182f |
unique=1
|
|
|
74182f |
IFS="
|
|
|
74182f |
"
|
|
|
74182f |
for set in $(${IPSET_BIN} list -n -t); do
|
|
|
74182f |
if grep -q "^create $(echo "${salt}${set}" | md5sum | head -c31) " "${merged}"; then
|
|
|
74182f |
unique=0
|
|
|
74182f |
break
|
|
|
74182f |
fi
|
|
|
74182f |
done
|
|
|
74182f |
unset IFS
|
|
|
74182f |
[ ${unique} -eq 1 ] && break
|
|
|
74182f |
salt=$((salt + 1))
|
|
|
74182f |
done
|
|
|
74182f |
|
|
|
74182f |
# Add sets, mangling names for conflicting sets
|
|
|
74182f |
mangled="$(mktemp -q /tmp/ipset.XXXXXX)"
|
|
|
74182f |
CLEAN_FILES="${CLEAN_FILES} ${mangled}"
|
|
|
74182f |
chmod 600 "${mangled}"
|
|
|
74182f |
|
|
|
74da1b |
cat "${merged}" > "${mangled}"
|
|
|
74da1b |
IFS='|'
|
|
|
74da1b |
for set in ${conflicts}; do
|
|
|
74da1b |
new_name=$(echo "${salt}${set}" | md5sum | head -c31)
|
|
|
74da1b |
echo "s/^(add|create) $set /\1 $new_name /"
|
|
|
74da1b |
done | sed -i -r -f - "${mangled}"
|
|
|
74da1b |
unset IFS
|
|
|
74182f |
if ! ipset_restore "${mangled}"; then
|
|
|
74182f |
err "Failed to restore configured sets"
|
|
|
74182f |
exit 1
|
|
|
74182f |
fi
|
|
|
a0fbec |
|
|
|
74182f |
rm "${mangled}"
|
|
|
74182f |
CLEAN_FILES="${CLEAN_FILES%* ${mangled}}"
|
|
|
74182f |
|
|
|
74182f |
# Swap and delete old sets
|
|
|
74182f |
IFS='|'
|
|
|
74182f |
for set in ${conflicts}; do
|
|
|
74182f |
mangled="$(echo "${salt}${set}" | md5sum | head -c31)"
|
|
|
74182f |
if ! ${IPSET_BIN} swap "${set}" "${mangled}" 2>/dev/null; then
|
|
|
74182f |
# This fails if set types are different: try to destroy
|
|
|
74182f |
# existing set
|
|
|
74182f |
if ! ${IPSET_BIN} destroy "${set}" 2>/dev/null; then
|
|
|
74182f |
# Conflicting set is in use, we can only warn
|
|
|
74182f |
# and flush the existing set
|
|
|
74182f |
err "Cannot load set \"${set}\", set with same name and conflicting type in use"
|
|
|
74182f |
${IPSET_BIN} flush "${set}"
|
|
|
74182f |
${IPSET_BIN} destroy "${mangled}"
|
|
|
74182f |
else
|
|
|
74182f |
${IPSET_BIN} rename "${mangled}" "${set}"
|
|
|
74182f |
fi
|
|
|
74182f |
else
|
|
|
74182f |
${IPSET_BIN} destroy "${mangled}"
|
|
|
74182f |
fi
|
|
|
74182f |
done
|
|
|
74182f |
unset IFS
|
|
|
a0fbec |
|
|
|
74182f |
rm "${merged}"
|
|
|
74182f |
CLEAN_FILES="${CLEAN_FILES%* ${merged}}"
|
|
|
74182f |
touch ${IPSET_RUN}
|
|
|
a0fbec |
}
|
|
|
a0fbec |
|
|
|
74182f |
cleanup() {
|
|
|
74182f |
${IPSET_BIN} flush || err "Failed to flush sets"
|
|
|
74182f |
|
|
|
74182f |
# Try to destroy all sets at once. This will fail if some are in use,
|
|
|
74182f |
# destroy all the other ones in that case
|
|
|
74182f |
${IPSET_BIN} destroy 2>/dev/null && return
|
|
|
74182f |
IFS="
|
|
|
74182f |
"
|
|
|
74182f |
for set in $(${IPSET_BIN} list -n -t); do
|
|
|
74182f |
if ! ${IPSET_BIN} destroy "${set}"; then
|
|
|
74182f |
err "Failed to destroy set ${set}"
|
|
|
74182f |
fi
|
|
|
74182f |
done
|
|
|
74182f |
unset IFS
|
|
|
a0fbec |
}
|
|
|
a0fbec |
|
|
|
a0fbec |
stop() {
|
|
|
74182f |
[ -f ${IPSET_RUN} ] || { info "Not running"; return 0; }
|
|
|
74182f |
[ "${IPSET_SAVE_ON_STOP}" = "yes" ] && { save || err "Failed to save sets"; }
|
|
|
a0fbec |
|
|
|
74182f |
# Nothing to stop if the ip_set module is not loaded
|
|
|
74182f |
lsmod | grep -q "^ip_set " || { info "Not running"; rm ${IPSET_RUN}; return 0; }
|
|
|
a0fbec |
|
|
|
74182f |
# If the xt_set module is in use, then iptables is using ipset, so
|
|
|
74182f |
# refuse to stop the service
|
|
|
74182f |
if mod="$(lsmod | grep ^xt_set)"; then
|
|
|
74182f |
if [ "$(echo "${mod}" | tr -s ' ' | cut -d' ' -f3)" != "0" ]; then
|
|
|
74182f |
err "Current iptables configuration requires ipset" && return 1
|
|
|
a0fbec |
fi
|
|
|
a0fbec |
fi
|
|
|
a0fbec |
|
|
|
74182f |
cleanup
|
|
|
a0fbec |
|
|
|
74182f |
rm ${IPSET_RUN}
|
|
|
74182f |
return 0
|
|
|
74182f |
}
|
|
|
a0fbec |
|
|
|
74182f |
lock
|
|
|
a0fbec |
case "$1" in
|
|
|
74182f |
start)
|
|
|
74182f |
load
|
|
|
a0fbec |
;;
|
|
|
74182f |
stop)
|
|
|
a0fbec |
stop
|
|
|
a0fbec |
;;
|
|
|
74182f |
reload)
|
|
|
74182f |
cleanup
|
|
|
74182f |
load
|
|
|
74182f |
;;
|
|
|
74182f |
save)
|
|
|
74182f |
save
|
|
|
a0fbec |
;;
|
|
|
74182f |
*)
|
|
|
74182f |
info "Usage: $0 {start|stop|reload|save}"
|
|
|
a0fbec |
exit 1
|
|
|
a0fbec |
esac
|
|
|
a0fbec |
|
|
|
74182f |
exit $?
|