From 61cd2b742886461f16d88e1b601d85b417bab62f Mon Sep 17 00:00:00 2001 From: Jeremy Lecour Date: Thu, 28 Apr 2022 19:14:31 +0200 Subject: [PATCH] minifirewall: upstream release 22.04 --- CHANGELOG.md | 2 +- minifirewall/files/minifirewall | 273 +++++++++++++++++++++++++++----- minifirewall/tasks/main.yml | 4 +- 3 files changed, 238 insertions(+), 41 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 70e058ca..8e77c681 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -28,7 +28,7 @@ The **patch** part changes is incremented if multiple releases happen the same m * generate-ldif: Add services check for bkctld * minifirewall: restore "force-restart" and fix "restart-if-needed" * minifirewall: tail template follows symlinks -* minifirewall: upstream release 22.03.5 +* minifirewall: upstream release 22.04 * openvpn: use a subnet topology instead of the net30 default topology * tomcat: Tomcat 9 by default with Debian 11 * openvpn: use a local copy of files instead of cloning an external git repository diff --git a/minifirewall/files/minifirewall b/minifirewall/files/minifirewall index f8729f79..f383d87c 100755 --- a/minifirewall/files/minifirewall +++ b/minifirewall/files/minifirewall @@ -1,7 +1,8 @@ #!/bin/sh +# shellcheck disable=SC2059 -# minifirewall is shellscripts for easy firewalling on a standalone server -# we used netfilter/iptables http://netfilter.org/ designed for recent Linux kernel +# minifirewall is a shell script for easy firewalling on a standalone server +# It uses netfilter/iptables http://netfilter.org/ designed for recent Linux kernel # See https://gitea.evolix.org/evolix/minifirewall # Copyright (c) 2007-2022 Evolix @@ -28,7 +29,7 @@ # Description: Firewall designed for standalone server ### END INIT INFO -VERSION="22.03.5" +VERSION="22.04" NAME="minifirewall" # shellcheck disable=SC2034 @@ -97,6 +98,42 @@ BACKUPSERVERS='' LEGACY_CONFIG='off' +STATE_FILE_LATEST='/var/run/minifirewall_state_latest' +STATE_FILE_CURRENT='/var/run/minifirewall_state_current' +STATE_FILE_PREVIOUS='/var/run/minifirewall_state_previous' +STATE_FILE_DIFF='/var/run/minifirewall_state_diff' + +LOGGER_BIN=$(command -v logger) + +# No colors by default +RED='' +GREEN='' +YELLOW='' +BLUE='' +MAGENTA='' +CYAN='' +WHITE='' +BOLD='' +RESET='' +# check if stdout is a terminal... +if [ -t 1 ]; then + + # see if it supports colors... + ncolors=$(tput colors) + + if [ -n "${ncolors}" ] && [ ${ncolors} -ge 8 ]; then + RED=$(tput setaf 1) + GREEN=$(tput setaf 2) + YELLOW=$(tput setaf 3) + BLUE=$(tput setaf 4) + MAGENTA=$(tput setaf 5) + CYAN=$(tput setaf 6) + WHITE=$(tput setaf 7) + BOLD=$(tput bold) + RESET='\e[m' + fi +fi + ## pseudo dry-run : ## Uncomment and call these functions instead of the real iptables and ip6tables commands # IPT="fake_iptables" @@ -109,6 +146,16 @@ LEGACY_CONFIG='off' # } ## Beware that commands executed from included files are not modified by this trick. +syslog_info() { + if [ -x "${LOGGER_BIN}" ]; then + ${LOGGER_BIN} -t "${NAME}" -p daemon.info "$1" + fi +} +syslog_error() { + if [ -x "${LOGGER_BIN}" ]; then + ${LOGGER_BIN} -t "${NAME}" -p daemon.error "$1" + fi +} sort_values() { echo "$*" | tr ' ' '\n' | sort -h } @@ -139,37 +186,40 @@ chain_exists() { } source_file_or_error() { file=$1 - echo "...sourcing '${file}\`" + syslog_info "sourcing \`${file}'" + printf "${BLUE}sourcing \`%s'${RESET}\n" "${file}" tmpfile=$(mktemp --tmpdir=/tmp minifirewall.XXX) . "${file}" 2>"${tmpfile}" >&2 if [ -s "${tmpfile}" ]; then - echo "${file} returns standard or error output (see below). Stopping." >&2 + syslog_error "Error while sourcing ${file}" + printf "${RED}%s returns standard or error output (see below). Stopping.${RESET}\n" ${file} >&2 cat "${tmpfile}" exit 1 fi - rm "${tmpfile}" + rm -f "${tmpfile}" } source_configuration() { if ! test -f ${config_file}; then - echo "${config_file} does not exist" >&2 + printf "${RED}%s does not exist${RESET}\n" "${config_file}" >&2 ## We still want to deal with this really old configuration file ## even if it has been deprecated since Debian 8 old_config_file="/etc/firewall.rc" if test -f ${old_config_file}; then - echo "${old_config_file} is deprecated. Rename it to ${config_file}" >&2 + printf "${YELLOW}%s is deprecated and ignored. Rename it to %s${RESET}\n" "${old_config_file}" "${config_file}" >&2 fi exit 1 fi - if grep -e "iptables" -e "ip6tables" "${config_file}" | grep -qvE "^#"; then + # If we find something other than a blank line, a comment or a variable assignment + if grep --quiet --extended-regexp --invert-match "^\s*(#|$|\w+=)" "${config_file}"; then # Backward compatible mode ########################### - echo "Legacy config detected" + printf "${YELLOW}legacy config detected${RESET}\n" LEGACY_CONFIG='on' # Non-backward compatible mode @@ -191,10 +241,11 @@ source_configuration() { # and not interfere with the configuration step. tmp_config_file=$(mktemp --tmpdir=/tmp minifirewall.XXX) - grep -E "^\s*[_a-zA-Z0-9]+=" "${config_file}" > "${tmp_config_file}" + # get only variable assignments + grep -E "^\s*\w+=" "${config_file}" > "${tmp_config_file}" source_file_or_error "${tmp_config_file}" - rm "${tmp_config_file}" + rm -f "${tmp_config_file}" else source_file_or_error "${config_file}" fi @@ -207,13 +258,88 @@ source_includes() { done fi } +check_unpersisted_state() { + cmp_bin=$(command -v cmp) + diff_bin=$(command -v diff) + + if [ -z "${cmp_bin}" ]; then + printf "${YELLOW}skip state comparison (Can't find cmp command)${RESET}\n" >&2 + elif [ -z "${diff_bin}" ]; then + printf "${YELLOW}skip state comparison (Can't find diff command)${RESET}\n" >&2 + else + # store current state + mkdir -p "$(dirname "${STATE_FILE_CURRENT}")" + status_without_numbers > "${STATE_FILE_CURRENT}" + + # clean previous diff file + rm -f "${STATE_FILE_DIFF}" + + if [ -f "${STATE_FILE_LATEST}" ]; then + cmp_result=$(cmp "${STATE_FILE_LATEST}" "${STATE_FILE_CURRENT}") + cmp_rc=$? + + if [ ${cmp_rc} -eq 0 ]; then + # echo " rules have not changed since latest start" + : + elif [ ${cmp_rc} -eq 1 ]; then + diff -u "${STATE_FILE_LATEST}" "${STATE_FILE_CURRENT}" > "${STATE_FILE_DIFF}" + printf "${YELLOW}WARNING: current state is different than persisted state, check %s${RESET}\n" "${STATE_FILE_DIFF}" >&2 + else + printf "${RED}ERROR comparing rules:${RESET}\n" >&2 + echo "${cmp_result}" >&2 + fi + fi + # cleanup + rm -f "${STATE_FILE_CURRENT}" + fi +} +report_state_changes() { + cmp_bin=$(command -v cmp) + diff_bin=$(command -v diff) + + if [ -z "${cmp_bin}" ]; then + printf "${YELLOW}skip state comparison (Can't find cmp command)${RESET}\n" >&2 + return + elif [ -z "${diff_bin}" ]; then + printf "${YELLOW}skip state comparison (Can't find diff command)${RESET}\n" >&2 + else + # If there is a known state + # let's compare it with the current state + if [ -f "${STATE_FILE_LATEST}" ]; then + check_unpersisted_state + fi + + # Then reset the known state + mkdir -p "$(dirname "${STATE_FILE_LATEST}")" + status_without_numbers > "${STATE_FILE_LATEST}" + + # But if there is a previous known state + # let's compare with the new known state + if [ -f "${STATE_FILE_PREVIOUS}" ]; then + cmp_result=$(cmp "${STATE_FILE_PREVIOUS}" "${STATE_FILE_LATEST}") + cmp_rc=$? + + if [ ${cmp_rc} -eq 0 ]; then + # echo "Rules have not changed since previous start" + : + elif [ ${cmp_rc} -eq 1 ]; then + diff -u "${STATE_FILE_PREVIOUS}" "${STATE_FILE_LATEST}" > "${STATE_FILE_DIFF}" + printf "${YELLOW}INFO: rules have changed since latest start, check %s${RESET}\n" "${STATE_FILE_DIFF}" >&2 + else + printf "${RED}ERROR comparing rules:${RESET}\n" >&2 + echo "${cmp_result}" >&2 + fi + fi + fi +} start() { - echo "Start IPTables rules..." + syslog_info "starting" + printf "${BOLD}${NAME} starting${RESET}\n" # Stop and warn if error! set -e - trap 'echo "ERROR in minifirewall configuration (fix it now!) or script manipulation (fix yourself)." ' INT TERM EXIT + trap 'printf "${RED}${NAME} failed : an error occured during startup.${RESET}\n"; syslog_error "failed" ' INT TERM EXIT # sysctl network security settings ################################## @@ -238,14 +364,14 @@ start() { if [ "${SYSCTL_ICMP_ECHO_IGNORE_BROADCASTS}" = "1" ] || [ "${SYSCTL_ICMP_ECHO_IGNORE_BROADCASTS}" = "0" ]; then echo "${SYSCTL_ICMP_ECHO_IGNORE_BROADCASTS}" > /proc/sys/net/ipv4/icmp_echo_ignore_broadcasts else - echo "Invalid SYSCTL_ICMP_ECHO_IGNORE_BROADCASTS value '${SYSCTL_ICMP_ECHO_IGNORE_BROADCASTS}', must be '0' or '1'." >&2 + printf "${RED}ERROR: invalid %s value '%s', must be '0' or '1'.\n" "SYSCTL_ICMP_ECHO_IGNORE_BROADCASTS" "${SYSCTL_ICMP_ECHO_IGNORE_BROADCASTS}" >&2 exit 1 fi if [ "${SYSCTL_ICMP_IGNORE_BOGUS_ERROR_RESPONSES}" = "1" ] || [ "${SYSCTL_ICMP_IGNORE_BOGUS_ERROR_RESPONSES}" = "0" ]; then echo "${SYSCTL_ICMP_IGNORE_BOGUS_ERROR_RESPONSES}" > /proc/sys/net/ipv4/icmp_ignore_bogus_error_responses else - echo "Invalid SYSCTL_ICMP_IGNORE_BOGUS_ERROR_RESPONSES value '${SYSCTL_ICMP_IGNORE_BOGUS_ERROR_RESPONSES}', must be '0' or '1'." >&2 + printf "${RED}ERROR: invalid %s value '%s', must be '0' or '1'.\n" "SYSCTL_ICMP_IGNORE_BOGUS_ERROR_RESPONSES" "${SYSCTL_ICMP_IGNORE_BOGUS_ERROR_RESPONSES}" >&2 exit 1 fi @@ -254,14 +380,14 @@ start() { echo "${SYSCTL_ACCEPT_SOURCE_ROUTE}" > "${proc_sys_file}" done else - echo "Invalid SYSCTL_ACCEPT_SOURCE_ROUTE value '${SYSCTL_ACCEPT_SOURCE_ROUTE}', must be '0' or '1'." >&2 + printf "${RED}ERROR: invalid %s value '%s', must be '0' or '1'.\n" "SYSCTL_ACCEPT_SOURCE_ROUTE" "${SYSCTL_ACCEPT_SOURCE_ROUTE}" >&2 exit 1 fi if [ "${SYSCTL_TCP_SYNCOOKIES}" = "1" ] || [ "${SYSCTL_TCP_SYNCOOKIES}" = "0" ]; then echo "${SYSCTL_TCP_SYNCOOKIES}" > /proc/sys/net/ipv4/tcp_syncookies else - echo "Invalid SYSCTL_TCP_SYNCOOKIES value '${SYSCTL_TCP_SYNCOOKIES}', must be '0' or '1'." >&2 + printf "${RED}ERROR: invalid %s value '%s', must be '0' or '1'.\n" "SYSCTL_TCP_SYNCOOKIES" "${SYSCTL_TCP_SYNCOOKIES}" >&2 exit 1 fi @@ -273,7 +399,7 @@ start() { echo "${SYSCTL_ICMP_REDIRECTS}" > "${proc_sys_file}" done else - echo "Invalid SYSCTL_ICMP_REDIRECTS value '${SYSCTL_ICMP_REDIRECTS}', must be '0' or '1'." >&2 + printf "${RED}ERROR: invalid %s value '%s', must be '0' or '1'.\n" "SYSCTL_ICMP_REDIRECTS" "${SYSCTL_ICMP_REDIRECTS}" >&2 exit 1 fi @@ -282,7 +408,7 @@ start() { echo "${SYSCTL_RP_FILTER}" > "${proc_sys_file}" done else - echo "Invalid SYSCTL_RP_FILTER value '${SYSCTL_RP_FILTER}', must be '0' or '1'." >&2 + printf "${RED}ERROR: invalid %s value '%s', must be '0' or '1'.\n" "SYSCTL_RP_FILTER" "${SYSCTL_RP_FILTER}" >&2 exit 1 fi @@ -291,7 +417,7 @@ start() { echo "${SYSCTL_LOG_MARTIANS}" > "${proc_sys_file}" done else - echo "Invalid SYSCTL_LOG_MARTIANS value '${SYSCTL_LOG_MARTIANS}', must be '0' or '1'." >&2 + printf "${RED}ERROR: invalid %s value '%s', must be '0' or '1'.\n" "SYSCTL_LOG_MARTIANS" "${SYSCTL_LOG_MARTIANS}" >&2 exit 1 fi @@ -707,7 +833,7 @@ start() { ${IPT} -A INPUT -p tcp --sport "${server_port}" --dport 1024:65535 -s "${server_ip}" -m state --state ESTABLISHED,RELATED -j ACCEPT fi else - echo "Unrecognized syntax for BACKUPSERVERS '${server}\`. Use space-separated IP:PORT tuples." >&2 + printf "${RED}ERROR: unrecognized syntax for BACKUPSERVERS '%s\`. Use space-separated IP:PORT tuples.${RESET}\n" "${server}" >&2 exit 1 fi done @@ -718,6 +844,10 @@ start() { ${IPT6} -A INPUT -p icmpv6 -j ACCEPT fi + # source config file for remaining commands + if is_legacy_config; then + source_file_or_error "${config_file}" + fi # IPTables policy ################# @@ -754,17 +884,28 @@ start() { ${IPT6} -A OUTPUT -p udp -j DROP fi - if is_legacy_config; then - source_file_or_error "${config_file}" - fi + # Finish + ######################## trap - INT TERM EXIT - echo "...starting IPTables rules is now finish : OK" + syslog_info "started" + printf "${GREEN}${BOLD}${NAME} started${RESET}\n" + + # No need to exit on error anymore + set +e + + report_state_changes } stop() { - echo "Flush all rules and accept everything..." + syslog_info "stopping" + printf "${BOLD}${NAME} stopping${RESET}\n" + + printf "${BLUE}flushing all rules and accepting everything${RESET}\n" + + mkdir -p "$(dirname "${STATE_FILE_PREVIOUS}")" + status_without_numbers > "${STATE_FILE_PREVIOUS}" # Delete all rules ${IPT} -F INPUT @@ -839,19 +980,45 @@ stop() { ${IPT6} -X NEEDRESTRICT fi - echo "...flushing IPTables rules is now finish : OK" + rm -f "${STATE_FILE_LATEST}" "${STATE_FILE_CURRENT}" + + syslog_info "stopped" + printf "${GREEN}${BOLD}${NAME} stopped${RESET}\n" } status() { - ${IPT} -L -n -v --line-numbers - ${IPT} -t nat -L -n -v --line-numbers - ${IPT} -t mangle -L -n -v --line-numbers - ${IPT6} -L -n -v --line-numbers - ${IPT6} -t mangle -L -n -v --line-numbers + printf "${BLUE}#### iptables --list ###############################${RESET}\n" + ${IPT} --list --numeric --verbose --line-numbers + printf "\n${BLUE}### iptables --table nat --list ####################${RESET}\n" + ${IPT} --table nat --list --numeric --verbose --line-numbers + printf "\n${BLUE}#### iptables --table mangle --list ################${RESET}\n" + ${IPT} --table mangle --list --numeric --verbose --line-numbers + if is_ipv6_enabled; then + printf "\n${BLUE}#### ip6tables --list ##############################${RESET}\n" + ${IPT6} --list --numeric --verbose --line-numbers + printf "\n${BLUE}#### ip6tables --table mangle --list ###############${RESET}\n" + ${IPT6} --table mangle --list --numeric --verbose --line-numbers + fi +} + +status_without_numbers() { + printf "${BLUE}#### iptables --list ###############################${RESET}\n" + ${IPT} --list --numeric + printf "\n${BLUE}### iptables --table nat --list ####################${RESET}\n" + ${IPT} --table nat --list --numeric + printf "\n${BLUE}#### iptables --table mangle --list ################${RESET}\n" + ${IPT} --table mangle --list --numeric + if is_ipv6_enabled; then + printf "\n${BLUE}#### ip6tables --list ##############################${RESET}\n" + ${IPT6} --list --numeric + printf "\n${BLUE}#### ip6tables --table mangle --list ###############${RESET}\n" + ${IPT6} --table mangle --list --numeric + fi } reset() { - echo "Reset all IPTables counters..." + syslog_info "resetting" + printf "${BOLD}${NAME} resetting${RESET}\n" ${IPT} -Z if is_ipv6_enabled; then @@ -865,36 +1032,66 @@ reset() { ${IPT6} -t mangle -Z fi - echo "...reseting IPTables counters is now finish : OK" + syslog_info "reset" + printf "${GREEN}${BOLD}${NAME} reset${RESET}\n" } +show_version() { + cat <. + +${NAME} comes with ABSOLUTELY NO WARRANTY. +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 3 +of the License. +END +} case "${1:-''}" in start) + source_configuration + check_unpersisted_state + start ;; stop) + source_configuration + check_unpersisted_state + stop ;; status) + source_configuration + check_unpersisted_state + status ;; reset) + source_configuration + check_unpersisted_state + reset ;; restart) + source_configuration + check_unpersisted_state + stop start ;; + version) + show_version + ;; + *) - echo "Usage: $0 {start|stop|restart|status|reset}" + echo "Usage: $0 {start|stop|restart|status|reset|version}" exit 1 ;; esac diff --git a/minifirewall/tasks/main.yml b/minifirewall/tasks/main.yml index e8355ceb..f5eb9ea4 100644 --- a/minifirewall/tasks/main.yml +++ b/minifirewall/tasks/main.yml @@ -94,8 +94,8 @@ - name: Force restart minifirewall (modern mode) command: /etc/init.d/minifirewall restart register: minifirewall_init_restart - failed_when: "'starting IPTables rules is now finish : OK' not in minifirewall_init_restart.stdout" - changed_when: "'starting IPTables rules is now finish : OK' in minifirewall_init_restart.stdout" + failed_when: "'minifirewall failed' in minifirewall_init_restart.stdout" + changed_when: "'minifirewall started' in minifirewall_init_restart.stdout" when: - minifirewall_install_mode != 'legacy' - minifirewall_restart_force | bool