RPZ: code updates and bug fixes #2

Message ID 20240823213907.3528222-1-jon.murphy@ipfire.org
State New
Headers
Series RPZ: code updates and bug fixes #2 |

Commit Message

jon Aug. 23, 2024, 9:39 p.m. UTC
  - feature: added "list" action
 - update: changed "cat << heredocs" to "echo > file"
 - update: remove path to executables
 - update: reformatted code and comments (tabs to spaces)
 - update: reworded some msg_log messages
 - update: change exit codes from "1" to unique exit code numbers
 - bug: added check for empty allow/block config file
 - bug: removed auth_zone_reload (had double reload for allow/block)
 - bug: change rpz config file to `chown nobody:nobody`
 - bug: change rpz config file to `chmod 644`

Signed-off-by: Jon Murphy <jon.murphy@ipfire.org>
---
 config/rootfiles/packages/rpz |   1 +
 config/rpz/00-rpz.conf        |  22 +--
 config/rpz/block.rpz.conf     |   7 +
 config/rpz/rpz-config         | 292 ++++++++++++++++++----------------
 config/rpz/rpz-metrics        | 163 ++++++++++---------
 config/rpz/rpz-sleep          |  26 +--
 lfs/rpz                       |  20 ++-
 src/paks/rpz/uninstall.sh     |   4 +
 src/paks/rpz/update.sh        |  22 ++-
 9 files changed, 297 insertions(+), 260 deletions(-)
 create mode 100644 config/rpz/block.rpz.conf
  

Comments

jon Sept. 28, 2024, 7:34 p.m. UTC | #1
Gentle reminder…


> On Aug 23, 2024, at 4:39 PM, Jon Murphy <jon.murphy@ipfire.org> wrote:
> 
> - feature: added "list" action
> - update: changed "cat << heredocs" to "echo > file"
> - update: remove path to executables
> - update: reformatted code and comments (tabs to spaces)
> - update: reworded some msg_log messages
> - update: change exit codes from "1" to unique exit code numbers
> - bug: added check for empty allow/block config file
> - bug: removed auth_zone_reload (had double reload for allow/block)
> - bug: change rpz config file to `chown nobody:nobody`
> - bug: change rpz config file to `chmod 644`
> 
> Signed-off-by: Jon Murphy <jon.murphy@ipfire.org>
> ---
> config/rootfiles/packages/rpz |   1 +
> config/rpz/00-rpz.conf        |  22 +--
> config/rpz/block.rpz.conf     |   7 +
> config/rpz/rpz-config         | 292 ++++++++++++++++++----------------
> config/rpz/rpz-metrics        | 163 ++++++++++---------
> config/rpz/rpz-sleep          |  26 +--
> lfs/rpz                       |  20 ++-
> src/paks/rpz/uninstall.sh     |   4 +
> src/paks/rpz/update.sh        |  22 ++-
> 9 files changed, 297 insertions(+), 260 deletions(-)
> create mode 100644 config/rpz/block.rpz.conf
> 
> diff --git a/config/rootfiles/packages/rpz b/config/rootfiles/packages/rpz
> index 183825362..73c3efa06 100644
> --- a/config/rootfiles/packages/rpz
> +++ b/config/rootfiles/packages/rpz
> @@ -1,4 +1,5 @@
> etc/unbound/local.d/00-rpz.conf
> +etc/unbound/local.d/block.rpz.conf
> etc/unbound/zonefiles
> etc/unbound/zonefiles/allow.rpz
> etc/unbound/zonefiles/block.rpz
> diff --git a/config/rpz/00-rpz.conf b/config/rpz/00-rpz.conf
> index 72c1d12e5..f005a4f2e 100644
> --- a/config/rpz/00-rpz.conf
> +++ b/config/rpz/00-rpz.conf
> @@ -1,18 +1,10 @@
> server:
> -	module-config: "respip validator iterator"
> +    module-config: "respip validator iterator"
> 
> rpz:
> -    name:					allow.rpz
> -    zonefile:				/etc/unbound/zonefiles/allow.rpz
> -    rpz-action-override:	passthru
> -    rpz-log:				yes
> -    rpz-log-name:			allow
> -    rpz-signal-nxdomain-ra:	yes
> -
> -rpz:
> -    name:					block.rpz
> -    zonefile:				/etc/unbound/zonefiles/block.rpz
> -    rpz-action-override:	nxdomain
> -    rpz-log:				yes
> -    rpz-log-name:			block
> -    rpz-signal-nxdomain-ra:	yes
> +    name:                    allow.rpz
> +    zonefile:                /etc/unbound/zonefiles/allow.rpz
> +    rpz-action-override:     passthru
> +    rpz-log:                 yes
> +    rpz-log-name:            allow
> +    rpz-signal-nxdomain-ra:  yes
> diff --git a/config/rpz/block.rpz.conf b/config/rpz/block.rpz.conf
> new file mode 100644
> index 000000000..605684257
> --- /dev/null
> +++ b/config/rpz/block.rpz.conf
> @@ -0,0 +1,7 @@
> +rpz:
> +    name:                    block.rpz
> +    zonefile:                /etc/unbound/zonefiles/block.rpz
> +    rpz-action-override:     nxdomain
> +    rpz-log:                 yes
> +    rpz-log-name:            block
> +    rpz-signal-nxdomain-ra:  yes
> diff --git a/config/rpz/rpz-config b/config/rpz/rpz-config
> index a24a5c132..9278aa004 100644
> --- a/config/rpz/rpz-config
> +++ b/config/rpz/rpz-config
> @@ -19,176 +19,186 @@
> #                                                                             #
> ###############################################################################
> 
> -#	v23 - 2024-07-30
> +version="2024-08-18"	# v28
> 
> ###############     Functions     ###############
> 
> msg_log () {
> -	/usr/bin/logger --tag "${tagName}" "$*"
> -	if tty --silent ; then
> -		echo "${tagName}:" "$*"
> -	fi
> +    logger --tag "${tagName}" "$*"
> +    if tty --silent ; then
> +        echo "${tagName}:" "$*"
> +    fi
> }
> 
> check_name () {
> -	local testName="${1}"
> -	#	check for a valid name
> -	regex='^[a-zA-Z0-9_]+$'
> -	if [[ ! "${testName}" =~ $regex ]] ; then
> -		msg_log "error: rpz: the NAME is not valid: \"${testName}\". exit."
> -		exit 1
> -	fi
> +    local testName="${1}"
> +    #  check for a valid name
> +    regex='^[a-zA-Z0-9_]+$'
> +    if [[ ! "${testName}" =~ $regex ]] || [[ "${testName}" == "allow" ]] || [[ "${testName}" == "block" ]]; then
> +        msg_log "error: rpz: the NAME is not valid: \"${testName}\". exit."
> +        exit 101
> +    fi
> }
> 
> check_unbound_conf () {
> -	#	check the above config files
> -	msg_log "info: rpz: check for errors with \"unbound-checkconf\""
> -	/usr/sbin/unbound-checkconf
> -	exit_code=$?
> -	if [[ "${exit_code}" -ne 0 ]] ; then
> -		msg_log "error: rpz: unbound-checkconf. exit."
> -		exit "${exit_code}"
> -	fi
> +    #  check the above config files
> +    msg_log "info: rpz: check for errors with \"unbound-checkconf\""
> +    unbound-checkconf
> +    exit_code=$?
> +    if [[ "${exit_code}" -ne 0 ]] ; then
> +        msg_log "error: rpz: unbound-checkconf found invalid configuration."
> +        msg_log "error: rpz: In the Terminal run the command \
> +          \"unbound-checkconf\" for more information. exit."
> +        exit 102
> +    fi
> }
> 
> make_rpz_file () {
> -	local theType="${1}"	#	allow or block
> -
> -	theList="/var/ipfire/dns/rpz/${theType}list"			#	input custom list of domains
> -	theZoneFile="/etc/unbound/zonefiles/${theType}.rpz"		#	output file for RPZ
> -
> -	theAction='.'
> -	if [[ "${theType}" =~ "allow" ]] ; then
> -		theAction='rpz-passthru.'
> -	fi
> -
> -	#	does a list exist?
> -	if [[ -s "${theList}" ]] ; then
> -		#	drop any extra "blanks" and add "CNAME <RPZ action>." to each line
> -		actionList=$( /usr/bin/awk '{$1=$1};1' "${theList}" |
> -			/bin/sed "/^[^;].*[[:alnum:]]/ s|$|	CNAME	${theAction}|" )
> -
> -		msg_log "info: rpz: create zonefile for ${theList}"
> -
> -		/bin/cat <<-EOF > "${theZoneFile}"
> -		; Name:				${theType} list
> -		; Last modified:	$(date "+%Y-%m-%d at %H.%M.%S %Z")
> -		;
> -		;	domains with actions list
> -		;
> -		${actionList}
> -		EOF
> -
> -		#	reload the zone that was just updated
> -		zoneBase=$( basename "${theZoneFile}" )
> -		/usr/sbin/unbound-control auth_zone_reload -q "${zoneBase}"
> -	fi
> +    local theType="${1}"    #   allow or block
> +
> +    theList="/var/ipfire/dns/rpz/${theType}list"         #  input custom list of domains
> +    theZoneFile="/etc/unbound/zonefiles/${theType}.rpz"  #  output file for RPZ
> +
> +    #  does a list exist?
> +    if ! [[ -s "${theList}" ]] ; then
> +        msg_log "error: rpz: the ${theList} is empty. exit."
> +        exit 103
> +    fi
> +
> +    theAction='.'
> +    if [[ "${theType}" =~ "allow" ]] ; then
> +        theAction='rpz-passthru.'
> +    fi
> +
> +    #  drop any extra "blanks" and add "CNAME <RPZ action>." to each line
> +    actionList=$( awk '{$1=$1};1' "${theList}" |
> +        sed "/^[^;].*[[:alnum:]]/ s|$|  CNAME   ${theAction}|" )
> +
> +    msg_log "info: rpz: create zonefile for ${theList}"
> +
> +echo "
> +; Name:             ${theType} list
> +; Last modified:    $(date "+%Y-%m-%d at %H.%M.%S %Z")
> +;
> +;   domains with actions list
> +;
> +${actionList}
> +" > "${theZoneFile}"
> +
> }
> 
> ###############       Main        ###############
> 
> tagName="unbound"
> 
> -theAction="${1}"										#	input action
> -theName="${2}"											#	input RPZ name
> -theURL="${3}"											#	input RPZ URL
> -
> -check_name "${theName}"									#	is this a valid name?
> +theAction="${1}"                    #  input action
> +theName="${2}"                      #  input RPZ name
> +theURL="${3}"                       #  input RPZ URL
> 
> -rpzConfig="/etc/unbound/local.d/${theName}.rpz.conf"	#	output zone conf file
> -rpzFile="/etc/unbound/zonefiles/${theName}.rpz"			#	output for RPZ file
> +rpzConfig="/etc/unbound/local.d/${theName}.rpz.conf"    #  output zone conf file
> +rpzFile="/etc/unbound/zonefiles/${theName}.rpz"         #  output for RPZ file
> 
> case "${theAction}" in
> 
> -	#	add new rpz list
> -	add )
> -		#	does this config already exist?  If yes, then exit
> -		if [[ -f "${rpzConfig}" ]] ; then
> -			msg_log "info: rpz: ${rpzConfig} already exists. exit"
> -			exit 1
> -		fi
> -
> -		#	is this a valid URL?
> -		regex='^https://[-[:alnum:]\+&@#/%?=~_|!:,.;]*[-[:alnum:]\+&@#/%=~_|]'
> -		if ! [[ "${theURL}" =~ $regex ]] ; then
> -			msg_log "error: rpz: the URL is not valid: \"${theURL}\". exit."
> -			exit 1
> -		fi
> -
> -		#	create the zone config file
> -		msg_log "info: rpz: add config file \"${theName}.rpz.conf\""
> -		cat <<-EOF > "${rpzConfig}"
> -		rpz:
> -			name:					${theName}.rpz
> -			zonefile:				/etc/unbound/zonefiles/${theName}.rpz
> -			url:  ${theURL}
> -			rpz-action-override:	nxdomain
> -			rpz-log:				yes
> -			rpz-log-name:			${theName}
> -			rpz-signal-nxdomain-ra:	yes
> -		EOF
> -
> -		#	set-up zone file
> -		/usr/bin/touch "${rpzFile}"
> -		#	unbound requires these settings for rpz files
> -		/bin/chown nobody:nobody "${rpzFile}"
> -		/bin/chmod 644 "${rpzFile}"
> -		;;
> -
> -	#	trash config file & rpz file
> -	remove )
> -		if ! [[ -f "${rpzConfig}" ]] ; then
> -			msg_log "info: rpz: ${rpzConfig} does not exist. exit"
> -			exit 1
> -		fi
> -
> -		msg_log "info: rpz: remove config file & rpz file \"${theName}\""
> -		/bin/rm "${rpzConfig}"
> -		/bin/rm "${rpzFile}"
> -
> -		check_unbound_conf
> -		;;
> -
> -	#	make a new allow or block rpz file
> -	make )
> -		case "${theName}" in
> -			allow )
> -				make_rpz_file allow
> -			;;
> -
> -			block )
> -				make_rpz_file block
> -			;;
> -
> -			allowblock )
> -				make_rpz_file allow
> -				make_rpz_file block
> -			;;
> -
> -			* )
> -				msg_log "error: rpz: the NAME is not valid: \"${theName}\". exit."
> -				exit 1
> -			;;
> -		esac
> -
> -		check_unbound_conf
> -		;;
> -
> -	*)
> -		msg_log "error: rpz: missing or incorrect parameter"
> -		/usr/bin/printf "Usage:  rpzConfig.sh <ACTION> <NAME> <URL>\n"
> -		exit 1
> -		;;
> +    #  add new rpz list
> +    add )
> +	check_name "${theName}"             #  is this a valid name?
> +        #  does this config already exist?  If yes, then exit
> +        if [[ -f "${rpzConfig}" ]] ; then
> +            msg_log "error: rpz: duplicate - ${rpzConfig} already exists. exit"
> +            exit 104
> +        fi
> +
> +        #  is this a valid URL?
> +        regex='^https://[-[:alnum:]\+&@#/%?=~_|!:,.;]*[-[:alnum:]\+&@#/%=~_|]'
> +        if ! [[ "${theURL}" =~ $regex ]] ; then
> +            msg_log "error: rpz: the URL is not valid: \"${theURL}\". exit."
> +            exit 105
> +        fi
> +
> +        #  create the zone config file
> +        msg_log "info: rpz: add config file \"${theName}.rpz.conf\""
> +echo "rpz:
> +    name:                   ${theName}.rpz
> +    zonefile:               ${rpzFile}
> +    url:                    ${theURL}
> +    rpz-action-override:    nxdomain
> +    rpz-log:                yes
> +    rpz-log-name:           ${theName}
> +    rpz-signal-nxdomain-ra: yes
> +" > "${rpzConfig}"
> +        
> +        #  set-up zonefile
> +        #    create an empty rpz file
> +        touch "${rpzFile}"
> +        #  unbound requires these settings for rpz files
> +        chown nobody:nobody "${rpzFile}" "${rpzConfig}"
> +        chmod 644 "${rpzFile}" "${rpzConfig}"
> +        ;;
> +
> +    #  trash config file & rpz file
> +    remove )
> +	check_name "${theName}"             #  is this a valid name?
> +        if ! [[ -f "${rpzConfig}" ]] ; then
> +            msg_log "error: rpz: cannot remove ${rpzConfig}, does not exist. exit"
> +            exit 106
> +        fi
> +
> +        msg_log "info: rpz: remove config file & rpz file \"${theName}\""
> +        rm "${rpzConfig}"
> +        rm "${rpzFile}"
> +
> +        check_unbound_conf
> +        ;;
> +
> +    #  make a new allow or block rpz file
> +    make )
> +        case "${theName}" in
> +            allow )
> +            make_rpz_file allow
> +            ;;
> +
> +        block )
> +            make_rpz_file block
> +            ;;
> +
> +        allowblock )
> +            make_rpz_file allow
> +            make_rpz_file block
> +            ;;
> +
> +        * )
> +            msg_log \
> +              "error: rpz: the NAME \"${theName}\" is not valid - \"allow\" or \"block\" only.  exit."
> +            exit 107
> +            ;;
> +        esac
> +
> +        check_unbound_conf
> +        ;;
> +        
> +    list)
> +        awk -F' ' '/^name:|\sname:/{ gsub(/.rpz/, "" ) ; printf $2} /\surl:|^url:/{print "="$2}' \
> +          /etc/unbound/local.d/*.rpz.conf
> +        exit
> +        ;;    
> +        
> +    *)
> +        msg_log "error: rpz: missing or incorrect parameter"
> +        printf "Usage:  rpzConfig <ACTION> <NAME> <URL>\n"
> +        printf "Version:  ${version}\n"
> +        exit 108
> +        ;;
> 
> esac
> 
> -#	reload due to the changes
> +#  reload due to the changes
> msg_log  "rpz: running \"unbound-control reload\""
> -/usr/sbin/unbound-control reload
> +unbound-control reload
> exit_code=$?
> if [[ "${exit_code}" -ne 0 ]] ; then
> -	msg_log "error: rpz: unbound-control \"${theName}\". exit."
> -	exit "${exit_code}"
> +    msg_log "error: rpz: unbound-control \"${theName}\". exit."
> +    exit 109
> fi
> 
> exit
> diff --git a/config/rpz/rpz-metrics b/config/rpz/rpz-metrics
> index 4d932726e..d154f70b9 100644
> --- a/config/rpz/rpz-metrics
> +++ b/config/rpz/rpz-metrics
> @@ -19,62 +19,61 @@
> #                                                                             #
> ###############################################################################
> 
> -#	v19 on 2024-07-30
> +version="2024-08-16"	# v20
> 
> ###############       Main        ###############
> 
> -weeks="${1:-2}"			#	default to two message logs
> -sortBy="${2:-name}"  	#	by name or by hits
> +weeks="${1:-2}"         #   default to two message logs
> +sortBy="${2:-name}"     #   by name or by hits
> 
> -#	get the list of message logs for N weeks
> -messageLogs=$( find /var/log/messages* -type f |
> -	/usr/bin/sort --version-sort |
> -	head -"${weeks}" )
> +#   get the list of message logs for N weeks
> +messageLogs=$( find /var/log/messages* -type f | sort --version-sort |
> +    head -"${weeks}" )
> 
> -#	get the list of RPZ names & counts from the message log(s)
> +#   get the list of RPZ names & counts from the message log(s)
> rpzNameCount=$( for logf in ${messageLogs} ; do
> -	/usr/bin/zgrep --text --extended-regexp 'info: rpz: applied.* A IN$' "${logf}" |
> -	/usr/bin/awk '$10 ~ /\[\w*]/ { print $10 }' ;
> -	done | /usr/bin/sort | /usr/bin/uniq --count )
> +    zgrep --text --extended-regexp 'info: rpz: applied.* A IN$' "${logf}" |
> +      awk '$10 ~ /\[\w*]/ { print $10 }' ;
> +    done | sort | uniq --count )
> 
> -#	flip results and remove brackets `[` and `]`
> -rpzNameCount=$( /bin/echo "${rpzNameCount}" |
> -	/usr/bin/awk '{ print $2, $1 }' |
> -	/bin/sed --regexp-extended 's|^\[(.*)\]|\1|' )
> +#   flip results and remove brackets `[` and `]`
> +rpzNameCount=$( echo "${rpzNameCount}" |
> +    awk '{ print $2, $1 }' |
> +      sed --regexp-extended 's|^\[(.*)\]|\1|' )
> 
> -#	grab only names
> -rpzNames=$( /bin/echo "${rpzNameCount}" | /usr/bin/awk '{ print $1 }' )
> +#   grab only names
> +rpzNames=$( echo "${rpzNameCount}" | awk '{ print $1 }' )
> 
> -#	get list of RPZ files
> -rpzFileList=$( /bin/find /etc/unbound/zonefiles -type f -iname "*.rpz" )
> +#   get list of RPZ files
> +rpzFileList=$( find /etc/unbound/zonefiles -type f -iname "*.rpz" )
> 
> -#	get basename of those files
> -rpzBaseNames=$( /bin/echo "${rpzFileList}" |
> -	/bin/sed 's|/etc/unbound/zonefiles/||g ; s|\.rpz||g ;' )
> +#   get basename of those files
> +rpzBaseNames=$( echo "${rpzFileList}" |
> +    sed 's|/etc/unbound/zonefiles/||g ; s|\.rpz||g ;' )
> 
> -#	add to rpzNames
> +#   add to rpzNames
> rpzNames="${rpzNames}"$'\n'"${rpzBaseNames}"
> 
> -#	drop duplicate names
> -rpzNames=$( echo "${rpzNames}" | /usr/bin/sort --unique  )
> +#   drop duplicate names
> +rpzNames=$( echo "${rpzNames}" | sort --unique  )
> 
> -#	get line count for each RPZ
> -lineCount=$( /bin/echo "${rpzFileList}" | /usr/bin/xargs wc -l )
> +#   get line count for each RPZ
> +lineCount=$( echo "${rpzFileList}" | xargs wc -l )
> 
> -#	get comment line count and blank line count for each RPZ
> -commentCount=$( /bin/echo "${rpzFileList}" |
> -	/usr/bin/xargs /bin/grep --count -e "^$" -e "^;" )
> +#   get comment line count and blank line count for each RPZ
> +commentCount=$( echo "${rpzFileList}" |
> +    xargs grep --count -e "^$" -e "^;" )
> 
> -#	get modified date each RPZ
> -modDateList=$( /bin/echo "${rpzFileList}" | xargs stat -c '%.10y  %n' )
> +#   get modified date each RPZ
> +modDateList=$( echo "${rpzFileList}" | xargs stat -c '%.10y  %n' )
> 
> -ucListAuthZones=$( /usr/sbin/unbound-control list_auth_zones )
> +ucListAuthZones=$( unbound-control list_auth_zones )
> 
> -#	get width of RPZ names
> -pWidth=$( /bin/echo "${rpzNames}" | /usr/bin/awk '{ print $1"   " }' | wc -L )
> +#   get width of RPZ names
> +pWidth=$( echo "${rpzNames}" | awk '{ print $1"   " }' | wc -L )
> pFormat="%-${pWidth}s %-8s %-8s %-8s %10s %12s\n"
> 
> -#	print title line
> +#   print title line
> printf "${pFormat}" "name" "hits" "active" "lines" "hits/line%" "last update"
> printf -- "--------------"
> 
> @@ -83,60 +82,60 @@ totalLines=0
> totalHits=0
> while read -r theName
> do
> -	printf -- "-"									#	pretend progress bar
> -	#	get hit count
> -	theHits="0"
> -	if output=$( /bin/grep "^${theName}\s" <<< "${rpzNameCount}" ) ; then
> -		theHits=$( /bin/echo "${output}" |
> -		/usr/bin/awk '{ print $2 }' )
> -		totalHits=$(( totalHits + theHits ))
> -	fi
> -
> -	#	is this RPZ list active?
> -	theActive="disabled"
> -	if /bin/grep --quiet "^${theName}\.rpz" <<< "${ucListAuthZones}"
> -	then
> -		theActive="enabled"
> -	fi
> -
> -	#	get line count then subtract comment count and blank line count
> -	#	  from total line count
> -	theLines="n/a"
> -	hitsPerLine="0"
> -	if output=$( /bin/grep --fixed-strings "/${theName}.rpz" <<< "${lineCount}" ) ; then
> -		theLines=$( /bin/echo "${output}" | /usr/bin/awk '{ print $1 }' )
> -		totalLines=$(( totalLines + theLines ))
> -
> -		if [[ "${theLines}" -gt 2 ]] ; then
> -			hitsPerLine=$(( 100 * theHits / theLines ))
> -		fi
> -	fi
> -
> -	#	get modification date
> -	theModDate="n/a"
> -	if output=$( /bin/grep --fixed-strings "/${theName}.rpz" <<< "${modDateList}" ) ; then
> -		theModDate=$( /bin/echo "${output}" | /usr/bin/awk '{ print $1 }' )
> -	fi
> -
> -	#	add to results list
> -	theResults+="${theName} ${theHits} ${theActive} ${theLines} ${hitsPerLine} ${theModDate}"$'\n'
> +    printf -- "-"        #   pretend progress bar
> +    #   get hit count
> +    theHits="0"
> +    if output=$( grep "^${theName}\s" <<< "${rpzNameCount}" ) ; then
> +        theHits=$( echo "${output}" |
> +        awk '{ print $2 }' )
> +        totalHits=$(( totalHits + theHits ))
> +    fi
> +
> +    #   is this RPZ list active?
> +    theActive="disabled"
> +    if grep --quiet "^${theName}\.rpz" <<< "${ucListAuthZones}"
> +    then
> +        theActive="enabled"
> +    fi
> +
> +    #   get line count
> +    theLines="n/a"
> +    hitsPerLine="0"
> +    if output=$( grep --fixed-strings "/${theName}.rpz" <<< "${lineCount}" ) ; then
> +        theLines=$( echo "${output}" | awk '{ print $1 }' )
> +        totalLines=$(( totalLines + theLines ))
> +
> +        if [[ "${theLines}" -gt 2 ]] ; then
> +            hitsPerLine=$(( 100 * theHits / theLines ))
> +        fi
> +    fi
> +
> +    #   get modification date
> +    theModDate="n/a"
> +    if output=$( grep --fixed-strings "/${theName}.rpz" <<< "${modDateList}" ) ; then
> +        theModDate=$( echo "${output}" | awk '{ print $1 }' )
> +    fi
> +
> +    #   add to results list
> +    theResults+="${theName} ${theHits} ${theActive} ${theLines} ${hitsPerLine} ${theModDate}"$'\n'
> +    
> done <<< "${rpzNames}"
> 
> case "${sortBy}" in
> -	names|name)	sortArg=(-k3,3r -k1,1)			;;	#	sort by "active" then by "name"
> +    names|name) sortArg=(-k3,3r -k1,1)          ;;  #   sort by "active" then by "name"
> 
> -	hits|hit)	sortArg=(-k3,3r -k2,2nr -k1,1)	;;	#	sort by "active" then by "hits" then by "name"
> +    hits|hit)   sortArg=(-k3,3r -k2,2nr -k1,1)  ;;  #   sort by "active" then by "hits" then by "name"
> 
> -	lines|line)	sortArg=(-k3,3r -k4,4nr -k1,1)	;;	#	sort by "active" then by "lines" then by "name"
> +    lines|line) sortArg=(-k3,3r -k4,4nr -k1,1)  ;;  #   sort by "active" then by "lines" then by "name"
> esac
> 
> printf -- "--------------\n"
> -#	remove blank lines, sort, print as columns
> -/bin/echo "${theResults}" |
> -	/usr/bin/awk '!/^[[:space:]]*$/' |
> -	/usr/bin/sort "${sortArg[@]}"    |
> -	/usr/bin/awk --assign=width="${pWidth}" \
> -		'{ printf "%-*s %-8s %-8s %-8s %10s %12s\n", width, $1, $2, $3, $4, $5, $6 }'
> +#   remove blank lines, sort, print as columns
> +echo "${theResults}" |
> +    awk '!/^[[:space:]]*$/' |
> +    sort "${sortArg[@]}"    |
> +    awk --assign=width="${pWidth}" \
> +        '{ printf "%-*s %-8s %-8s %-8s %10s %12s\n", width, $1, $2, $3, $4, $5, $6 }'
> 
> printf "${pFormat}" "" "=======" "" "========" "" ""
> printf "${pFormat}" "Totals -->" "${totalHits}" "" "${totalLines}" "" ""
> diff --git a/config/rpz/rpz-sleep b/config/rpz/rpz-sleep
> index eeef1174a..dd3603599 100644
> --- a/config/rpz/rpz-sleep
> +++ b/config/rpz/rpz-sleep
> @@ -19,38 +19,38 @@
> #                                                                             #
> ###############################################################################
> 
> -#	v04 on 2024-07-05
> +version="2024-08-16"        # v05
> 
> ###############     Functions     ###############
> 
> -#	send message to message log
> +#   send message to message log
> msg_log () {
> -	/usr/bin/logger --tag "${tagName}" "$*"
> -	if /usr/bin/tty --silent ; then
> -		echo "${tagName}:" "$*"
> -	fi
> +    logger --tag "${tagName}" "$*"
> +    if tty --silent ; then
> +        echo "${tagName}:" "$*"
> +    fi
> }
> 
> ###############       Main        ###############
> 
> tagName="unbound"
> 
> -sleepTime="${1:-5m}"			#	default to sleep for 5m (5 minutes)
> +sleepTime="${1:-5m}"            #  default to sleep for 5m (5 minutes)
> 
> -zoneList=$( /usr/sbin/unbound-control list_auth_zones | /usr/bin/awk '{print $1}' )
> +zoneList=$( unbound-control list_auth_zones | awk '{print $1}' )
> 
> for zone in ${zoneList} ; do
> -	/usr/bin/printf "disable ${zone}\t"
> -	/usr/sbin/unbound-control rpz_disable "${zone}"
> +    printf "disable ${zone}\t"
> +    unbound-control rpz_disable "${zone}"
> done
> 
> msg_log "info: rpz: disabled all zones for ${sleepTime}"
> 
> -/bin/sleep "${sleepTime}"
> +sleep "${sleepTime}"
> 
> for zone in ${zoneList} ; do
> -	/usr/bin/printf "enable ${zone}\t"
> -	/usr/sbin/unbound-control rpz_enable "${zone}"
> +    printf "enable ${zone}\t"
> +    unbound-control rpz_enable "${zone}"
> done
> 
> msg_log "info: rpz: enabled all zones"
> diff --git a/lfs/rpz b/lfs/rpz
> index 73f6f2b1b..16d1d0803 100644
> --- a/lfs/rpz
> +++ b/lfs/rpz
> @@ -62,25 +62,31 @@ $(TARGET) :
> 	@$(PREBUILD)
> 	@rm -rf $(DIR_APP)
> 
> -	#	install RPZ scripts
> +	# install RPZ scripts
> 	install -v -m 755 \
> 	  $(DIR_CONF)/rpz/{rpz-config,rpz-metrics,rpz-sleep} -t /usr/sbin
> 
> +	# Add conf file to /etc directory
> +	cp -vf $(DIR_CONF)/rpz/00-rpz.conf /etc/unbound/local.d
> +	cp -vf $(DIR_CONF)/rpz/block.rpz.conf /etc/unbound/local.d
> +
> 	# Install settings folder and two empty files
> 	mkdir -pv /var/ipfire/dns/rpz
> 	touch /var/ipfire/dns/rpz/allowlist
> 	touch /var/ipfire/dns/rpz/blocklist
> 
> -	# Add conf file to /etc directory
> -	cp -vf $(DIR_CONF)/rpz/00-rpz.conf /etc/unbound/local.d
> -
> -	#	create zonefiles directory for the RPZ files and add two empty RPZ
> -	#	  files to avoid a unbound config error
> +	# create zonefiles directory for the RPZ files and add two empty RPZ
> +	#  files to avoid a unbound config error
> 	mkdir -pv /etc/unbound/zonefiles
> -	chown -v nobody:nobody /etc/unbound/zonefiles
> 	touch /etc/unbound/zonefiles/allow.rpz
> 	touch /etc/unbound/zonefiles/block.rpz
> 
> +	#  set owner for unbound related files
> +	chown -vR nobody:nobody  \
> +	  /var/ipfire/dns/rpz    \
> +	  /etc/unbound/zonefiles \
> +	  /etc/unbound/local.d
> +
> 	# Install backup definition
> 	cp -vf $(DIR_CONF)/backup/includes/rpz /var/ipfire/backup/addons/includes/rpz
> 
> diff --git a/src/paks/rpz/uninstall.sh b/src/paks/rpz/uninstall.sh
> index 4fb20e127..51edaa176 100644
> --- a/src/paks/rpz/uninstall.sh
> +++ b/src/paks/rpz/uninstall.sh
> @@ -27,5 +27,9 @@
> make_backup ${NAME}
> remove_files
> 
> +#  delete rpz config files.  Otherwise unbound will throw error:
> +#    "[1723428668] unbound-control[17117:0] error: connect: Connection refused for 127.0.0.1 port 8953"
> +/bin/rm -fv /etc/unbound/local.d/*.rpz.conf
> +
> #  start unbound to load unbound config file
> /etc/init.d/unbound start
> diff --git a/src/paks/rpz/update.sh b/src/paks/rpz/update.sh
> index 938a93a40..fd46a185a 100644
> --- a/src/paks/rpz/update.sh
> +++ b/src/paks/rpz/update.sh
> @@ -20,6 +20,24 @@
> ###############################################################################
> #
> . /opt/pakfire/lib/functions.sh
> +
> +#  from update.sh
> extract_backup_includes
> -./uninstall.sh
> -./install.sh
> +
> +#  stop unbound to delete RPZ conf file
> +/etc/init.d/unbound stop
> +
> +#  from uninstall.sh
> +make_backup ${NAME}
> +remove_files
> +
> +#  delete rpz config files.  Otherwise unbound will throw error:
> +#    "unbound-control[nn:0] error: connect: Connection refused for 127.0.0.1 port 8953"
> +/bin/rm --verbose --force /etc/unbound/local.d/*.rpz.conf
> +
> +#  from install.sh
> +extract_files
> +restore_backup ${NAME}
> +
> +#  restart unbound to load config files
> +/etc/init.d/unbound start
> -- 
> 2.30.2
> 

Jon
  
Michael Tremer Sept. 29, 2024, 11:31 a.m. UTC | #2
What are you reminding us about?

> On 28 Sep 2024, at 20:34, jon <jon.murphy@ipfire.org> wrote:
> 
> Gentle reminder…
> 
> 
>> On Aug 23, 2024, at 4:39 PM, Jon Murphy <jon.murphy@ipfire.org> wrote:
>> 
>> - feature: added "list" action
>> - update: changed "cat << heredocs" to "echo > file"
>> - update: remove path to executables
>> - update: reformatted code and comments (tabs to spaces)
>> - update: reworded some msg_log messages
>> - update: change exit codes from "1" to unique exit code numbers
>> - bug: added check for empty allow/block config file
>> - bug: removed auth_zone_reload (had double reload for allow/block)
>> - bug: change rpz config file to `chown nobody:nobody`
>> - bug: change rpz config file to `chmod 644`
>> 
>> Signed-off-by: Jon Murphy <jon.murphy@ipfire.org>
>> ---
>> config/rootfiles/packages/rpz |   1 +
>> config/rpz/00-rpz.conf        |  22 +--
>> config/rpz/block.rpz.conf     |   7 +
>> config/rpz/rpz-config         | 292 ++++++++++++++++++----------------
>> config/rpz/rpz-metrics        | 163 ++++++++++---------
>> config/rpz/rpz-sleep          |  26 +--
>> lfs/rpz                       |  20 ++-
>> src/paks/rpz/uninstall.sh     |   4 +
>> src/paks/rpz/update.sh        |  22 ++-
>> 9 files changed, 297 insertions(+), 260 deletions(-)
>> create mode 100644 config/rpz/block.rpz.conf
>> 
>> diff --git a/config/rootfiles/packages/rpz b/config/rootfiles/packages/rpz
>> index 183825362..73c3efa06 100644
>> --- a/config/rootfiles/packages/rpz
>> +++ b/config/rootfiles/packages/rpz
>> @@ -1,4 +1,5 @@
>> etc/unbound/local.d/00-rpz.conf
>> +etc/unbound/local.d/block.rpz.conf
>> etc/unbound/zonefiles
>> etc/unbound/zonefiles/allow.rpz
>> etc/unbound/zonefiles/block.rpz
>> diff --git a/config/rpz/00-rpz.conf b/config/rpz/00-rpz.conf
>> index 72c1d12e5..f005a4f2e 100644
>> --- a/config/rpz/00-rpz.conf
>> +++ b/config/rpz/00-rpz.conf
>> @@ -1,18 +1,10 @@
>> server:
>> - module-config: "respip validator iterator"
>> +    module-config: "respip validator iterator"
>> 
>> rpz:
>> -    name: allow.rpz
>> -    zonefile: /etc/unbound/zonefiles/allow.rpz
>> -    rpz-action-override: passthru
>> -    rpz-log: yes
>> -    rpz-log-name: allow
>> -    rpz-signal-nxdomain-ra: yes
>> -
>> -rpz:
>> -    name: block.rpz
>> -    zonefile: /etc/unbound/zonefiles/block.rpz
>> -    rpz-action-override: nxdomain
>> -    rpz-log: yes
>> -    rpz-log-name: block
>> -    rpz-signal-nxdomain-ra: yes
>> +    name:                    allow.rpz
>> +    zonefile:                /etc/unbound/zonefiles/allow.rpz
>> +    rpz-action-override:     passthru
>> +    rpz-log:                 yes
>> +    rpz-log-name:            allow
>> +    rpz-signal-nxdomain-ra:  yes
>> diff --git a/config/rpz/block.rpz.conf b/config/rpz/block.rpz.conf
>> new file mode 100644
>> index 000000000..605684257
>> --- /dev/null
>> +++ b/config/rpz/block.rpz.conf
>> @@ -0,0 +1,7 @@
>> +rpz:
>> +    name:                    block.rpz
>> +    zonefile:                /etc/unbound/zonefiles/block.rpz
>> +    rpz-action-override:     nxdomain
>> +    rpz-log:                 yes
>> +    rpz-log-name:            block
>> +    rpz-signal-nxdomain-ra:  yes
>> diff --git a/config/rpz/rpz-config b/config/rpz/rpz-config
>> index a24a5c132..9278aa004 100644
>> --- a/config/rpz/rpz-config
>> +++ b/config/rpz/rpz-config
>> @@ -19,176 +19,186 @@
>> #                                                                             #
>> ###############################################################################
>> 
>> -# v23 - 2024-07-30
>> +version="2024-08-18" # v28
>> 
>> ###############     Functions     ###############
>> 
>> msg_log () {
>> - /usr/bin/logger --tag "${tagName}" "$*"
>> - if tty --silent ; then
>> - echo "${tagName}:" "$*"
>> - fi
>> +    logger --tag "${tagName}" "$*"
>> +    if tty --silent ; then
>> +        echo "${tagName}:" "$*"
>> +    fi
>> }
>> 
>> check_name () {
>> - local testName="${1}"
>> - # check for a valid name
>> - regex='^[a-zA-Z0-9_]+$'
>> - if [[ ! "${testName}" =~ $regex ]] ; then
>> - msg_log "error: rpz: the NAME is not valid: \"${testName}\". exit."
>> - exit 1
>> - fi
>> +    local testName="${1}"
>> +    #  check for a valid name
>> +    regex='^[a-zA-Z0-9_]+$'
>> +    if [[ ! "${testName}" =~ $regex ]] || [[ "${testName}" == "allow" ]] || [[ "${testName}" == "block" ]]; then
>> +        msg_log "error: rpz: the NAME is not valid: \"${testName}\". exit."
>> +        exit 101
>> +    fi
>> }
>> 
>> check_unbound_conf () {
>> - # check the above config files
>> - msg_log "info: rpz: check for errors with \"unbound-checkconf\""
>> - /usr/sbin/unbound-checkconf
>> - exit_code=$?
>> - if [[ "${exit_code}" -ne 0 ]] ; then
>> - msg_log "error: rpz: unbound-checkconf. exit."
>> - exit "${exit_code}"
>> - fi
>> +    #  check the above config files
>> +    msg_log "info: rpz: check for errors with \"unbound-checkconf\""
>> +    unbound-checkconf
>> +    exit_code=$?
>> +    if [[ "${exit_code}" -ne 0 ]] ; then
>> +        msg_log "error: rpz: unbound-checkconf found invalid configuration."
>> +        msg_log "error: rpz: In the Terminal run the command \
>> +          \"unbound-checkconf\" for more information. exit."
>> +        exit 102
>> +    fi
>> }
>> 
>> make_rpz_file () {
>> - local theType="${1}" # allow or block
>> -
>> - theList="/var/ipfire/dns/rpz/${theType}list" # input custom list of domains
>> - theZoneFile="/etc/unbound/zonefiles/${theType}.rpz" # output file for RPZ
>> -
>> - theAction='.'
>> - if [[ "${theType}" =~ "allow" ]] ; then
>> - theAction='rpz-passthru.'
>> - fi
>> -
>> - # does a list exist?
>> - if [[ -s "${theList}" ]] ; then
>> - # drop any extra "blanks" and add "CNAME <RPZ action>." to each line
>> - actionList=$( /usr/bin/awk '{$1=$1};1' "${theList}" |
>> - /bin/sed "/^[^;].*[[:alnum:]]/ s|$| CNAME ${theAction}|" )
>> -
>> - msg_log "info: rpz: create zonefile for ${theList}"
>> -
>> - /bin/cat <<-EOF > "${theZoneFile}"
>> - ; Name: ${theType} list
>> - ; Last modified: $(date "+%Y-%m-%d at %H.%M.%S %Z")
>> - ;
>> - ; domains with actions list
>> - ;
>> - ${actionList}
>> - EOF
>> -
>> - # reload the zone that was just updated
>> - zoneBase=$( basename "${theZoneFile}" )
>> - /usr/sbin/unbound-control auth_zone_reload -q "${zoneBase}"
>> - fi
>> +    local theType="${1}"    #   allow or block
>> +
>> +    theList="/var/ipfire/dns/rpz/${theType}list"         #  input custom list of domains
>> +    theZoneFile="/etc/unbound/zonefiles/${theType}.rpz"  #  output file for RPZ
>> +
>> +    #  does a list exist?
>> +    if ! [[ -s "${theList}" ]] ; then
>> +        msg_log "error: rpz: the ${theList} is empty. exit."
>> +        exit 103
>> +    fi
>> +
>> +    theAction='.'
>> +    if [[ "${theType}" =~ "allow" ]] ; then
>> +        theAction='rpz-passthru.'
>> +    fi
>> +
>> +    #  drop any extra "blanks" and add "CNAME <RPZ action>." to each line
>> +    actionList=$( awk '{$1=$1};1' "${theList}" |
>> +        sed "/^[^;].*[[:alnum:]]/ s|$|  CNAME   ${theAction}|" )
>> +
>> +    msg_log "info: rpz: create zonefile for ${theList}"
>> +
>> +echo "
>> +; Name:             ${theType} list
>> +; Last modified:    $(date "+%Y-%m-%d at %H.%M.%S %Z")
>> +;
>> +;   domains with actions list
>> +;
>> +${actionList}
>> +" > "${theZoneFile}"
>> +
>> }
>> 
>> ###############       Main        ###############
>> 
>> tagName="unbound"
>> 
>> -theAction="${1}" # input action
>> -theName="${2}" # input RPZ name
>> -theURL="${3}" # input RPZ URL
>> -
>> -check_name "${theName}" # is this a valid name?
>> +theAction="${1}"                    #  input action
>> +theName="${2}"                      #  input RPZ name
>> +theURL="${3}"                       #  input RPZ URL
>> 
>> -rpzConfig="/etc/unbound/local.d/${theName}.rpz.conf" # output zone conf file
>> -rpzFile="/etc/unbound/zonefiles/${theName}.rpz" # output for RPZ file
>> +rpzConfig="/etc/unbound/local.d/${theName}.rpz.conf"    #  output zone conf file
>> +rpzFile="/etc/unbound/zonefiles/${theName}.rpz"         #  output for RPZ file
>> 
>> case "${theAction}" in
>> 
>> - # add new rpz list
>> - add )
>> - # does this config already exist?  If yes, then exit
>> - if [[ -f "${rpzConfig}" ]] ; then
>> - msg_log "info: rpz: ${rpzConfig} already exists. exit"
>> - exit 1
>> - fi
>> -
>> - # is this a valid URL?
>> - regex='^https://[-[:alnum:]\+&@#/%?=~_|!:,.;]*[-[:alnum:]\+&@#/%=~_|]'
>> - if ! [[ "${theURL}" =~ $regex ]] ; then
>> - msg_log "error: rpz: the URL is not valid: \"${theURL}\". exit."
>> - exit 1
>> - fi
>> -
>> - # create the zone config file
>> - msg_log "info: rpz: add config file \"${theName}.rpz.conf\""
>> - cat <<-EOF > "${rpzConfig}"
>> - rpz:
>> - name: ${theName}.rpz
>> - zonefile: /etc/unbound/zonefiles/${theName}.rpz
>> - url:  ${theURL}
>> - rpz-action-override: nxdomain
>> - rpz-log: yes
>> - rpz-log-name: ${theName}
>> - rpz-signal-nxdomain-ra: yes
>> - EOF
>> -
>> - # set-up zone file
>> - /usr/bin/touch "${rpzFile}"
>> - # unbound requires these settings for rpz files
>> - /bin/chown nobody:nobody "${rpzFile}"
>> - /bin/chmod 644 "${rpzFile}"
>> - ;;
>> -
>> - # trash config file & rpz file
>> - remove )
>> - if ! [[ -f "${rpzConfig}" ]] ; then
>> - msg_log "info: rpz: ${rpzConfig} does not exist. exit"
>> - exit 1
>> - fi
>> -
>> - msg_log "info: rpz: remove config file & rpz file \"${theName}\""
>> - /bin/rm "${rpzConfig}"
>> - /bin/rm "${rpzFile}"
>> -
>> - check_unbound_conf
>> - ;;
>> -
>> - # make a new allow or block rpz file
>> - make )
>> - case "${theName}" in
>> - allow )
>> - make_rpz_file allow
>> - ;;
>> -
>> - block )
>> - make_rpz_file block
>> - ;;
>> -
>> - allowblock )
>> - make_rpz_file allow
>> - make_rpz_file block
>> - ;;
>> -
>> - * )
>> - msg_log "error: rpz: the NAME is not valid: \"${theName}\". exit."
>> - exit 1
>> - ;;
>> - esac
>> -
>> - check_unbound_conf
>> - ;;
>> -
>> - *)
>> - msg_log "error: rpz: missing or incorrect parameter"
>> - /usr/bin/printf "Usage:  rpzConfig.sh <ACTION> <NAME> <URL>\n"
>> - exit 1
>> - ;;
>> +    #  add new rpz list
>> +    add )
>> + check_name "${theName}"             #  is this a valid name?
>> +        #  does this config already exist?  If yes, then exit
>> +        if [[ -f "${rpzConfig}" ]] ; then
>> +            msg_log "error: rpz: duplicate - ${rpzConfig} already exists. exit"
>> +            exit 104
>> +        fi
>> +
>> +        #  is this a valid URL?
>> +        regex='^https://[-[:alnum:]\+&@#/%?=~_|!:,.;]*[-[:alnum:]\+&@#/%=~_|]'
>> +        if ! [[ "${theURL}" =~ $regex ]] ; then
>> +            msg_log "error: rpz: the URL is not valid: \"${theURL}\". exit."
>> +            exit 105
>> +        fi
>> +
>> +        #  create the zone config file
>> +        msg_log "info: rpz: add config file \"${theName}.rpz.conf\""
>> +echo "rpz:
>> +    name:                   ${theName}.rpz
>> +    zonefile:               ${rpzFile}
>> +    url:                    ${theURL}
>> +    rpz-action-override:    nxdomain
>> +    rpz-log:                yes
>> +    rpz-log-name:           ${theName}
>> +    rpz-signal-nxdomain-ra: yes
>> +" > "${rpzConfig}"
>> +        
>> +        #  set-up zonefile
>> +        #    create an empty rpz file
>> +        touch "${rpzFile}"
>> +        #  unbound requires these settings for rpz files
>> +        chown nobody:nobody "${rpzFile}" "${rpzConfig}"
>> +        chmod 644 "${rpzFile}" "${rpzConfig}"
>> +        ;;
>> +
>> +    #  trash config file & rpz file
>> +    remove )
>> + check_name "${theName}"             #  is this a valid name?
>> +        if ! [[ -f "${rpzConfig}" ]] ; then
>> +            msg_log "error: rpz: cannot remove ${rpzConfig}, does not exist. exit"
>> +            exit 106
>> +        fi
>> +
>> +        msg_log "info: rpz: remove config file & rpz file \"${theName}\""
>> +        rm "${rpzConfig}"
>> +        rm "${rpzFile}"
>> +
>> +        check_unbound_conf
>> +        ;;
>> +
>> +    #  make a new allow or block rpz file
>> +    make )
>> +        case "${theName}" in
>> +            allow )
>> +            make_rpz_file allow
>> +            ;;
>> +
>> +        block )
>> +            make_rpz_file block
>> +            ;;
>> +
>> +        allowblock )
>> +            make_rpz_file allow
>> +            make_rpz_file block
>> +            ;;
>> +
>> +        * )
>> +            msg_log \
>> +              "error: rpz: the NAME \"${theName}\" is not valid - \"allow\" or \"block\" only.  exit."
>> +            exit 107
>> +            ;;
>> +        esac
>> +
>> +        check_unbound_conf
>> +        ;;
>> +        
>> +    list)
>> +        awk -F' ' '/^name:|\sname:/{ gsub(/.rpz/, "" ) ; printf $2} /\surl:|^url:/{print "="$2}' \
>> +          /etc/unbound/local.d/*.rpz.conf
>> +        exit
>> +        ;;    
>> +        
>> +    *)
>> +        msg_log "error: rpz: missing or incorrect parameter"
>> +        printf "Usage:  rpzConfig <ACTION> <NAME> <URL>\n"
>> +        printf "Version:  ${version}\n"
>> +        exit 108
>> +        ;;
>> 
>> esac
>> 
>> -# reload due to the changes
>> +#  reload due to the changes
>> msg_log  "rpz: running \"unbound-control reload\""
>> -/usr/sbin/unbound-control reload
>> +unbound-control reload
>> exit_code=$?
>> if [[ "${exit_code}" -ne 0 ]] ; then
>> - msg_log "error: rpz: unbound-control \"${theName}\". exit."
>> - exit "${exit_code}"
>> +    msg_log "error: rpz: unbound-control \"${theName}\". exit."
>> +    exit 109
>> fi
>> 
>> exit
>> diff --git a/config/rpz/rpz-metrics b/config/rpz/rpz-metrics
>> index 4d932726e..d154f70b9 100644
>> --- a/config/rpz/rpz-metrics
>> +++ b/config/rpz/rpz-metrics
>> @@ -19,62 +19,61 @@
>> #                                                                             #
>> ###############################################################################
>> 
>> -# v19 on 2024-07-30
>> +version="2024-08-16" # v20
>> 
>> ###############       Main        ###############
>> 
>> -weeks="${1:-2}" # default to two message logs
>> -sortBy="${2:-name}"   # by name or by hits
>> +weeks="${1:-2}"         #   default to two message logs
>> +sortBy="${2:-name}"     #   by name or by hits
>> 
>> -# get the list of message logs for N weeks
>> -messageLogs=$( find /var/log/messages* -type f |
>> - /usr/bin/sort --version-sort |
>> - head -"${weeks}" )
>> +#   get the list of message logs for N weeks
>> +messageLogs=$( find /var/log/messages* -type f | sort --version-sort |
>> +    head -"${weeks}" )
>> 
>> -# get the list of RPZ names & counts from the message log(s)
>> +#   get the list of RPZ names & counts from the message log(s)
>> rpzNameCount=$( for logf in ${messageLogs} ; do
>> - /usr/bin/zgrep --text --extended-regexp 'info: rpz: applied.* A IN$' "${logf}" |
>> - /usr/bin/awk '$10 ~ /\[\w*]/ { print $10 }' ;
>> - done | /usr/bin/sort | /usr/bin/uniq --count )
>> +    zgrep --text --extended-regexp 'info: rpz: applied.* A IN$' "${logf}" |
>> +      awk '$10 ~ /\[\w*]/ { print $10 }' ;
>> +    done | sort | uniq --count )
>> 
>> -# flip results and remove brackets `[` and `]`
>> -rpzNameCount=$( /bin/echo "${rpzNameCount}" |
>> - /usr/bin/awk '{ print $2, $1 }' |
>> - /bin/sed --regexp-extended 's|^\[(.*)\]|\1|' )
>> +#   flip results and remove brackets `[` and `]`
>> +rpzNameCount=$( echo "${rpzNameCount}" |
>> +    awk '{ print $2, $1 }' |
>> +      sed --regexp-extended 's|^\[(.*)\]|\1|' )
>> 
>> -# grab only names
>> -rpzNames=$( /bin/echo "${rpzNameCount}" | /usr/bin/awk '{ print $1 }' )
>> +#   grab only names
>> +rpzNames=$( echo "${rpzNameCount}" | awk '{ print $1 }' )
>> 
>> -# get list of RPZ files
>> -rpzFileList=$( /bin/find /etc/unbound/zonefiles -type f -iname "*.rpz" )
>> +#   get list of RPZ files
>> +rpzFileList=$( find /etc/unbound/zonefiles -type f -iname "*.rpz" )
>> 
>> -# get basename of those files
>> -rpzBaseNames=$( /bin/echo "${rpzFileList}" |
>> - /bin/sed 's|/etc/unbound/zonefiles/||g ; s|\.rpz||g ;' )
>> +#   get basename of those files
>> +rpzBaseNames=$( echo "${rpzFileList}" |
>> +    sed 's|/etc/unbound/zonefiles/||g ; s|\.rpz||g ;' )
>> 
>> -# add to rpzNames
>> +#   add to rpzNames
>> rpzNames="${rpzNames}"$'\n'"${rpzBaseNames}"
>> 
>> -# drop duplicate names
>> -rpzNames=$( echo "${rpzNames}" | /usr/bin/sort --unique  )
>> +#   drop duplicate names
>> +rpzNames=$( echo "${rpzNames}" | sort --unique  )
>> 
>> -# get line count for each RPZ
>> -lineCount=$( /bin/echo "${rpzFileList}" | /usr/bin/xargs wc -l )
>> +#   get line count for each RPZ
>> +lineCount=$( echo "${rpzFileList}" | xargs wc -l )
>> 
>> -# get comment line count and blank line count for each RPZ
>> -commentCount=$( /bin/echo "${rpzFileList}" |
>> - /usr/bin/xargs /bin/grep --count -e "^$" -e "^;" )
>> +#   get comment line count and blank line count for each RPZ
>> +commentCount=$( echo "${rpzFileList}" |
>> +    xargs grep --count -e "^$" -e "^;" )
>> 
>> -# get modified date each RPZ
>> -modDateList=$( /bin/echo "${rpzFileList}" | xargs stat -c '%.10y  %n' )
>> +#   get modified date each RPZ
>> +modDateList=$( echo "${rpzFileList}" | xargs stat -c '%.10y  %n' )
>> 
>> -ucListAuthZones=$( /usr/sbin/unbound-control list_auth_zones )
>> +ucListAuthZones=$( unbound-control list_auth_zones )
>> 
>> -# get width of RPZ names
>> -pWidth=$( /bin/echo "${rpzNames}" | /usr/bin/awk '{ print $1"   " }' | wc -L )
>> +#   get width of RPZ names
>> +pWidth=$( echo "${rpzNames}" | awk '{ print $1"   " }' | wc -L )
>> pFormat="%-${pWidth}s %-8s %-8s %-8s %10s %12s\n"
>> 
>> -# print title line
>> +#   print title line
>> printf "${pFormat}" "name" "hits" "active" "lines" "hits/line%" "last update"
>> printf -- "--------------"
>> 
>> @@ -83,60 +82,60 @@ totalLines=0
>> totalHits=0
>> while read -r theName
>> do
>> - printf -- "-" # pretend progress bar
>> - # get hit count
>> - theHits="0"
>> - if output=$( /bin/grep "^${theName}\s" <<< "${rpzNameCount}" ) ; then
>> - theHits=$( /bin/echo "${output}" |
>> - /usr/bin/awk '{ print $2 }' )
>> - totalHits=$(( totalHits + theHits ))
>> - fi
>> -
>> - # is this RPZ list active?
>> - theActive="disabled"
>> - if /bin/grep --quiet "^${theName}\.rpz" <<< "${ucListAuthZones}"
>> - then
>> - theActive="enabled"
>> - fi
>> -
>> - # get line count then subtract comment count and blank line count
>> - #  from total line count
>> - theLines="n/a"
>> - hitsPerLine="0"
>> - if output=$( /bin/grep --fixed-strings "/${theName}.rpz" <<< "${lineCount}" ) ; then
>> - theLines=$( /bin/echo "${output}" | /usr/bin/awk '{ print $1 }' )
>> - totalLines=$(( totalLines + theLines ))
>> -
>> - if [[ "${theLines}" -gt 2 ]] ; then
>> - hitsPerLine=$(( 100 * theHits / theLines ))
>> - fi
>> - fi
>> -
>> - # get modification date
>> - theModDate="n/a"
>> - if output=$( /bin/grep --fixed-strings "/${theName}.rpz" <<< "${modDateList}" ) ; then
>> - theModDate=$( /bin/echo "${output}" | /usr/bin/awk '{ print $1 }' )
>> - fi
>> -
>> - # add to results list
>> - theResults+="${theName} ${theHits} ${theActive} ${theLines} ${hitsPerLine} ${theModDate}"$'\n'
>> +    printf -- "-"        #   pretend progress bar
>> +    #   get hit count
>> +    theHits="0"
>> +    if output=$( grep "^${theName}\s" <<< "${rpzNameCount}" ) ; then
>> +        theHits=$( echo "${output}" |
>> +        awk '{ print $2 }' )
>> +        totalHits=$(( totalHits + theHits ))
>> +    fi
>> +
>> +    #   is this RPZ list active?
>> +    theActive="disabled"
>> +    if grep --quiet "^${theName}\.rpz" <<< "${ucListAuthZones}"
>> +    then
>> +        theActive="enabled"
>> +    fi
>> +
>> +    #   get line count
>> +    theLines="n/a"
>> +    hitsPerLine="0"
>> +    if output=$( grep --fixed-strings "/${theName}.rpz" <<< "${lineCount}" ) ; then
>> +        theLines=$( echo "${output}" | awk '{ print $1 }' )
>> +        totalLines=$(( totalLines + theLines ))
>> +
>> +        if [[ "${theLines}" -gt 2 ]] ; then
>> +            hitsPerLine=$(( 100 * theHits / theLines ))
>> +        fi
>> +    fi
>> +
>> +    #   get modification date
>> +    theModDate="n/a"
>> +    if output=$( grep --fixed-strings "/${theName}.rpz" <<< "${modDateList}" ) ; then
>> +        theModDate=$( echo "${output}" | awk '{ print $1 }' )
>> +    fi
>> +
>> +    #   add to results list
>> +    theResults+="${theName} ${theHits} ${theActive} ${theLines} ${hitsPerLine} ${theModDate}"$'\n'
>> +    
>> done <<< "${rpzNames}"
>> 
>> case "${sortBy}" in
>> - names|name) sortArg=(-k3,3r -k1,1) ;; # sort by "active" then by "name"
>> +    names|name) sortArg=(-k3,3r -k1,1)          ;;  #   sort by "active" then by "name"
>> 
>> - hits|hit) sortArg=(-k3,3r -k2,2nr -k1,1) ;; # sort by "active" then by "hits" then by "name"
>> +    hits|hit)   sortArg=(-k3,3r -k2,2nr -k1,1)  ;;  #   sort by "active" then by "hits" then by "name"
>> 
>> - lines|line) sortArg=(-k3,3r -k4,4nr -k1,1) ;; # sort by "active" then by "lines" then by "name"
>> +    lines|line) sortArg=(-k3,3r -k4,4nr -k1,1)  ;;  #   sort by "active" then by "lines" then by "name"
>> esac
>> 
>> printf -- "--------------\n"
>> -# remove blank lines, sort, print as columns
>> -/bin/echo "${theResults}" |
>> - /usr/bin/awk '!/^[[:space:]]*$/' |
>> - /usr/bin/sort "${sortArg[@]}"    |
>> - /usr/bin/awk --assign=width="${pWidth}" \
>> - '{ printf "%-*s %-8s %-8s %-8s %10s %12s\n", width, $1, $2, $3, $4, $5, $6 }'
>> +#   remove blank lines, sort, print as columns
>> +echo "${theResults}" |
>> +    awk '!/^[[:space:]]*$/' |
>> +    sort "${sortArg[@]}"    |
>> +    awk --assign=width="${pWidth}" \
>> +        '{ printf "%-*s %-8s %-8s %-8s %10s %12s\n", width, $1, $2, $3, $4, $5, $6 }'
>> 
>> printf "${pFormat}" "" "=======" "" "========" "" ""
>> printf "${pFormat}" "Totals -->" "${totalHits}" "" "${totalLines}" "" ""
>> diff --git a/config/rpz/rpz-sleep b/config/rpz/rpz-sleep
>> index eeef1174a..dd3603599 100644
>> --- a/config/rpz/rpz-sleep
>> +++ b/config/rpz/rpz-sleep
>> @@ -19,38 +19,38 @@
>> #                                                                             #
>> ###############################################################################
>> 
>> -# v04 on 2024-07-05
>> +version="2024-08-16"        # v05
>> 
>> ###############     Functions     ###############
>> 
>> -# send message to message log
>> +#   send message to message log
>> msg_log () {
>> - /usr/bin/logger --tag "${tagName}" "$*"
>> - if /usr/bin/tty --silent ; then
>> - echo "${tagName}:" "$*"
>> - fi
>> +    logger --tag "${tagName}" "$*"
>> +    if tty --silent ; then
>> +        echo "${tagName}:" "$*"
>> +    fi
>> }
>> 
>> ###############       Main        ###############
>> 
>> tagName="unbound"
>> 
>> -sleepTime="${1:-5m}" # default to sleep for 5m (5 minutes)
>> +sleepTime="${1:-5m}"            #  default to sleep for 5m (5 minutes)
>> 
>> -zoneList=$( /usr/sbin/unbound-control list_auth_zones | /usr/bin/awk '{print $1}' )
>> +zoneList=$( unbound-control list_auth_zones | awk '{print $1}' )
>> 
>> for zone in ${zoneList} ; do
>> - /usr/bin/printf "disable ${zone}\t"
>> - /usr/sbin/unbound-control rpz_disable "${zone}"
>> +    printf "disable ${zone}\t"
>> +    unbound-control rpz_disable "${zone}"
>> done
>> 
>> msg_log "info: rpz: disabled all zones for ${sleepTime}"
>> 
>> -/bin/sleep "${sleepTime}"
>> +sleep "${sleepTime}"
>> 
>> for zone in ${zoneList} ; do
>> - /usr/bin/printf "enable ${zone}\t"
>> - /usr/sbin/unbound-control rpz_enable "${zone}"
>> +    printf "enable ${zone}\t"
>> +    unbound-control rpz_enable "${zone}"
>> done
>> 
>> msg_log "info: rpz: enabled all zones"
>> diff --git a/lfs/rpz b/lfs/rpz
>> index 73f6f2b1b..16d1d0803 100644
>> --- a/lfs/rpz
>> +++ b/lfs/rpz
>> @@ -62,25 +62,31 @@ $(TARGET) :
>> @$(PREBUILD)
>> @rm -rf $(DIR_APP)
>> 
>> - # install RPZ scripts
>> + # install RPZ scripts
>> install -v -m 755 \
>>  $(DIR_CONF)/rpz/{rpz-config,rpz-metrics,rpz-sleep} -t /usr/sbin
>> 
>> + # Add conf file to /etc directory
>> + cp -vf $(DIR_CONF)/rpz/00-rpz.conf /etc/unbound/local.d
>> + cp -vf $(DIR_CONF)/rpz/block.rpz.conf /etc/unbound/local.d
>> +
>> # Install settings folder and two empty files
>> mkdir -pv /var/ipfire/dns/rpz
>> touch /var/ipfire/dns/rpz/allowlist
>> touch /var/ipfire/dns/rpz/blocklist
>> 
>> - # Add conf file to /etc directory
>> - cp -vf $(DIR_CONF)/rpz/00-rpz.conf /etc/unbound/local.d
>> -
>> - # create zonefiles directory for the RPZ files and add two empty RPZ
>> - #  files to avoid a unbound config error
>> + # create zonefiles directory for the RPZ files and add two empty RPZ
>> + #  files to avoid a unbound config error
>> mkdir -pv /etc/unbound/zonefiles
>> - chown -v nobody:nobody /etc/unbound/zonefiles
>> touch /etc/unbound/zonefiles/allow.rpz
>> touch /etc/unbound/zonefiles/block.rpz
>> 
>> + #  set owner for unbound related files
>> + chown -vR nobody:nobody  \
>> +  /var/ipfire/dns/rpz    \
>> +  /etc/unbound/zonefiles \
>> +  /etc/unbound/local.d
>> +
>> # Install backup definition
>> cp -vf $(DIR_CONF)/backup/includes/rpz /var/ipfire/backup/addons/includes/rpz
>> 
>> diff --git a/src/paks/rpz/uninstall.sh b/src/paks/rpz/uninstall.sh
>> index 4fb20e127..51edaa176 100644
>> --- a/src/paks/rpz/uninstall.sh
>> +++ b/src/paks/rpz/uninstall.sh
>> @@ -27,5 +27,9 @@
>> make_backup ${NAME}
>> remove_files
>> 
>> +#  delete rpz config files.  Otherwise unbound will throw error:
>> +#    "[1723428668] unbound-control[17117:0] error: connect: Connection refused for 127.0.0.1 port 8953"
>> +/bin/rm -fv /etc/unbound/local.d/*.rpz.conf
>> +
>> #  start unbound to load unbound config file
>> /etc/init.d/unbound start
>> diff --git a/src/paks/rpz/update.sh b/src/paks/rpz/update.sh
>> index 938a93a40..fd46a185a 100644
>> --- a/src/paks/rpz/update.sh
>> +++ b/src/paks/rpz/update.sh
>> @@ -20,6 +20,24 @@
>> ###############################################################################
>> #
>> . /opt/pakfire/lib/functions.sh
>> +
>> +#  from update.sh
>> extract_backup_includes
>> -./uninstall.sh
>> -./install.sh
>> +
>> +#  stop unbound to delete RPZ conf file
>> +/etc/init.d/unbound stop
>> +
>> +#  from uninstall.sh
>> +make_backup ${NAME}
>> +remove_files
>> +
>> +#  delete rpz config files.  Otherwise unbound will throw error:
>> +#    "unbound-control[nn:0] error: connect: Connection refused for 127.0.0.1 port 8953"
>> +/bin/rm --verbose --force /etc/unbound/local.d/*.rpz.conf
>> +
>> +#  from install.sh
>> +extract_files
>> +restore_backup ${NAME}
>> +
>> +#  restart unbound to load config files
>> +/etc/init.d/unbound start
>> -- 
>> 2.30.2
>> 
> 
> Jon
> 
> 
> -- 
> Jon Murphy
> jon.murphy@ipfire.org
> 
> 
> 
>
  
jon Sept. 29, 2024, 3:53 p.m. UTC | #3
To review and approve


See: https://lists.ipfire.org/hyperkitty/list/development@lists.ipfire.org/message/B4ZN5EDEDKRRP4BOD4M5UCICJGBYUQGN/


Jon

> On Sep 29, 2024, at 6:31 AM, Michael Tremer <michael.tremer@ipfire.org> wrote:
> 
> What are you reminding us about?
> 
>> On 28 Sep 2024, at 20:34, jon <jon.murphy@ipfire.org> wrote:
>> 
>> Gentle reminder…
>> 
>> 
>>> On Aug 23, 2024, at 4:39 PM, Jon Murphy <jon.murphy@ipfire.org> wrote:
>>> 
>>> - feature: added "list" action
>>> - update: changed "cat << heredocs" to "echo > file"
>>> - update: remove path to executables
>>> - update: reformatted code and comments (tabs to spaces)
>>> - update: reworded some msg_log messages
>>> - update: change exit codes from "1" to unique exit code numbers
>>> - bug: added check for empty allow/block config file
>>> - bug: removed auth_zone_reload (had double reload for allow/block)
>>> - bug: change rpz config file to `chown nobody:nobody`
>>> - bug: change rpz config file to `chmod 644`
>>> 
>>> Signed-off-by: Jon Murphy <jon.murphy@ipfire.org>
>>> ---
>>> config/rootfiles/packages/rpz |   1 +
>>> config/rpz/00-rpz.conf        |  22 +--
>>> config/rpz/block.rpz.conf     |   7 +
>>> config/rpz/rpz-config         | 292 ++++++++++++++++++----------------
>>> config/rpz/rpz-metrics        | 163 ++++++++++---------
>>> config/rpz/rpz-sleep          |  26 +--
>>> lfs/rpz                       |  20 ++-
>>> src/paks/rpz/uninstall.sh     |   4 +
>>> src/paks/rpz/update.sh        |  22 ++-
>>> 9 files changed, 297 insertions(+), 260 deletions(-)
>>> create mode 100644 config/rpz/block.rpz.conf
>>> 
>>> diff --git a/config/rootfiles/packages/rpz b/config/rootfiles/packages/rpz
>>> index 183825362..73c3efa06 100644
>>> --- a/config/rootfiles/packages/rpz
>>> +++ b/config/rootfiles/packages/rpz
>>> @@ -1,4 +1,5 @@
>>> etc/unbound/local.d/00-rpz.conf
>>> +etc/unbound/local.d/block.rpz.conf
>>> etc/unbound/zonefiles
>>> etc/unbound/zonefiles/allow.rpz
>>> etc/unbound/zonefiles/block.rpz
>>> diff --git a/config/rpz/00-rpz.conf b/config/rpz/00-rpz.conf
>>> index 72c1d12e5..f005a4f2e 100644
>>> --- a/config/rpz/00-rpz.conf
>>> +++ b/config/rpz/00-rpz.conf
>>> @@ -1,18 +1,10 @@
>>> server:
>>> - module-config: "respip validator iterator"
>>> +    module-config: "respip validator iterator"
>>> 
>>> rpz:
>>> -    name: allow.rpz
>>> -    zonefile: /etc/unbound/zonefiles/allow.rpz
>>> -    rpz-action-override: passthru
>>> -    rpz-log: yes
>>> -    rpz-log-name: allow
>>> -    rpz-signal-nxdomain-ra: yes
>>> -
>>> -rpz:
>>> -    name: block.rpz
>>> -    zonefile: /etc/unbound/zonefiles/block.rpz
>>> -    rpz-action-override: nxdomain
>>> -    rpz-log: yes
>>> -    rpz-log-name: block
>>> -    rpz-signal-nxdomain-ra: yes
>>> +    name:                    allow.rpz
>>> +    zonefile:                /etc/unbound/zonefiles/allow.rpz
>>> +    rpz-action-override:     passthru
>>> +    rpz-log:                 yes
>>> +    rpz-log-name:            allow
>>> +    rpz-signal-nxdomain-ra:  yes
>>> diff --git a/config/rpz/block.rpz.conf b/config/rpz/block.rpz.conf
>>> new file mode 100644
>>> index 000000000..605684257
>>> --- /dev/null
>>> +++ b/config/rpz/block.rpz.conf
>>> @@ -0,0 +1,7 @@
>>> +rpz:
>>> +    name:                    block.rpz
>>> +    zonefile:                /etc/unbound/zonefiles/block.rpz
>>> +    rpz-action-override:     nxdomain
>>> +    rpz-log:                 yes
>>> +    rpz-log-name:            block
>>> +    rpz-signal-nxdomain-ra:  yes
>>> diff --git a/config/rpz/rpz-config b/config/rpz/rpz-config
>>> index a24a5c132..9278aa004 100644
>>> --- a/config/rpz/rpz-config
>>> +++ b/config/rpz/rpz-config
>>> @@ -19,176 +19,186 @@
>>> #                                                                             #
>>> ###############################################################################
>>> 
>>> -# v23 - 2024-07-30
>>> +version="2024-08-18" # v28
>>> 
>>> ###############     Functions     ###############
>>> 
>>> msg_log () {
>>> - /usr/bin/logger --tag "${tagName}" "$*"
>>> - if tty --silent ; then
>>> - echo "${tagName}:" "$*"
>>> - fi
>>> +    logger --tag "${tagName}" "$*"
>>> +    if tty --silent ; then
>>> +        echo "${tagName}:" "$*"
>>> +    fi
>>> }
>>> 
>>> check_name () {
>>> - local testName="${1}"
>>> - # check for a valid name
>>> - regex='^[a-zA-Z0-9_]+$'
>>> - if [[ ! "${testName}" =~ $regex ]] ; then
>>> - msg_log "error: rpz: the NAME is not valid: \"${testName}\". exit."
>>> - exit 1
>>> - fi
>>> +    local testName="${1}"
>>> +    #  check for a valid name
>>> +    regex='^[a-zA-Z0-9_]+$'
>>> +    if [[ ! "${testName}" =~ $regex ]] || [[ "${testName}" == "allow" ]] || [[ "${testName}" == "block" ]]; then
>>> +        msg_log "error: rpz: the NAME is not valid: \"${testName}\". exit."
>>> +        exit 101
>>> +    fi
>>> }
>>> 
>>> check_unbound_conf () {
>>> - # check the above config files
>>> - msg_log "info: rpz: check for errors with \"unbound-checkconf\""
>>> - /usr/sbin/unbound-checkconf
>>> - exit_code=$?
>>> - if [[ "${exit_code}" -ne 0 ]] ; then
>>> - msg_log "error: rpz: unbound-checkconf. exit."
>>> - exit "${exit_code}"
>>> - fi
>>> +    #  check the above config files
>>> +    msg_log "info: rpz: check for errors with \"unbound-checkconf\""
>>> +    unbound-checkconf
>>> +    exit_code=$?
>>> +    if [[ "${exit_code}" -ne 0 ]] ; then
>>> +        msg_log "error: rpz: unbound-checkconf found invalid configuration."
>>> +        msg_log "error: rpz: In the Terminal run the command \
>>> +          \"unbound-checkconf\" for more information. exit."
>>> +        exit 102
>>> +    fi
>>> }
>>> 
>>> make_rpz_file () {
>>> - local theType="${1}" # allow or block
>>> -
>>> - theList="/var/ipfire/dns/rpz/${theType}list" # input custom list of domains
>>> - theZoneFile="/etc/unbound/zonefiles/${theType}.rpz" # output file for RPZ
>>> -
>>> - theAction='.'
>>> - if [[ "${theType}" =~ "allow" ]] ; then
>>> - theAction='rpz-passthru.'
>>> - fi
>>> -
>>> - # does a list exist?
>>> - if [[ -s "${theList}" ]] ; then
>>> - # drop any extra "blanks" and add "CNAME <RPZ action>." to each line
>>> - actionList=$( /usr/bin/awk '{$1=$1};1' "${theList}" |
>>> - /bin/sed "/^[^;].*[[:alnum:]]/ s|$| CNAME ${theAction}|" )
>>> -
>>> - msg_log "info: rpz: create zonefile for ${theList}"
>>> -
>>> - /bin/cat <<-EOF > "${theZoneFile}"
>>> - ; Name: ${theType} list
>>> - ; Last modified: $(date "+%Y-%m-%d at %H.%M.%S %Z")
>>> - ;
>>> - ; domains with actions list
>>> - ;
>>> - ${actionList}
>>> - EOF
>>> -
>>> - # reload the zone that was just updated
>>> - zoneBase=$( basename "${theZoneFile}" )
>>> - /usr/sbin/unbound-control auth_zone_reload -q "${zoneBase}"
>>> - fi
>>> +    local theType="${1}"    #   allow or block
>>> +
>>> +    theList="/var/ipfire/dns/rpz/${theType}list"         #  input custom list of domains
>>> +    theZoneFile="/etc/unbound/zonefiles/${theType}.rpz"  #  output file for RPZ
>>> +
>>> +    #  does a list exist?
>>> +    if ! [[ -s "${theList}" ]] ; then
>>> +        msg_log "error: rpz: the ${theList} is empty. exit."
>>> +        exit 103
>>> +    fi
>>> +
>>> +    theAction='.'
>>> +    if [[ "${theType}" =~ "allow" ]] ; then
>>> +        theAction='rpz-passthru.'
>>> +    fi
>>> +
>>> +    #  drop any extra "blanks" and add "CNAME <RPZ action>." to each line
>>> +    actionList=$( awk '{$1=$1};1' "${theList}" |
>>> +        sed "/^[^;].*[[:alnum:]]/ s|$|  CNAME   ${theAction}|" )
>>> +
>>> +    msg_log "info: rpz: create zonefile for ${theList}"
>>> +
>>> +echo "
>>> +; Name:             ${theType} list
>>> +; Last modified:    $(date "+%Y-%m-%d at %H.%M.%S %Z")
>>> +;
>>> +;   domains with actions list
>>> +;
>>> +${actionList}
>>> +" > "${theZoneFile}"
>>> +
>>> }
>>> 
>>> ###############       Main        ###############
>>> 
>>> tagName="unbound"
>>> 
>>> -theAction="${1}" # input action
>>> -theName="${2}" # input RPZ name
>>> -theURL="${3}" # input RPZ URL
>>> -
>>> -check_name "${theName}" # is this a valid name?
>>> +theAction="${1}"                    #  input action
>>> +theName="${2}"                      #  input RPZ name
>>> +theURL="${3}"                       #  input RPZ URL
>>> 
>>> -rpzConfig="/etc/unbound/local.d/${theName}.rpz.conf" # output zone conf file
>>> -rpzFile="/etc/unbound/zonefiles/${theName}.rpz" # output for RPZ file
>>> +rpzConfig="/etc/unbound/local.d/${theName}.rpz.conf"    #  output zone conf file
>>> +rpzFile="/etc/unbound/zonefiles/${theName}.rpz"         #  output for RPZ file
>>> 
>>> case "${theAction}" in
>>> 
>>> - # add new rpz list
>>> - add )
>>> - # does this config already exist?  If yes, then exit
>>> - if [[ -f "${rpzConfig}" ]] ; then
>>> - msg_log "info: rpz: ${rpzConfig} already exists. exit"
>>> - exit 1
>>> - fi
>>> -
>>> - # is this a valid URL?
>>> - regex='^https://[-[:alnum:]\+&@#/%?=~_|!:,.;]*[-[:alnum:]\+&@#/%=~_|]'
>>> - if ! [[ "${theURL}" =~ $regex ]] ; then
>>> - msg_log "error: rpz: the URL is not valid: \"${theURL}\". exit."
>>> - exit 1
>>> - fi
>>> -
>>> - # create the zone config file
>>> - msg_log "info: rpz: add config file \"${theName}.rpz.conf\""
>>> - cat <<-EOF > "${rpzConfig}"
>>> - rpz:
>>> - name: ${theName}.rpz
>>> - zonefile: /etc/unbound/zonefiles/${theName}.rpz
>>> - url:  ${theURL}
>>> - rpz-action-override: nxdomain
>>> - rpz-log: yes
>>> - rpz-log-name: ${theName}
>>> - rpz-signal-nxdomain-ra: yes
>>> - EOF
>>> -
>>> - # set-up zone file
>>> - /usr/bin/touch "${rpzFile}"
>>> - # unbound requires these settings for rpz files
>>> - /bin/chown nobody:nobody "${rpzFile}"
>>> - /bin/chmod 644 "${rpzFile}"
>>> - ;;
>>> -
>>> - # trash config file & rpz file
>>> - remove )
>>> - if ! [[ -f "${rpzConfig}" ]] ; then
>>> - msg_log "info: rpz: ${rpzConfig} does not exist. exit"
>>> - exit 1
>>> - fi
>>> -
>>> - msg_log "info: rpz: remove config file & rpz file \"${theName}\""
>>> - /bin/rm "${rpzConfig}"
>>> - /bin/rm "${rpzFile}"
>>> -
>>> - check_unbound_conf
>>> - ;;
>>> -
>>> - # make a new allow or block rpz file
>>> - make )
>>> - case "${theName}" in
>>> - allow )
>>> - make_rpz_file allow
>>> - ;;
>>> -
>>> - block )
>>> - make_rpz_file block
>>> - ;;
>>> -
>>> - allowblock )
>>> - make_rpz_file allow
>>> - make_rpz_file block
>>> - ;;
>>> -
>>> - * )
>>> - msg_log "error: rpz: the NAME is not valid: \"${theName}\". exit."
>>> - exit 1
>>> - ;;
>>> - esac
>>> -
>>> - check_unbound_conf
>>> - ;;
>>> -
>>> - *)
>>> - msg_log "error: rpz: missing or incorrect parameter"
>>> - /usr/bin/printf "Usage:  rpzConfig.sh <ACTION> <NAME> <URL>\n"
>>> - exit 1
>>> - ;;
>>> +    #  add new rpz list
>>> +    add )
>>> + check_name "${theName}"             #  is this a valid name?
>>> +        #  does this config already exist?  If yes, then exit
>>> +        if [[ -f "${rpzConfig}" ]] ; then
>>> +            msg_log "error: rpz: duplicate - ${rpzConfig} already exists. exit"
>>> +            exit 104
>>> +        fi
>>> +
>>> +        #  is this a valid URL?
>>> +        regex='^https://[-[:alnum:]\+&@#/%?=~_|!:,.;]*[-[:alnum:]\+&@#/%=~_|]'
>>> +        if ! [[ "${theURL}" =~ $regex ]] ; then
>>> +            msg_log "error: rpz: the URL is not valid: \"${theURL}\". exit."
>>> +            exit 105
>>> +        fi
>>> +
>>> +        #  create the zone config file
>>> +        msg_log "info: rpz: add config file \"${theName}.rpz.conf\""
>>> +echo "rpz:
>>> +    name:                   ${theName}.rpz
>>> +    zonefile:               ${rpzFile}
>>> +    url:                    ${theURL}
>>> +    rpz-action-override:    nxdomain
>>> +    rpz-log:                yes
>>> +    rpz-log-name:           ${theName}
>>> +    rpz-signal-nxdomain-ra: yes
>>> +" > "${rpzConfig}"
>>> +        
>>> +        #  set-up zonefile
>>> +        #    create an empty rpz file
>>> +        touch "${rpzFile}"
>>> +        #  unbound requires these settings for rpz files
>>> +        chown nobody:nobody "${rpzFile}" "${rpzConfig}"
>>> +        chmod 644 "${rpzFile}" "${rpzConfig}"
>>> +        ;;
>>> +
>>> +    #  trash config file & rpz file
>>> +    remove )
>>> + check_name "${theName}"             #  is this a valid name?
>>> +        if ! [[ -f "${rpzConfig}" ]] ; then
>>> +            msg_log "error: rpz: cannot remove ${rpzConfig}, does not exist. exit"
>>> +            exit 106
>>> +        fi
>>> +
>>> +        msg_log "info: rpz: remove config file & rpz file \"${theName}\""
>>> +        rm "${rpzConfig}"
>>> +        rm "${rpzFile}"
>>> +
>>> +        check_unbound_conf
>>> +        ;;
>>> +
>>> +    #  make a new allow or block rpz file
>>> +    make )
>>> +        case "${theName}" in
>>> +            allow )
>>> +            make_rpz_file allow
>>> +            ;;
>>> +
>>> +        block )
>>> +            make_rpz_file block
>>> +            ;;
>>> +
>>> +        allowblock )
>>> +            make_rpz_file allow
>>> +            make_rpz_file block
>>> +            ;;
>>> +
>>> +        * )
>>> +            msg_log \
>>> +              "error: rpz: the NAME \"${theName}\" is not valid - \"allow\" or \"block\" only.  exit."
>>> +            exit 107
>>> +            ;;
>>> +        esac
>>> +
>>> +        check_unbound_conf
>>> +        ;;
>>> +        
>>> +    list)
>>> +        awk -F' ' '/^name:|\sname:/{ gsub(/.rpz/, "" ) ; printf $2} /\surl:|^url:/{print "="$2}' \
>>> +          /etc/unbound/local.d/*.rpz.conf
>>> +        exit
>>> +        ;;    
>>> +        
>>> +    *)
>>> +        msg_log "error: rpz: missing or incorrect parameter"
>>> +        printf "Usage:  rpzConfig <ACTION> <NAME> <URL>\n"
>>> +        printf "Version:  ${version}\n"
>>> +        exit 108
>>> +        ;;
>>> 
>>> esac
>>> 
>>> -# reload due to the changes
>>> +#  reload due to the changes
>>> msg_log  "rpz: running \"unbound-control reload\""
>>> -/usr/sbin/unbound-control reload
>>> +unbound-control reload
>>> exit_code=$?
>>> if [[ "${exit_code}" -ne 0 ]] ; then
>>> - msg_log "error: rpz: unbound-control \"${theName}\". exit."
>>> - exit "${exit_code}"
>>> +    msg_log "error: rpz: unbound-control \"${theName}\". exit."
>>> +    exit 109
>>> fi
>>> 
>>> exit
>>> diff --git a/config/rpz/rpz-metrics b/config/rpz/rpz-metrics
>>> index 4d932726e..d154f70b9 100644
>>> --- a/config/rpz/rpz-metrics
>>> +++ b/config/rpz/rpz-metrics
>>> @@ -19,62 +19,61 @@
>>> #                                                                             #
>>> ###############################################################################
>>> 
>>> -# v19 on 2024-07-30
>>> +version="2024-08-16" # v20
>>> 
>>> ###############       Main        ###############
>>> 
>>> -weeks="${1:-2}" # default to two message logs
>>> -sortBy="${2:-name}"   # by name or by hits
>>> +weeks="${1:-2}"         #   default to two message logs
>>> +sortBy="${2:-name}"     #   by name or by hits
>>> 
>>> -# get the list of message logs for N weeks
>>> -messageLogs=$( find /var/log/messages* -type f |
>>> - /usr/bin/sort --version-sort |
>>> - head -"${weeks}" )
>>> +#   get the list of message logs for N weeks
>>> +messageLogs=$( find /var/log/messages* -type f | sort --version-sort |
>>> +    head -"${weeks}" )
>>> 
>>> -# get the list of RPZ names & counts from the message log(s)
>>> +#   get the list of RPZ names & counts from the message log(s)
>>> rpzNameCount=$( for logf in ${messageLogs} ; do
>>> - /usr/bin/zgrep --text --extended-regexp 'info: rpz: applied.* A IN$' "${logf}" |
>>> - /usr/bin/awk '$10 ~ /\[\w*]/ { print $10 }' ;
>>> - done | /usr/bin/sort | /usr/bin/uniq --count )
>>> +    zgrep --text --extended-regexp 'info: rpz: applied.* A IN$' "${logf}" |
>>> +      awk '$10 ~ /\[\w*]/ { print $10 }' ;
>>> +    done | sort | uniq --count )
>>> 
>>> -# flip results and remove brackets `[` and `]`
>>> -rpzNameCount=$( /bin/echo "${rpzNameCount}" |
>>> - /usr/bin/awk '{ print $2, $1 }' |
>>> - /bin/sed --regexp-extended 's|^\[(.*)\]|\1|' )
>>> +#   flip results and remove brackets `[` and `]`
>>> +rpzNameCount=$( echo "${rpzNameCount}" |
>>> +    awk '{ print $2, $1 }' |
>>> +      sed --regexp-extended 's|^\[(.*)\]|\1|' )
>>> 
>>> -# grab only names
>>> -rpzNames=$( /bin/echo "${rpzNameCount}" | /usr/bin/awk '{ print $1 }' )
>>> +#   grab only names
>>> +rpzNames=$( echo "${rpzNameCount}" | awk '{ print $1 }' )
>>> 
>>> -# get list of RPZ files
>>> -rpzFileList=$( /bin/find /etc/unbound/zonefiles -type f -iname "*.rpz" )
>>> +#   get list of RPZ files
>>> +rpzFileList=$( find /etc/unbound/zonefiles -type f -iname "*.rpz" )
>>> 
>>> -# get basename of those files
>>> -rpzBaseNames=$( /bin/echo "${rpzFileList}" |
>>> - /bin/sed 's|/etc/unbound/zonefiles/||g ; s|\.rpz||g ;' )
>>> +#   get basename of those files
>>> +rpzBaseNames=$( echo "${rpzFileList}" |
>>> +    sed 's|/etc/unbound/zonefiles/||g ; s|\.rpz||g ;' )
>>> 
>>> -# add to rpzNames
>>> +#   add to rpzNames
>>> rpzNames="${rpzNames}"$'\n'"${rpzBaseNames}"
>>> 
>>> -# drop duplicate names
>>> -rpzNames=$( echo "${rpzNames}" | /usr/bin/sort --unique  )
>>> +#   drop duplicate names
>>> +rpzNames=$( echo "${rpzNames}" | sort --unique  )
>>> 
>>> -# get line count for each RPZ
>>> -lineCount=$( /bin/echo "${rpzFileList}" | /usr/bin/xargs wc -l )
>>> +#   get line count for each RPZ
>>> +lineCount=$( echo "${rpzFileList}" | xargs wc -l )
>>> 
>>> -# get comment line count and blank line count for each RPZ
>>> -commentCount=$( /bin/echo "${rpzFileList}" |
>>> - /usr/bin/xargs /bin/grep --count -e "^$" -e "^;" )
>>> +#   get comment line count and blank line count for each RPZ
>>> +commentCount=$( echo "${rpzFileList}" |
>>> +    xargs grep --count -e "^$" -e "^;" )
>>> 
>>> -# get modified date each RPZ
>>> -modDateList=$( /bin/echo "${rpzFileList}" | xargs stat -c '%.10y  %n' )
>>> +#   get modified date each RPZ
>>> +modDateList=$( echo "${rpzFileList}" | xargs stat -c '%.10y  %n' )
>>> 
>>> -ucListAuthZones=$( /usr/sbin/unbound-control list_auth_zones )
>>> +ucListAuthZones=$( unbound-control list_auth_zones )
>>> 
>>> -# get width of RPZ names
>>> -pWidth=$( /bin/echo "${rpzNames}" | /usr/bin/awk '{ print $1"   " }' | wc -L )
>>> +#   get width of RPZ names
>>> +pWidth=$( echo "${rpzNames}" | awk '{ print $1"   " }' | wc -L )
>>> pFormat="%-${pWidth}s %-8s %-8s %-8s %10s %12s\n"
>>> 
>>> -# print title line
>>> +#   print title line
>>> printf "${pFormat}" "name" "hits" "active" "lines" "hits/line%" "last update"
>>> printf -- "--------------"
>>> 
>>> @@ -83,60 +82,60 @@ totalLines=0
>>> totalHits=0
>>> while read -r theName
>>> do
>>> - printf -- "-" # pretend progress bar
>>> - # get hit count
>>> - theHits="0"
>>> - if output=$( /bin/grep "^${theName}\s" <<< "${rpzNameCount}" ) ; then
>>> - theHits=$( /bin/echo "${output}" |
>>> - /usr/bin/awk '{ print $2 }' )
>>> - totalHits=$(( totalHits + theHits ))
>>> - fi
>>> -
>>> - # is this RPZ list active?
>>> - theActive="disabled"
>>> - if /bin/grep --quiet "^${theName}\.rpz" <<< "${ucListAuthZones}"
>>> - then
>>> - theActive="enabled"
>>> - fi
>>> -
>>> - # get line count then subtract comment count and blank line count
>>> - #  from total line count
>>> - theLines="n/a"
>>> - hitsPerLine="0"
>>> - if output=$( /bin/grep --fixed-strings "/${theName}.rpz" <<< "${lineCount}" ) ; then
>>> - theLines=$( /bin/echo "${output}" | /usr/bin/awk '{ print $1 }' )
>>> - totalLines=$(( totalLines + theLines ))
>>> -
>>> - if [[ "${theLines}" -gt 2 ]] ; then
>>> - hitsPerLine=$(( 100 * theHits / theLines ))
>>> - fi
>>> - fi
>>> -
>>> - # get modification date
>>> - theModDate="n/a"
>>> - if output=$( /bin/grep --fixed-strings "/${theName}.rpz" <<< "${modDateList}" ) ; then
>>> - theModDate=$( /bin/echo "${output}" | /usr/bin/awk '{ print $1 }' )
>>> - fi
>>> -
>>> - # add to results list
>>> - theResults+="${theName} ${theHits} ${theActive} ${theLines} ${hitsPerLine} ${theModDate}"$'\n'
>>> +    printf -- "-"        #   pretend progress bar
>>> +    #   get hit count
>>> +    theHits="0"
>>> +    if output=$( grep "^${theName}\s" <<< "${rpzNameCount}" ) ; then
>>> +        theHits=$( echo "${output}" |
>>> +        awk '{ print $2 }' )
>>> +        totalHits=$(( totalHits + theHits ))
>>> +    fi
>>> +
>>> +    #   is this RPZ list active?
>>> +    theActive="disabled"
>>> +    if grep --quiet "^${theName}\.rpz" <<< "${ucListAuthZones}"
>>> +    then
>>> +        theActive="enabled"
>>> +    fi
>>> +
>>> +    #   get line count
>>> +    theLines="n/a"
>>> +    hitsPerLine="0"
>>> +    if output=$( grep --fixed-strings "/${theName}.rpz" <<< "${lineCount}" ) ; then
>>> +        theLines=$( echo "${output}" | awk '{ print $1 }' )
>>> +        totalLines=$(( totalLines + theLines ))
>>> +
>>> +        if [[ "${theLines}" -gt 2 ]] ; then
>>> +            hitsPerLine=$(( 100 * theHits / theLines ))
>>> +        fi
>>> +    fi
>>> +
>>> +    #   get modification date
>>> +    theModDate="n/a"
>>> +    if output=$( grep --fixed-strings "/${theName}.rpz" <<< "${modDateList}" ) ; then
>>> +        theModDate=$( echo "${output}" | awk '{ print $1 }' )
>>> +    fi
>>> +
>>> +    #   add to results list
>>> +    theResults+="${theName} ${theHits} ${theActive} ${theLines} ${hitsPerLine} ${theModDate}"$'\n'
>>> +    
>>> done <<< "${rpzNames}"
>>> 
>>> case "${sortBy}" in
>>> - names|name) sortArg=(-k3,3r -k1,1) ;; # sort by "active" then by "name"
>>> +    names|name) sortArg=(-k3,3r -k1,1)          ;;  #   sort by "active" then by "name"
>>> 
>>> - hits|hit) sortArg=(-k3,3r -k2,2nr -k1,1) ;; # sort by "active" then by "hits" then by "name"
>>> +    hits|hit)   sortArg=(-k3,3r -k2,2nr -k1,1)  ;;  #   sort by "active" then by "hits" then by "name"
>>> 
>>> - lines|line) sortArg=(-k3,3r -k4,4nr -k1,1) ;; # sort by "active" then by "lines" then by "name"
>>> +    lines|line) sortArg=(-k3,3r -k4,4nr -k1,1)  ;;  #   sort by "active" then by "lines" then by "name"
>>> esac
>>> 
>>> printf -- "--------------\n"
>>> -# remove blank lines, sort, print as columns
>>> -/bin/echo "${theResults}" |
>>> - /usr/bin/awk '!/^[[:space:]]*$/' |
>>> - /usr/bin/sort "${sortArg[@]}"    |
>>> - /usr/bin/awk --assign=width="${pWidth}" \
>>> - '{ printf "%-*s %-8s %-8s %-8s %10s %12s\n", width, $1, $2, $3, $4, $5, $6 }'
>>> +#   remove blank lines, sort, print as columns
>>> +echo "${theResults}" |
>>> +    awk '!/^[[:space:]]*$/' |
>>> +    sort "${sortArg[@]}"    |
>>> +    awk --assign=width="${pWidth}" \
>>> +        '{ printf "%-*s %-8s %-8s %-8s %10s %12s\n", width, $1, $2, $3, $4, $5, $6 }'
>>> 
>>> printf "${pFormat}" "" "=======" "" "========" "" ""
>>> printf "${pFormat}" "Totals -->" "${totalHits}" "" "${totalLines}" "" ""
>>> diff --git a/config/rpz/rpz-sleep b/config/rpz/rpz-sleep
>>> index eeef1174a..dd3603599 100644
>>> --- a/config/rpz/rpz-sleep
>>> +++ b/config/rpz/rpz-sleep
>>> @@ -19,38 +19,38 @@
>>> #                                                                             #
>>> ###############################################################################
>>> 
>>> -# v04 on 2024-07-05
>>> +version="2024-08-16"        # v05
>>> 
>>> ###############     Functions     ###############
>>> 
>>> -# send message to message log
>>> +#   send message to message log
>>> msg_log () {
>>> - /usr/bin/logger --tag "${tagName}" "$*"
>>> - if /usr/bin/tty --silent ; then
>>> - echo "${tagName}:" "$*"
>>> - fi
>>> +    logger --tag "${tagName}" "$*"
>>> +    if tty --silent ; then
>>> +        echo "${tagName}:" "$*"
>>> +    fi
>>> }
>>> 
>>> ###############       Main        ###############
>>> 
>>> tagName="unbound"
>>> 
>>> -sleepTime="${1:-5m}" # default to sleep for 5m (5 minutes)
>>> +sleepTime="${1:-5m}"            #  default to sleep for 5m (5 minutes)
>>> 
>>> -zoneList=$( /usr/sbin/unbound-control list_auth_zones | /usr/bin/awk '{print $1}' )
>>> +zoneList=$( unbound-control list_auth_zones | awk '{print $1}' )
>>> 
>>> for zone in ${zoneList} ; do
>>> - /usr/bin/printf "disable ${zone}\t"
>>> - /usr/sbin/unbound-control rpz_disable "${zone}"
>>> +    printf "disable ${zone}\t"
>>> +    unbound-control rpz_disable "${zone}"
>>> done
>>> 
>>> msg_log "info: rpz: disabled all zones for ${sleepTime}"
>>> 
>>> -/bin/sleep "${sleepTime}"
>>> +sleep "${sleepTime}"
>>> 
>>> for zone in ${zoneList} ; do
>>> - /usr/bin/printf "enable ${zone}\t"
>>> - /usr/sbin/unbound-control rpz_enable "${zone}"
>>> +    printf "enable ${zone}\t"
>>> +    unbound-control rpz_enable "${zone}"
>>> done
>>> 
>>> msg_log "info: rpz: enabled all zones"
>>> diff --git a/lfs/rpz b/lfs/rpz
>>> index 73f6f2b1b..16d1d0803 100644
>>> --- a/lfs/rpz
>>> +++ b/lfs/rpz
>>> @@ -62,25 +62,31 @@ $(TARGET) :
>>> @$(PREBUILD)
>>> @rm -rf $(DIR_APP)
>>> 
>>> - # install RPZ scripts
>>> + # install RPZ scripts
>>> install -v -m 755 \
>>> $(DIR_CONF)/rpz/{rpz-config,rpz-metrics,rpz-sleep} -t /usr/sbin
>>> 
>>> + # Add conf file to /etc directory
>>> + cp -vf $(DIR_CONF)/rpz/00-rpz.conf /etc/unbound/local.d
>>> + cp -vf $(DIR_CONF)/rpz/block.rpz.conf /etc/unbound/local.d
>>> +
>>> # Install settings folder and two empty files
>>> mkdir -pv /var/ipfire/dns/rpz
>>> touch /var/ipfire/dns/rpz/allowlist
>>> touch /var/ipfire/dns/rpz/blocklist
>>> 
>>> - # Add conf file to /etc directory
>>> - cp -vf $(DIR_CONF)/rpz/00-rpz.conf /etc/unbound/local.d
>>> -
>>> - # create zonefiles directory for the RPZ files and add two empty RPZ
>>> - #  files to avoid a unbound config error
>>> + # create zonefiles directory for the RPZ files and add two empty RPZ
>>> + #  files to avoid a unbound config error
>>> mkdir -pv /etc/unbound/zonefiles
>>> - chown -v nobody:nobody /etc/unbound/zonefiles
>>> touch /etc/unbound/zonefiles/allow.rpz
>>> touch /etc/unbound/zonefiles/block.rpz
>>> 
>>> + #  set owner for unbound related files
>>> + chown -vR nobody:nobody  \
>>> +  /var/ipfire/dns/rpz    \
>>> +  /etc/unbound/zonefiles \
>>> +  /etc/unbound/local.d
>>> +
>>> # Install backup definition
>>> cp -vf $(DIR_CONF)/backup/includes/rpz /var/ipfire/backup/addons/includes/rpz
>>> 
>>> diff --git a/src/paks/rpz/uninstall.sh b/src/paks/rpz/uninstall.sh
>>> index 4fb20e127..51edaa176 100644
>>> --- a/src/paks/rpz/uninstall.sh
>>> +++ b/src/paks/rpz/uninstall.sh
>>> @@ -27,5 +27,9 @@
>>> make_backup ${NAME}
>>> remove_files
>>> 
>>> +#  delete rpz config files.  Otherwise unbound will throw error:
>>> +#    "[1723428668] unbound-control[17117:0] error: connect: Connection refused for 127.0.0.1 port 8953"
>>> +/bin/rm -fv /etc/unbound/local.d/*.rpz.conf
>>> +
>>> #  start unbound to load unbound config file
>>> /etc/init.d/unbound start
>>> diff --git a/src/paks/rpz/update.sh b/src/paks/rpz/update.sh
>>> index 938a93a40..fd46a185a 100644
>>> --- a/src/paks/rpz/update.sh
>>> +++ b/src/paks/rpz/update.sh
>>> @@ -20,6 +20,24 @@
>>> ###############################################################################
>>> #
>>> . /opt/pakfire/lib/functions.sh
>>> +
>>> +#  from update.sh
>>> extract_backup_includes
>>> -./uninstall.sh
>>> -./install.sh
>>> +
>>> +#  stop unbound to delete RPZ conf file
>>> +/etc/init.d/unbound stop
>>> +
>>> +#  from uninstall.sh
>>> +make_backup ${NAME}
>>> +remove_files
>>> +
>>> +#  delete rpz config files.  Otherwise unbound will throw error:
>>> +#    "unbound-control[nn:0] error: connect: Connection refused for 127.0.0.1 port 8953"
>>> +/bin/rm --verbose --force /etc/unbound/local.d/*.rpz.conf
>>> +
>>> +#  from install.sh
>>> +extract_files
>>> +restore_backup ${NAME}
>>> +
>>> +#  restart unbound to load config files
>>> +/etc/init.d/unbound start
>>> -- 
>>> 2.30.2
>>> 
>> 
>> Jon
>> 
>> 
>> -- 
>> Jon Murphy
>> jon.murphy@ipfire.org
>> 
>> 
>> 
>> 
> 

Jon
  
Michael Tremer Sept. 30, 2024, 4:27 p.m. UTC | #4
Hello Jon,

I don’t think that we are at a point where I feel comfortable with approving this. I believe that the fundamental questions that I have raised about this have not been addressed.

As stated before I did not look much at the code and I am not a point where a code review is what I need. I am at a point right now where I want to figure out what this feature is going to be and value is it going to bring our users. I just once again went through the Hagezi lists to find any sense in them and I must say that this is not really what could replace the URL filter at all. It is just large lists that nobody knows where they are coming from and what they contain. I don’t have any time to invest into researching alternatives.

And for this reason alone I don’t see why this would be interesting for our users.

It would really help me if you can help me understand this and help answering the questions that I have asked in the past.

What do you mean by moving *forward*? What are the next steps?

-Michael

> On 29 Sep 2024, at 16:53, jon <jon.murphy@ipfire.org> wrote:
> 
> To review and approve
> 
> 
> See: https://lists.ipfire.org/hyperkitty/list/development@lists.ipfire.org/message/B4ZN5EDEDKRRP4BOD4M5UCICJGBYUQGN/
> 
> 
> Jon
> 
>> On Sep 29, 2024, at 6:31 AM, Michael Tremer <michael.tremer@ipfire.org> wrote:
>> 
>> What are you reminding us about?
>> 
>>> On 28 Sep 2024, at 20:34, jon <jon.murphy@ipfire.org> wrote:
>>> 
>>> Gentle reminder…
>>> 
>>> 
>>>> On Aug 23, 2024, at 4:39 PM, Jon Murphy <jon.murphy@ipfire.org> wrote:
>>>> 
>>>> - feature: added "list" action
>>>> - update: changed "cat << heredocs" to "echo > file"
>>>> - update: remove path to executables
>>>> - update: reformatted code and comments (tabs to spaces)
>>>> - update: reworded some msg_log messages
>>>> - update: change exit codes from "1" to unique exit code numbers
>>>> - bug: added check for empty allow/block config file
>>>> - bug: removed auth_zone_reload (had double reload for allow/block)
>>>> - bug: change rpz config file to `chown nobody:nobody`
>>>> - bug: change rpz config file to `chmod 644`
>>>> 
>>>> Signed-off-by: Jon Murphy <jon.murphy@ipfire.org>
>>>> ---
>>>> config/rootfiles/packages/rpz |   1 +
>>>> config/rpz/00-rpz.conf        |  22 +--
>>>> config/rpz/block.rpz.conf     |   7 +
>>>> config/rpz/rpz-config         | 292 ++++++++++++++++++----------------
>>>> config/rpz/rpz-metrics        | 163 ++++++++++---------
>>>> config/rpz/rpz-sleep          |  26 +--
>>>> lfs/rpz                       |  20 ++-
>>>> src/paks/rpz/uninstall.sh     |   4 +
>>>> src/paks/rpz/update.sh        |  22 ++-
>>>> 9 files changed, 297 insertions(+), 260 deletions(-)
>>>> create mode 100644 config/rpz/block.rpz.conf
>>>> 
>>>> diff --git a/config/rootfiles/packages/rpz b/config/rootfiles/packages/rpz
>>>> index 183825362..73c3efa06 100644
>>>> --- a/config/rootfiles/packages/rpz
>>>> +++ b/config/rootfiles/packages/rpz
>>>> @@ -1,4 +1,5 @@
>>>> etc/unbound/local.d/00-rpz.conf
>>>> +etc/unbound/local.d/block.rpz.conf
>>>> etc/unbound/zonefiles
>>>> etc/unbound/zonefiles/allow.rpz
>>>> etc/unbound/zonefiles/block.rpz
>>>> diff --git a/config/rpz/00-rpz.conf b/config/rpz/00-rpz.conf
>>>> index 72c1d12e5..f005a4f2e 100644
>>>> --- a/config/rpz/00-rpz.conf
>>>> +++ b/config/rpz/00-rpz.conf
>>>> @@ -1,18 +1,10 @@
>>>> server:
>>>> - module-config: "respip validator iterator"
>>>> +    module-config: "respip validator iterator"
>>>> 
>>>> rpz:
>>>> -    name: allow.rpz
>>>> -    zonefile: /etc/unbound/zonefiles/allow.rpz
>>>> -    rpz-action-override: passthru
>>>> -    rpz-log: yes
>>>> -    rpz-log-name: allow
>>>> -    rpz-signal-nxdomain-ra: yes
>>>> -
>>>> -rpz:
>>>> -    name: block.rpz
>>>> -    zonefile: /etc/unbound/zonefiles/block.rpz
>>>> -    rpz-action-override: nxdomain
>>>> -    rpz-log: yes
>>>> -    rpz-log-name: block
>>>> -    rpz-signal-nxdomain-ra: yes
>>>> +    name:                    allow.rpz
>>>> +    zonefile:                /etc/unbound/zonefiles/allow.rpz
>>>> +    rpz-action-override:     passthru
>>>> +    rpz-log:                 yes
>>>> +    rpz-log-name:            allow
>>>> +    rpz-signal-nxdomain-ra:  yes
>>>> diff --git a/config/rpz/block.rpz.conf b/config/rpz/block.rpz.conf
>>>> new file mode 100644
>>>> index 000000000..605684257
>>>> --- /dev/null
>>>> +++ b/config/rpz/block.rpz.conf
>>>> @@ -0,0 +1,7 @@
>>>> +rpz:
>>>> +    name:                    block.rpz
>>>> +    zonefile:                /etc/unbound/zonefiles/block.rpz
>>>> +    rpz-action-override:     nxdomain
>>>> +    rpz-log:                 yes
>>>> +    rpz-log-name:            block
>>>> +    rpz-signal-nxdomain-ra:  yes
>>>> diff --git a/config/rpz/rpz-config b/config/rpz/rpz-config
>>>> index a24a5c132..9278aa004 100644
>>>> --- a/config/rpz/rpz-config
>>>> +++ b/config/rpz/rpz-config
>>>> @@ -19,176 +19,186 @@
>>>> #                                                                             #
>>>> ###############################################################################
>>>> 
>>>> -# v23 - 2024-07-30
>>>> +version="2024-08-18" # v28
>>>> 
>>>> ###############     Functions     ###############
>>>> 
>>>> msg_log () {
>>>> - /usr/bin/logger --tag "${tagName}" "$*"
>>>> - if tty --silent ; then
>>>> - echo "${tagName}:" "$*"
>>>> - fi
>>>> +    logger --tag "${tagName}" "$*"
>>>> +    if tty --silent ; then
>>>> +        echo "${tagName}:" "$*"
>>>> +    fi
>>>> }
>>>> 
>>>> check_name () {
>>>> - local testName="${1}"
>>>> - # check for a valid name
>>>> - regex='^[a-zA-Z0-9_]+$'
>>>> - if [[ ! "${testName}" =~ $regex ]] ; then
>>>> - msg_log "error: rpz: the NAME is not valid: \"${testName}\". exit."
>>>> - exit 1
>>>> - fi
>>>> +    local testName="${1}"
>>>> +    #  check for a valid name
>>>> +    regex='^[a-zA-Z0-9_]+$'
>>>> +    if [[ ! "${testName}" =~ $regex ]] || [[ "${testName}" == "allow" ]] || [[ "${testName}" == "block" ]]; then
>>>> +        msg_log "error: rpz: the NAME is not valid: \"${testName}\". exit."
>>>> +        exit 101
>>>> +    fi
>>>> }
>>>> 
>>>> check_unbound_conf () {
>>>> - # check the above config files
>>>> - msg_log "info: rpz: check for errors with \"unbound-checkconf\""
>>>> - /usr/sbin/unbound-checkconf
>>>> - exit_code=$?
>>>> - if [[ "${exit_code}" -ne 0 ]] ; then
>>>> - msg_log "error: rpz: unbound-checkconf. exit."
>>>> - exit "${exit_code}"
>>>> - fi
>>>> +    #  check the above config files
>>>> +    msg_log "info: rpz: check for errors with \"unbound-checkconf\""
>>>> +    unbound-checkconf
>>>> +    exit_code=$?
>>>> +    if [[ "${exit_code}" -ne 0 ]] ; then
>>>> +        msg_log "error: rpz: unbound-checkconf found invalid configuration."
>>>> +        msg_log "error: rpz: In the Terminal run the command \
>>>> +          \"unbound-checkconf\" for more information. exit."
>>>> +        exit 102
>>>> +    fi
>>>> }
>>>> 
>>>> make_rpz_file () {
>>>> - local theType="${1}" # allow or block
>>>> -
>>>> - theList="/var/ipfire/dns/rpz/${theType}list" # input custom list of domains
>>>> - theZoneFile="/etc/unbound/zonefiles/${theType}.rpz" # output file for RPZ
>>>> -
>>>> - theAction='.'
>>>> - if [[ "${theType}" =~ "allow" ]] ; then
>>>> - theAction='rpz-passthru.'
>>>> - fi
>>>> -
>>>> - # does a list exist?
>>>> - if [[ -s "${theList}" ]] ; then
>>>> - # drop any extra "blanks" and add "CNAME <RPZ action>." to each line
>>>> - actionList=$( /usr/bin/awk '{$1=$1};1' "${theList}" |
>>>> - /bin/sed "/^[^;].*[[:alnum:]]/ s|$| CNAME ${theAction}|" )
>>>> -
>>>> - msg_log "info: rpz: create zonefile for ${theList}"
>>>> -
>>>> - /bin/cat <<-EOF > "${theZoneFile}"
>>>> - ; Name: ${theType} list
>>>> - ; Last modified: $(date "+%Y-%m-%d at %H.%M.%S %Z")
>>>> - ;
>>>> - ; domains with actions list
>>>> - ;
>>>> - ${actionList}
>>>> - EOF
>>>> -
>>>> - # reload the zone that was just updated
>>>> - zoneBase=$( basename "${theZoneFile}" )
>>>> - /usr/sbin/unbound-control auth_zone_reload -q "${zoneBase}"
>>>> - fi
>>>> +    local theType="${1}"    #   allow or block
>>>> +
>>>> +    theList="/var/ipfire/dns/rpz/${theType}list"         #  input custom list of domains
>>>> +    theZoneFile="/etc/unbound/zonefiles/${theType}.rpz"  #  output file for RPZ
>>>> +
>>>> +    #  does a list exist?
>>>> +    if ! [[ -s "${theList}" ]] ; then
>>>> +        msg_log "error: rpz: the ${theList} is empty. exit."
>>>> +        exit 103
>>>> +    fi
>>>> +
>>>> +    theAction='.'
>>>> +    if [[ "${theType}" =~ "allow" ]] ; then
>>>> +        theAction='rpz-passthru.'
>>>> +    fi
>>>> +
>>>> +    #  drop any extra "blanks" and add "CNAME <RPZ action>." to each line
>>>> +    actionList=$( awk '{$1=$1};1' "${theList}" |
>>>> +        sed "/^[^;].*[[:alnum:]]/ s|$|  CNAME   ${theAction}|" )
>>>> +
>>>> +    msg_log "info: rpz: create zonefile for ${theList}"
>>>> +
>>>> +echo "
>>>> +; Name:             ${theType} list
>>>> +; Last modified:    $(date "+%Y-%m-%d at %H.%M.%S %Z")
>>>> +;
>>>> +;   domains with actions list
>>>> +;
>>>> +${actionList}
>>>> +" > "${theZoneFile}"
>>>> +
>>>> }
>>>> 
>>>> ###############       Main        ###############
>>>> 
>>>> tagName="unbound"
>>>> 
>>>> -theAction="${1}" # input action
>>>> -theName="${2}" # input RPZ name
>>>> -theURL="${3}" # input RPZ URL
>>>> -
>>>> -check_name "${theName}" # is this a valid name?
>>>> +theAction="${1}"                    #  input action
>>>> +theName="${2}"                      #  input RPZ name
>>>> +theURL="${3}"                       #  input RPZ URL
>>>> 
>>>> -rpzConfig="/etc/unbound/local.d/${theName}.rpz.conf" # output zone conf file
>>>> -rpzFile="/etc/unbound/zonefiles/${theName}.rpz" # output for RPZ file
>>>> +rpzConfig="/etc/unbound/local.d/${theName}.rpz.conf"    #  output zone conf file
>>>> +rpzFile="/etc/unbound/zonefiles/${theName}.rpz"         #  output for RPZ file
>>>> 
>>>> case "${theAction}" in
>>>> 
>>>> - # add new rpz list
>>>> - add )
>>>> - # does this config already exist?  If yes, then exit
>>>> - if [[ -f "${rpzConfig}" ]] ; then
>>>> - msg_log "info: rpz: ${rpzConfig} already exists. exit"
>>>> - exit 1
>>>> - fi
>>>> -
>>>> - # is this a valid URL?
>>>> - regex='^https://[-[:alnum:]\+&@#/%?=~_|!:,.;]*[-[:alnum:]\+&@#/%=~_|]'
>>>> - if ! [[ "${theURL}" =~ $regex ]] ; then
>>>> - msg_log "error: rpz: the URL is not valid: \"${theURL}\". exit."
>>>> - exit 1
>>>> - fi
>>>> -
>>>> - # create the zone config file
>>>> - msg_log "info: rpz: add config file \"${theName}.rpz.conf\""
>>>> - cat <<-EOF > "${rpzConfig}"
>>>> - rpz:
>>>> - name: ${theName}.rpz
>>>> - zonefile: /etc/unbound/zonefiles/${theName}.rpz
>>>> - url:  ${theURL}
>>>> - rpz-action-override: nxdomain
>>>> - rpz-log: yes
>>>> - rpz-log-name: ${theName}
>>>> - rpz-signal-nxdomain-ra: yes
>>>> - EOF
>>>> -
>>>> - # set-up zone file
>>>> - /usr/bin/touch "${rpzFile}"
>>>> - # unbound requires these settings for rpz files
>>>> - /bin/chown nobody:nobody "${rpzFile}"
>>>> - /bin/chmod 644 "${rpzFile}"
>>>> - ;;
>>>> -
>>>> - # trash config file & rpz file
>>>> - remove )
>>>> - if ! [[ -f "${rpzConfig}" ]] ; then
>>>> - msg_log "info: rpz: ${rpzConfig} does not exist. exit"
>>>> - exit 1
>>>> - fi
>>>> -
>>>> - msg_log "info: rpz: remove config file & rpz file \"${theName}\""
>>>> - /bin/rm "${rpzConfig}"
>>>> - /bin/rm "${rpzFile}"
>>>> -
>>>> - check_unbound_conf
>>>> - ;;
>>>> -
>>>> - # make a new allow or block rpz file
>>>> - make )
>>>> - case "${theName}" in
>>>> - allow )
>>>> - make_rpz_file allow
>>>> - ;;
>>>> -
>>>> - block )
>>>> - make_rpz_file block
>>>> - ;;
>>>> -
>>>> - allowblock )
>>>> - make_rpz_file allow
>>>> - make_rpz_file block
>>>> - ;;
>>>> -
>>>> - * )
>>>> - msg_log "error: rpz: the NAME is not valid: \"${theName}\". exit."
>>>> - exit 1
>>>> - ;;
>>>> - esac
>>>> -
>>>> - check_unbound_conf
>>>> - ;;
>>>> -
>>>> - *)
>>>> - msg_log "error: rpz: missing or incorrect parameter"
>>>> - /usr/bin/printf "Usage:  rpzConfig.sh <ACTION> <NAME> <URL>\n"
>>>> - exit 1
>>>> - ;;
>>>> +    #  add new rpz list
>>>> +    add )
>>>> + check_name "${theName}"             #  is this a valid name?
>>>> +        #  does this config already exist?  If yes, then exit
>>>> +        if [[ -f "${rpzConfig}" ]] ; then
>>>> +            msg_log "error: rpz: duplicate - ${rpzConfig} already exists. exit"
>>>> +            exit 104
>>>> +        fi
>>>> +
>>>> +        #  is this a valid URL?
>>>> +        regex='^https://[-[:alnum:]\+&@#/%?=~_|!:,.;]*[-[:alnum:]\+&@#/%=~_|]'
>>>> +        if ! [[ "${theURL}" =~ $regex ]] ; then
>>>> +            msg_log "error: rpz: the URL is not valid: \"${theURL}\". exit."
>>>> +            exit 105
>>>> +        fi
>>>> +
>>>> +        #  create the zone config file
>>>> +        msg_log "info: rpz: add config file \"${theName}.rpz.conf\""
>>>> +echo "rpz:
>>>> +    name:                   ${theName}.rpz
>>>> +    zonefile:               ${rpzFile}
>>>> +    url:                    ${theURL}
>>>> +    rpz-action-override:    nxdomain
>>>> +    rpz-log:                yes
>>>> +    rpz-log-name:           ${theName}
>>>> +    rpz-signal-nxdomain-ra: yes
>>>> +" > "${rpzConfig}"
>>>> +        
>>>> +        #  set-up zonefile
>>>> +        #    create an empty rpz file
>>>> +        touch "${rpzFile}"
>>>> +        #  unbound requires these settings for rpz files
>>>> +        chown nobody:nobody "${rpzFile}" "${rpzConfig}"
>>>> +        chmod 644 "${rpzFile}" "${rpzConfig}"
>>>> +        ;;
>>>> +
>>>> +    #  trash config file & rpz file
>>>> +    remove )
>>>> + check_name "${theName}"             #  is this a valid name?
>>>> +        if ! [[ -f "${rpzConfig}" ]] ; then
>>>> +            msg_log "error: rpz: cannot remove ${rpzConfig}, does not exist. exit"
>>>> +            exit 106
>>>> +        fi
>>>> +
>>>> +        msg_log "info: rpz: remove config file & rpz file \"${theName}\""
>>>> +        rm "${rpzConfig}"
>>>> +        rm "${rpzFile}"
>>>> +
>>>> +        check_unbound_conf
>>>> +        ;;
>>>> +
>>>> +    #  make a new allow or block rpz file
>>>> +    make )
>>>> +        case "${theName}" in
>>>> +            allow )
>>>> +            make_rpz_file allow
>>>> +            ;;
>>>> +
>>>> +        block )
>>>> +            make_rpz_file block
>>>> +            ;;
>>>> +
>>>> +        allowblock )
>>>> +            make_rpz_file allow
>>>> +            make_rpz_file block
>>>> +            ;;
>>>> +
>>>> +        * )
>>>> +            msg_log \
>>>> +              "error: rpz: the NAME \"${theName}\" is not valid - \"allow\" or \"block\" only.  exit."
>>>> +            exit 107
>>>> +            ;;
>>>> +        esac
>>>> +
>>>> +        check_unbound_conf
>>>> +        ;;
>>>> +        
>>>> +    list)
>>>> +        awk -F' ' '/^name:|\sname:/{ gsub(/.rpz/, "" ) ; printf $2} /\surl:|^url:/{print "="$2}' \
>>>> +          /etc/unbound/local.d/*.rpz.conf
>>>> +        exit
>>>> +        ;;    
>>>> +        
>>>> +    *)
>>>> +        msg_log "error: rpz: missing or incorrect parameter"
>>>> +        printf "Usage:  rpzConfig <ACTION> <NAME> <URL>\n"
>>>> +        printf "Version:  ${version}\n"
>>>> +        exit 108
>>>> +        ;;
>>>> 
>>>> esac
>>>> 
>>>> -# reload due to the changes
>>>> +#  reload due to the changes
>>>> msg_log  "rpz: running \"unbound-control reload\""
>>>> -/usr/sbin/unbound-control reload
>>>> +unbound-control reload
>>>> exit_code=$?
>>>> if [[ "${exit_code}" -ne 0 ]] ; then
>>>> - msg_log "error: rpz: unbound-control \"${theName}\". exit."
>>>> - exit "${exit_code}"
>>>> +    msg_log "error: rpz: unbound-control \"${theName}\". exit."
>>>> +    exit 109
>>>> fi
>>>> 
>>>> exit
>>>> diff --git a/config/rpz/rpz-metrics b/config/rpz/rpz-metrics
>>>> index 4d932726e..d154f70b9 100644
>>>> --- a/config/rpz/rpz-metrics
>>>> +++ b/config/rpz/rpz-metrics
>>>> @@ -19,62 +19,61 @@
>>>> #                                                                             #
>>>> ###############################################################################
>>>> 
>>>> -# v19 on 2024-07-30
>>>> +version="2024-08-16" # v20
>>>> 
>>>> ###############       Main        ###############
>>>> 
>>>> -weeks="${1:-2}" # default to two message logs
>>>> -sortBy="${2:-name}"   # by name or by hits
>>>> +weeks="${1:-2}"         #   default to two message logs
>>>> +sortBy="${2:-name}"     #   by name or by hits
>>>> 
>>>> -# get the list of message logs for N weeks
>>>> -messageLogs=$( find /var/log/messages* -type f |
>>>> - /usr/bin/sort --version-sort |
>>>> - head -"${weeks}" )
>>>> +#   get the list of message logs for N weeks
>>>> +messageLogs=$( find /var/log/messages* -type f | sort --version-sort |
>>>> +    head -"${weeks}" )
>>>> 
>>>> -# get the list of RPZ names & counts from the message log(s)
>>>> +#   get the list of RPZ names & counts from the message log(s)
>>>> rpzNameCount=$( for logf in ${messageLogs} ; do
>>>> - /usr/bin/zgrep --text --extended-regexp 'info: rpz: applied.* A IN$' "${logf}" |
>>>> - /usr/bin/awk '$10 ~ /\[\w*]/ { print $10 }' ;
>>>> - done | /usr/bin/sort | /usr/bin/uniq --count )
>>>> +    zgrep --text --extended-regexp 'info: rpz: applied.* A IN$' "${logf}" |
>>>> +      awk '$10 ~ /\[\w*]/ { print $10 }' ;
>>>> +    done | sort | uniq --count )
>>>> 
>>>> -# flip results and remove brackets `[` and `]`
>>>> -rpzNameCount=$( /bin/echo "${rpzNameCount}" |
>>>> - /usr/bin/awk '{ print $2, $1 }' |
>>>> - /bin/sed --regexp-extended 's|^\[(.*)\]|\1|' )
>>>> +#   flip results and remove brackets `[` and `]`
>>>> +rpzNameCount=$( echo "${rpzNameCount}" |
>>>> +    awk '{ print $2, $1 }' |
>>>> +      sed --regexp-extended 's|^\[(.*)\]|\1|' )
>>>> 
>>>> -# grab only names
>>>> -rpzNames=$( /bin/echo "${rpzNameCount}" | /usr/bin/awk '{ print $1 }' )
>>>> +#   grab only names
>>>> +rpzNames=$( echo "${rpzNameCount}" | awk '{ print $1 }' )
>>>> 
>>>> -# get list of RPZ files
>>>> -rpzFileList=$( /bin/find /etc/unbound/zonefiles -type f -iname "*.rpz" )
>>>> +#   get list of RPZ files
>>>> +rpzFileList=$( find /etc/unbound/zonefiles -type f -iname "*.rpz" )
>>>> 
>>>> -# get basename of those files
>>>> -rpzBaseNames=$( /bin/echo "${rpzFileList}" |
>>>> - /bin/sed 's|/etc/unbound/zonefiles/||g ; s|\.rpz||g ;' )
>>>> +#   get basename of those files
>>>> +rpzBaseNames=$( echo "${rpzFileList}" |
>>>> +    sed 's|/etc/unbound/zonefiles/||g ; s|\.rpz||g ;' )
>>>> 
>>>> -# add to rpzNames
>>>> +#   add to rpzNames
>>>> rpzNames="${rpzNames}"$'\n'"${rpzBaseNames}"
>>>> 
>>>> -# drop duplicate names
>>>> -rpzNames=$( echo "${rpzNames}" | /usr/bin/sort --unique  )
>>>> +#   drop duplicate names
>>>> +rpzNames=$( echo "${rpzNames}" | sort --unique  )
>>>> 
>>>> -# get line count for each RPZ
>>>> -lineCount=$( /bin/echo "${rpzFileList}" | /usr/bin/xargs wc -l )
>>>> +#   get line count for each RPZ
>>>> +lineCount=$( echo "${rpzFileList}" | xargs wc -l )
>>>> 
>>>> -# get comment line count and blank line count for each RPZ
>>>> -commentCount=$( /bin/echo "${rpzFileList}" |
>>>> - /usr/bin/xargs /bin/grep --count -e "^$" -e "^;" )
>>>> +#   get comment line count and blank line count for each RPZ
>>>> +commentCount=$( echo "${rpzFileList}" |
>>>> +    xargs grep --count -e "^$" -e "^;" )
>>>> 
>>>> -# get modified date each RPZ
>>>> -modDateList=$( /bin/echo "${rpzFileList}" | xargs stat -c '%.10y  %n' )
>>>> +#   get modified date each RPZ
>>>> +modDateList=$( echo "${rpzFileList}" | xargs stat -c '%.10y  %n' )
>>>> 
>>>> -ucListAuthZones=$( /usr/sbin/unbound-control list_auth_zones )
>>>> +ucListAuthZones=$( unbound-control list_auth_zones )
>>>> 
>>>> -# get width of RPZ names
>>>> -pWidth=$( /bin/echo "${rpzNames}" | /usr/bin/awk '{ print $1"   " }' | wc -L )
>>>> +#   get width of RPZ names
>>>> +pWidth=$( echo "${rpzNames}" | awk '{ print $1"   " }' | wc -L )
>>>> pFormat="%-${pWidth}s %-8s %-8s %-8s %10s %12s\n"
>>>> 
>>>> -# print title line
>>>> +#   print title line
>>>> printf "${pFormat}" "name" "hits" "active" "lines" "hits/line%" "last update"
>>>> printf -- "--------------"
>>>> 
>>>> @@ -83,60 +82,60 @@ totalLines=0
>>>> totalHits=0
>>>> while read -r theName
>>>> do
>>>> - printf -- "-" # pretend progress bar
>>>> - # get hit count
>>>> - theHits="0"
>>>> - if output=$( /bin/grep "^${theName}\s" <<< "${rpzNameCount}" ) ; then
>>>> - theHits=$( /bin/echo "${output}" |
>>>> - /usr/bin/awk '{ print $2 }' )
>>>> - totalHits=$(( totalHits + theHits ))
>>>> - fi
>>>> -
>>>> - # is this RPZ list active?
>>>> - theActive="disabled"
>>>> - if /bin/grep --quiet "^${theName}\.rpz" <<< "${ucListAuthZones}"
>>>> - then
>>>> - theActive="enabled"
>>>> - fi
>>>> -
>>>> - # get line count then subtract comment count and blank line count
>>>> - #  from total line count
>>>> - theLines="n/a"
>>>> - hitsPerLine="0"
>>>> - if output=$( /bin/grep --fixed-strings "/${theName}.rpz" <<< "${lineCount}" ) ; then
>>>> - theLines=$( /bin/echo "${output}" | /usr/bin/awk '{ print $1 }' )
>>>> - totalLines=$(( totalLines + theLines ))
>>>> -
>>>> - if [[ "${theLines}" -gt 2 ]] ; then
>>>> - hitsPerLine=$(( 100 * theHits / theLines ))
>>>> - fi
>>>> - fi
>>>> -
>>>> - # get modification date
>>>> - theModDate="n/a"
>>>> - if output=$( /bin/grep --fixed-strings "/${theName}.rpz" <<< "${modDateList}" ) ; then
>>>> - theModDate=$( /bin/echo "${output}" | /usr/bin/awk '{ print $1 }' )
>>>> - fi
>>>> -
>>>> - # add to results list
>>>> - theResults+="${theName} ${theHits} ${theActive} ${theLines} ${hitsPerLine} ${theModDate}"$'\n'
>>>> +    printf -- "-"        #   pretend progress bar
>>>> +    #   get hit count
>>>> +    theHits="0"
>>>> +    if output=$( grep "^${theName}\s" <<< "${rpzNameCount}" ) ; then
>>>> +        theHits=$( echo "${output}" |
>>>> +        awk '{ print $2 }' )
>>>> +        totalHits=$(( totalHits + theHits ))
>>>> +    fi
>>>> +
>>>> +    #   is this RPZ list active?
>>>> +    theActive="disabled"
>>>> +    if grep --quiet "^${theName}\.rpz" <<< "${ucListAuthZones}"
>>>> +    then
>>>> +        theActive="enabled"
>>>> +    fi
>>>> +
>>>> +    #   get line count
>>>> +    theLines="n/a"
>>>> +    hitsPerLine="0"
>>>> +    if output=$( grep --fixed-strings "/${theName}.rpz" <<< "${lineCount}" ) ; then
>>>> +        theLines=$( echo "${output}" | awk '{ print $1 }' )
>>>> +        totalLines=$(( totalLines + theLines ))
>>>> +
>>>> +        if [[ "${theLines}" -gt 2 ]] ; then
>>>> +            hitsPerLine=$(( 100 * theHits / theLines ))
>>>> +        fi
>>>> +    fi
>>>> +
>>>> +    #   get modification date
>>>> +    theModDate="n/a"
>>>> +    if output=$( grep --fixed-strings "/${theName}.rpz" <<< "${modDateList}" ) ; then
>>>> +        theModDate=$( echo "${output}" | awk '{ print $1 }' )
>>>> +    fi
>>>> +
>>>> +    #   add to results list
>>>> +    theResults+="${theName} ${theHits} ${theActive} ${theLines} ${hitsPerLine} ${theModDate}"$'\n'
>>>> +    
>>>> done <<< "${rpzNames}"
>>>> 
>>>> case "${sortBy}" in
>>>> - names|name) sortArg=(-k3,3r -k1,1) ;; # sort by "active" then by "name"
>>>> +    names|name) sortArg=(-k3,3r -k1,1)          ;;  #   sort by "active" then by "name"
>>>> 
>>>> - hits|hit) sortArg=(-k3,3r -k2,2nr -k1,1) ;; # sort by "active" then by "hits" then by "name"
>>>> +    hits|hit)   sortArg=(-k3,3r -k2,2nr -k1,1)  ;;  #   sort by "active" then by "hits" then by "name"
>>>> 
>>>> - lines|line) sortArg=(-k3,3r -k4,4nr -k1,1) ;; # sort by "active" then by "lines" then by "name"
>>>> +    lines|line) sortArg=(-k3,3r -k4,4nr -k1,1)  ;;  #   sort by "active" then by "lines" then by "name"
>>>> esac
>>>> 
>>>> printf -- "--------------\n"
>>>> -# remove blank lines, sort, print as columns
>>>> -/bin/echo "${theResults}" |
>>>> - /usr/bin/awk '!/^[[:space:]]*$/' |
>>>> - /usr/bin/sort "${sortArg[@]}"    |
>>>> - /usr/bin/awk --assign=width="${pWidth}" \
>>>> - '{ printf "%-*s %-8s %-8s %-8s %10s %12s\n", width, $1, $2, $3, $4, $5, $6 }'
>>>> +#   remove blank lines, sort, print as columns
>>>> +echo "${theResults}" |
>>>> +    awk '!/^[[:space:]]*$/' |
>>>> +    sort "${sortArg[@]}"    |
>>>> +    awk --assign=width="${pWidth}" \
>>>> +        '{ printf "%-*s %-8s %-8s %-8s %10s %12s\n", width, $1, $2, $3, $4, $5, $6 }'
>>>> 
>>>> printf "${pFormat}" "" "=======" "" "========" "" ""
>>>> printf "${pFormat}" "Totals -->" "${totalHits}" "" "${totalLines}" "" ""
>>>> diff --git a/config/rpz/rpz-sleep b/config/rpz/rpz-sleep
>>>> index eeef1174a..dd3603599 100644
>>>> --- a/config/rpz/rpz-sleep
>>>> +++ b/config/rpz/rpz-sleep
>>>> @@ -19,38 +19,38 @@
>>>> #                                                                             #
>>>> ###############################################################################
>>>> 
>>>> -# v04 on 2024-07-05
>>>> +version="2024-08-16"        # v05
>>>> 
>>>> ###############     Functions     ###############
>>>> 
>>>> -# send message to message log
>>>> +#   send message to message log
>>>> msg_log () {
>>>> - /usr/bin/logger --tag "${tagName}" "$*"
>>>> - if /usr/bin/tty --silent ; then
>>>> - echo "${tagName}:" "$*"
>>>> - fi
>>>> +    logger --tag "${tagName}" "$*"
>>>> +    if tty --silent ; then
>>>> +        echo "${tagName}:" "$*"
>>>> +    fi
>>>> }
>>>> 
>>>> ###############       Main        ###############
>>>> 
>>>> tagName="unbound"
>>>> 
>>>> -sleepTime="${1:-5m}" # default to sleep for 5m (5 minutes)
>>>> +sleepTime="${1:-5m}"            #  default to sleep for 5m (5 minutes)
>>>> 
>>>> -zoneList=$( /usr/sbin/unbound-control list_auth_zones | /usr/bin/awk '{print $1}' )
>>>> +zoneList=$( unbound-control list_auth_zones | awk '{print $1}' )
>>>> 
>>>> for zone in ${zoneList} ; do
>>>> - /usr/bin/printf "disable ${zone}\t"
>>>> - /usr/sbin/unbound-control rpz_disable "${zone}"
>>>> +    printf "disable ${zone}\t"
>>>> +    unbound-control rpz_disable "${zone}"
>>>> done
>>>> 
>>>> msg_log "info: rpz: disabled all zones for ${sleepTime}"
>>>> 
>>>> -/bin/sleep "${sleepTime}"
>>>> +sleep "${sleepTime}"
>>>> 
>>>> for zone in ${zoneList} ; do
>>>> - /usr/bin/printf "enable ${zone}\t"
>>>> - /usr/sbin/unbound-control rpz_enable "${zone}"
>>>> +    printf "enable ${zone}\t"
>>>> +    unbound-control rpz_enable "${zone}"
>>>> done
>>>> 
>>>> msg_log "info: rpz: enabled all zones"
>>>> diff --git a/lfs/rpz b/lfs/rpz
>>>> index 73f6f2b1b..16d1d0803 100644
>>>> --- a/lfs/rpz
>>>> +++ b/lfs/rpz
>>>> @@ -62,25 +62,31 @@ $(TARGET) :
>>>> @$(PREBUILD)
>>>> @rm -rf $(DIR_APP)
>>>> 
>>>> - # install RPZ scripts
>>>> + # install RPZ scripts
>>>> install -v -m 755 \
>>>> $(DIR_CONF)/rpz/{rpz-config,rpz-metrics,rpz-sleep} -t /usr/sbin
>>>> 
>>>> + # Add conf file to /etc directory
>>>> + cp -vf $(DIR_CONF)/rpz/00-rpz.conf /etc/unbound/local.d
>>>> + cp -vf $(DIR_CONF)/rpz/block.rpz.conf /etc/unbound/local.d
>>>> +
>>>> # Install settings folder and two empty files
>>>> mkdir -pv /var/ipfire/dns/rpz
>>>> touch /var/ipfire/dns/rpz/allowlist
>>>> touch /var/ipfire/dns/rpz/blocklist
>>>> 
>>>> - # Add conf file to /etc directory
>>>> - cp -vf $(DIR_CONF)/rpz/00-rpz.conf /etc/unbound/local.d
>>>> -
>>>> - # create zonefiles directory for the RPZ files and add two empty RPZ
>>>> - #  files to avoid a unbound config error
>>>> + # create zonefiles directory for the RPZ files and add two empty RPZ
>>>> + #  files to avoid a unbound config error
>>>> mkdir -pv /etc/unbound/zonefiles
>>>> - chown -v nobody:nobody /etc/unbound/zonefiles
>>>> touch /etc/unbound/zonefiles/allow.rpz
>>>> touch /etc/unbound/zonefiles/block.rpz
>>>> 
>>>> + #  set owner for unbound related files
>>>> + chown -vR nobody:nobody  \
>>>> +  /var/ipfire/dns/rpz    \
>>>> +  /etc/unbound/zonefiles \
>>>> +  /etc/unbound/local.d
>>>> +
>>>> # Install backup definition
>>>> cp -vf $(DIR_CONF)/backup/includes/rpz /var/ipfire/backup/addons/includes/rpz
>>>> 
>>>> diff --git a/src/paks/rpz/uninstall.sh b/src/paks/rpz/uninstall.sh
>>>> index 4fb20e127..51edaa176 100644
>>>> --- a/src/paks/rpz/uninstall.sh
>>>> +++ b/src/paks/rpz/uninstall.sh
>>>> @@ -27,5 +27,9 @@
>>>> make_backup ${NAME}
>>>> remove_files
>>>> 
>>>> +#  delete rpz config files.  Otherwise unbound will throw error:
>>>> +#    "[1723428668] unbound-control[17117:0] error: connect: Connection refused for 127.0.0.1 port 8953"
>>>> +/bin/rm -fv /etc/unbound/local.d/*.rpz.conf
>>>> +
>>>> #  start unbound to load unbound config file
>>>> /etc/init.d/unbound start
>>>> diff --git a/src/paks/rpz/update.sh b/src/paks/rpz/update.sh
>>>> index 938a93a40..fd46a185a 100644
>>>> --- a/src/paks/rpz/update.sh
>>>> +++ b/src/paks/rpz/update.sh
>>>> @@ -20,6 +20,24 @@
>>>> ###############################################################################
>>>> #
>>>> . /opt/pakfire/lib/functions.sh
>>>> +
>>>> +#  from update.sh
>>>> extract_backup_includes
>>>> -./uninstall.sh
>>>> -./install.sh
>>>> +
>>>> +#  stop unbound to delete RPZ conf file
>>>> +/etc/init.d/unbound stop
>>>> +
>>>> +#  from uninstall.sh
>>>> +make_backup ${NAME}
>>>> +remove_files
>>>> +
>>>> +#  delete rpz config files.  Otherwise unbound will throw error:
>>>> +#    "unbound-control[nn:0] error: connect: Connection refused for 127.0.0.1 port 8953"
>>>> +/bin/rm --verbose --force /etc/unbound/local.d/*.rpz.conf
>>>> +
>>>> +#  from install.sh
>>>> +extract_files
>>>> +restore_backup ${NAME}
>>>> +
>>>> +#  restart unbound to load config files
>>>> +/etc/init.d/unbound start
>>>> -- 
>>>> 2.30.2
>>>> 
>>> 
>>> Jon
>>> 
>>> 
>>> -- 
>>> Jon Murphy
>>> jon.murphy@ipfire.org
>>> 
>>> 
>>> 
>>> 
>> 
> 
> Jon
> 
> 
> -- 
> Jon Murphy
> jon.murphy@ipfire.org
> 
> 
> 
>
  
jon Sept. 30, 2024, 6:55 p.m. UTC | #5
> On Sep 30, 2024, at 11:27 AM, Michael Tremer <michael.tremer@ipfire.org> wrote:
> 
> Hello Jon,
> 
> I don’t think that we are at a point where I feel comfortable with approving this. I believe that the fundamental questions that I have raised about this have not been addressed.

I did answer all of your questions on Aug 23 except for the one concerning adding RPZ to a "global DNS system".  If I missed something else I’d be happy to correct.


> 
> As stated before I did not look much at the code and I am not a point where a code review is what I need. I am at a point right now where I want to figure out what this feature is going to be and value is it going to bring our users.

This is what I am trying to do also.  And this is the point of the current release: "does RPZ have value to users?"


> I just once again went through the Hagezi lists to find any sense in them and I must say that this is not really what could replace the URL filter at all. It is just large lists that nobody knows where they are coming from and what they contain. I don’t have any time to invest into researching alternatives.

I understand, that is why I am trying to help.

There are multi lists that contain many items and there are individual lists that contain one category:

>>> • Fake - Protects against internet scams, traps & fakes!
>>> • Pop-Up Ads - Protects against annoying and malicious pop-up ads!
>>> • Threat Intelligence Feeds - Increases security significantly! (Recommended) : Full - Medium - Mini - IPs
>>> • Newly Registered Domains - Favoured by threat actors to launch malicious campaigns! : 14 days - 30 days
>>> • DoH/VPN/TOR/Proxy Bypass - Prevent methods to bypass your DNS! : Full - DoH only - DoH IPs
>>> • Safesearch not supported - Prevent the use of search engines that do not support Safesearch!
>>> • Dynamic DNS - Protects against the malicious use of dynamic DNS services!
>>> • Badware Hoster - Protects against the malicious use of free host services!
>>> • Most Abused TLDs - Protects against known malicious Top Level Domains!
>>> • Anti Piracy - Protects against piracy!
>>> • Gambling - Protects against gambling content! : Full - Medium - Mini
>>> • NSFW (external) - oisd NSFW - Protects against adult content!
>>> • Native Tracker - Broadband tracker of devices, services and operating systems



> 
> And for this reason alone I don’t see why this would be interesting for our users.

There is interest.  Please read the Community post when you have a moment.
https://community.ipfire.org/t/i-created-a-test-version-of-a-rpz-add-on-and-i-am-looking-for-feedback/11934


> 
> It would really help me if you can help me understand this and help answering the questions that I have asked in the past.
> 
> What do you mean by moving *forward*? What are the next steps?


This is what I had answered on Aug 23:

>> What do you want to move forward with? 

> 

> 

> To implement the first phase:  An RPZ add-on that is currently shell based.  

> 

> The release will be similar to the Patch way below but with the fixes you suggested.  Plus a few bug fixes!  I’ll make the current changes and send a new Patch. 

> 

> The main goal of this Phase is to measure user interest in RPZ.  The metrics will be feedback within the Community.

> 

> 

>> What are the next steps?

>> 

> 

> If there is interest from the user Community, then:

> 

> • Phase 2: Input WebGUI (image shown in previous post)

> 

> • Phase 3: RPZ logs / metrics via WebGUI

> 



Does this help answer your questions?

I’d really like to continue with this project.  But it needs your blessing to keep going forward.


Jon



> 
> -Michael
> 
>> On 29 Sep 2024, at 16:53, jon <jon.murphy@ipfire.org> wrote:
>> 
>> To review and approve
>> 
>> 
>> See: https://lists.ipfire.org/hyperkitty/list/development@lists.ipfire.org/message/B4ZN5EDEDKRRP4BOD4M5UCICJGBYUQGN/
>> 
>> 
>> Jon
>> 
>>> On Sep 29, 2024, at 6:31 AM, Michael Tremer <michael.tremer@ipfire.org> wrote:
>>> 
>>> What are you reminding us about?
>>> 
>>>> On 28 Sep 2024, at 20:34, jon <jon.murphy@ipfire.org> wrote:
>>>> 
>>>> Gentle reminder…
>>>> 
>>>> 
>>>>> On Aug 23, 2024, at 4:39 PM, Jon Murphy <jon.murphy@ipfire.org> wrote:
>>>>> 
>>>>> - feature: added "list" action
>>>>> - update: changed "cat << heredocs" to "echo > file"
>>>>> - update: remove path to executables
>>>>> - update: reformatted code and comments (tabs to spaces)
>>>>> - update: reworded some msg_log messages
>>>>> - update: change exit codes from "1" to unique exit code numbers
>>>>> - bug: added check for empty allow/block config file
>>>>> - bug: removed auth_zone_reload (had double reload for allow/block)
>>>>> - bug: change rpz config file to `chown nobody:nobody`
>>>>> - bug: change rpz config file to `chmod 644`
>>>>> 
>>>>> Signed-off-by: Jon Murphy <jon.murphy@ipfire.org>
>>>>> ---
>>>>> config/rootfiles/packages/rpz |   1 +
>>>>> config/rpz/00-rpz.conf        |  22 +--
>>>>> config/rpz/block.rpz.conf     |   7 +
>>>>> config/rpz/rpz-config         | 292 ++++++++++++++++++----------------
>>>>> config/rpz/rpz-metrics        | 163 ++++++++++---------
>>>>> config/rpz/rpz-sleep          |  26 +--
>>>>> lfs/rpz                       |  20 ++-
>>>>> src/paks/rpz/uninstall.sh     |   4 +
>>>>> src/paks/rpz/update.sh        |  22 ++-
>>>>> 9 files changed, 297 insertions(+), 260 deletions(-)
>>>>> create mode 100644 config/rpz/block.rpz.conf
>>>>> 
>>>>> diff --git a/config/rootfiles/packages/rpz b/config/rootfiles/packages/rpz
>>>>> index 183825362..73c3efa06 100644
>>>>> --- a/config/rootfiles/packages/rpz
>>>>> +++ b/config/rootfiles/packages/rpz
>>>>> @@ -1,4 +1,5 @@
>>>>> etc/unbound/local.d/00-rpz.conf
>>>>> +etc/unbound/local.d/block.rpz.conf
>>>>> etc/unbound/zonefiles
>>>>> etc/unbound/zonefiles/allow.rpz
>>>>> etc/unbound/zonefiles/block.rpz
>>>>> diff --git a/config/rpz/00-rpz.conf b/config/rpz/00-rpz.conf
>>>>> index 72c1d12e5..f005a4f2e 100644
>>>>> --- a/config/rpz/00-rpz.conf
>>>>> +++ b/config/rpz/00-rpz.conf
>>>>> @@ -1,18 +1,10 @@
>>>>> server:
>>>>> - module-config: "respip validator iterator"
>>>>> +    module-config: "respip validator iterator"
>>>>> 
>>>>> rpz:
>>>>> -    name: allow.rpz
>>>>> -    zonefile: /etc/unbound/zonefiles/allow.rpz
>>>>> -    rpz-action-override: passthru
>>>>> -    rpz-log: yes
>>>>> -    rpz-log-name: allow
>>>>> -    rpz-signal-nxdomain-ra: yes
>>>>> -
>>>>> -rpz:
>>>>> -    name: block.rpz
>>>>> -    zonefile: /etc/unbound/zonefiles/block.rpz
>>>>> -    rpz-action-override: nxdomain
>>>>> -    rpz-log: yes
>>>>> -    rpz-log-name: block
>>>>> -    rpz-signal-nxdomain-ra: yes
>>>>> +    name:                    allow.rpz
>>>>> +    zonefile:                /etc/unbound/zonefiles/allow.rpz
>>>>> +    rpz-action-override:     passthru
>>>>> +    rpz-log:                 yes
>>>>> +    rpz-log-name:            allow
>>>>> +    rpz-signal-nxdomain-ra:  yes
>>>>> diff --git a/config/rpz/block.rpz.conf b/config/rpz/block.rpz.conf
>>>>> new file mode 100644
>>>>> index 000000000..605684257
>>>>> --- /dev/null
>>>>> +++ b/config/rpz/block.rpz.conf
>>>>> @@ -0,0 +1,7 @@
>>>>> +rpz:
>>>>> +    name:                    block.rpz
>>>>> +    zonefile:                /etc/unbound/zonefiles/block.rpz
>>>>> +    rpz-action-override:     nxdomain
>>>>> +    rpz-log:                 yes
>>>>> +    rpz-log-name:            block
>>>>> +    rpz-signal-nxdomain-ra:  yes
>>>>> diff --git a/config/rpz/rpz-config b/config/rpz/rpz-config
>>>>> index a24a5c132..9278aa004 100644
>>>>> --- a/config/rpz/rpz-config
>>>>> +++ b/config/rpz/rpz-config
>>>>> @@ -19,176 +19,186 @@
>>>>> #                                                                             #
>>>>> ###############################################################################
>>>>> 
>>>>> -# v23 - 2024-07-30
>>>>> +version="2024-08-18" # v28
>>>>> 
>>>>> ###############     Functions     ###############
>>>>> 
>>>>> msg_log () {
>>>>> - /usr/bin/logger --tag "${tagName}" "$*"
>>>>> - if tty --silent ; then
>>>>> - echo "${tagName}:" "$*"
>>>>> - fi
>>>>> +    logger --tag "${tagName}" "$*"
>>>>> +    if tty --silent ; then
>>>>> +        echo "${tagName}:" "$*"
>>>>> +    fi
>>>>> }
>>>>> 
>>>>> check_name () {
>>>>> - local testName="${1}"
>>>>> - # check for a valid name
>>>>> - regex='^[a-zA-Z0-9_]+$'
>>>>> - if [[ ! "${testName}" =~ $regex ]] ; then
>>>>> - msg_log "error: rpz: the NAME is not valid: \"${testName}\". exit."
>>>>> - exit 1
>>>>> - fi
>>>>> +    local testName="${1}"
>>>>> +    #  check for a valid name
>>>>> +    regex='^[a-zA-Z0-9_]+$'
>>>>> +    if [[ ! "${testName}" =~ $regex ]] || [[ "${testName}" == "allow" ]] || [[ "${testName}" == "block" ]]; then
>>>>> +        msg_log "error: rpz: the NAME is not valid: \"${testName}\". exit."
>>>>> +        exit 101
>>>>> +    fi
>>>>> }
>>>>> 
>>>>> check_unbound_conf () {
>>>>> - # check the above config files
>>>>> - msg_log "info: rpz: check for errors with \"unbound-checkconf\""
>>>>> - /usr/sbin/unbound-checkconf
>>>>> - exit_code=$?
>>>>> - if [[ "${exit_code}" -ne 0 ]] ; then
>>>>> - msg_log "error: rpz: unbound-checkconf. exit."
>>>>> - exit "${exit_code}"
>>>>> - fi
>>>>> +    #  check the above config files
>>>>> +    msg_log "info: rpz: check for errors with \"unbound-checkconf\""
>>>>> +    unbound-checkconf
>>>>> +    exit_code=$?
>>>>> +    if [[ "${exit_code}" -ne 0 ]] ; then
>>>>> +        msg_log "error: rpz: unbound-checkconf found invalid configuration."
>>>>> +        msg_log "error: rpz: In the Terminal run the command \
>>>>> +          \"unbound-checkconf\" for more information. exit."
>>>>> +        exit 102
>>>>> +    fi
>>>>> }
>>>>> 
>>>>> make_rpz_file () {
>>>>> - local theType="${1}" # allow or block
>>>>> -
>>>>> - theList="/var/ipfire/dns/rpz/${theType}list" # input custom list of domains
>>>>> - theZoneFile="/etc/unbound/zonefiles/${theType}.rpz" # output file for RPZ
>>>>> -
>>>>> - theAction='.'
>>>>> - if [[ "${theType}" =~ "allow" ]] ; then
>>>>> - theAction='rpz-passthru.'
>>>>> - fi
>>>>> -
>>>>> - # does a list exist?
>>>>> - if [[ -s "${theList}" ]] ; then
>>>>> - # drop any extra "blanks" and add "CNAME <RPZ action>." to each line
>>>>> - actionList=$( /usr/bin/awk '{$1=$1};1' "${theList}" |
>>>>> - /bin/sed "/^[^;].*[[:alnum:]]/ s|$| CNAME ${theAction}|" )
>>>>> -
>>>>> - msg_log "info: rpz: create zonefile for ${theList}"
>>>>> -
>>>>> - /bin/cat <<-EOF > "${theZoneFile}"
>>>>> - ; Name: ${theType} list
>>>>> - ; Last modified: $(date "+%Y-%m-%d at %H.%M.%S %Z")
>>>>> - ;
>>>>> - ; domains with actions list
>>>>> - ;
>>>>> - ${actionList}
>>>>> - EOF
>>>>> -
>>>>> - # reload the zone that was just updated
>>>>> - zoneBase=$( basename "${theZoneFile}" )
>>>>> - /usr/sbin/unbound-control auth_zone_reload -q "${zoneBase}"
>>>>> - fi
>>>>> +    local theType="${1}"    #   allow or block
>>>>> +
>>>>> +    theList="/var/ipfire/dns/rpz/${theType}list"         #  input custom list of domains
>>>>> +    theZoneFile="/etc/unbound/zonefiles/${theType}.rpz"  #  output file for RPZ
>>>>> +
>>>>> +    #  does a list exist?
>>>>> +    if ! [[ -s "${theList}" ]] ; then
>>>>> +        msg_log "error: rpz: the ${theList} is empty. exit."
>>>>> +        exit 103
>>>>> +    fi
>>>>> +
>>>>> +    theAction='.'
>>>>> +    if [[ "${theType}" =~ "allow" ]] ; then
>>>>> +        theAction='rpz-passthru.'
>>>>> +    fi
>>>>> +
>>>>> +    #  drop any extra "blanks" and add "CNAME <RPZ action>." to each line
>>>>> +    actionList=$( awk '{$1=$1};1' "${theList}" |
>>>>> +        sed "/^[^;].*[[:alnum:]]/ s|$|  CNAME   ${theAction}|" )
>>>>> +
>>>>> +    msg_log "info: rpz: create zonefile for ${theList}"
>>>>> +
>>>>> +echo "
>>>>> +; Name:             ${theType} list
>>>>> +; Last modified:    $(date "+%Y-%m-%d at %H.%M.%S %Z")
>>>>> +;
>>>>> +;   domains with actions list
>>>>> +;
>>>>> +${actionList}
>>>>> +" > "${theZoneFile}"
>>>>> +
>>>>> }
>>>>> 
>>>>> ###############       Main        ###############
>>>>> 
>>>>> tagName="unbound"
>>>>> 
>>>>> -theAction="${1}" # input action
>>>>> -theName="${2}" # input RPZ name
>>>>> -theURL="${3}" # input RPZ URL
>>>>> -
>>>>> -check_name "${theName}" # is this a valid name?
>>>>> +theAction="${1}"                    #  input action
>>>>> +theName="${2}"                      #  input RPZ name
>>>>> +theURL="${3}"                       #  input RPZ URL
>>>>> 
>>>>> -rpzConfig="/etc/unbound/local.d/${theName}.rpz.conf" # output zone conf file
>>>>> -rpzFile="/etc/unbound/zonefiles/${theName}.rpz" # output for RPZ file
>>>>> +rpzConfig="/etc/unbound/local.d/${theName}.rpz.conf"    #  output zone conf file
>>>>> +rpzFile="/etc/unbound/zonefiles/${theName}.rpz"         #  output for RPZ file
>>>>> 
>>>>> case "${theAction}" in
>>>>> 
>>>>> - # add new rpz list
>>>>> - add )
>>>>> - # does this config already exist?  If yes, then exit
>>>>> - if [[ -f "${rpzConfig}" ]] ; then
>>>>> - msg_log "info: rpz: ${rpzConfig} already exists. exit"
>>>>> - exit 1
>>>>> - fi
>>>>> -
>>>>> - # is this a valid URL?
>>>>> - regex='^https://[-[:alnum:]\+&@#/%?=~_|!:,.;]*[-[:alnum:]\+&@#/%=~_|]'
>>>>> - if ! [[ "${theURL}" =~ $regex ]] ; then
>>>>> - msg_log "error: rpz: the URL is not valid: \"${theURL}\". exit."
>>>>> - exit 1
>>>>> - fi
>>>>> -
>>>>> - # create the zone config file
>>>>> - msg_log "info: rpz: add config file \"${theName}.rpz.conf\""
>>>>> - cat <<-EOF > "${rpzConfig}"
>>>>> - rpz:
>>>>> - name: ${theName}.rpz
>>>>> - zonefile: /etc/unbound/zonefiles/${theName}.rpz
>>>>> - url:  ${theURL}
>>>>> - rpz-action-override: nxdomain
>>>>> - rpz-log: yes
>>>>> - rpz-log-name: ${theName}
>>>>> - rpz-signal-nxdomain-ra: yes
>>>>> - EOF
>>>>> -
>>>>> - # set-up zone file
>>>>> - /usr/bin/touch "${rpzFile}"
>>>>> - # unbound requires these settings for rpz files
>>>>> - /bin/chown nobody:nobody "${rpzFile}"
>>>>> - /bin/chmod 644 "${rpzFile}"
>>>>> - ;;
>>>>> -
>>>>> - # trash config file & rpz file
>>>>> - remove )
>>>>> - if ! [[ -f "${rpzConfig}" ]] ; then
>>>>> - msg_log "info: rpz: ${rpzConfig} does not exist. exit"
>>>>> - exit 1
>>>>> - fi
>>>>> -
>>>>> - msg_log "info: rpz: remove config file & rpz file \"${theName}\""
>>>>> - /bin/rm "${rpzConfig}"
>>>>> - /bin/rm "${rpzFile}"
>>>>> -
>>>>> - check_unbound_conf
>>>>> - ;;
>>>>> -
>>>>> - # make a new allow or block rpz file
>>>>> - make )
>>>>> - case "${theName}" in
>>>>> - allow )
>>>>> - make_rpz_file allow
>>>>> - ;;
>>>>> -
>>>>> - block )
>>>>> - make_rpz_file block
>>>>> - ;;
>>>>> -
>>>>> - allowblock )
>>>>> - make_rpz_file allow
>>>>> - make_rpz_file block
>>>>> - ;;
>>>>> -
>>>>> - * )
>>>>> - msg_log "error: rpz: the NAME is not valid: \"${theName}\". exit."
>>>>> - exit 1
>>>>> - ;;
>>>>> - esac
>>>>> -
>>>>> - check_unbound_conf
>>>>> - ;;
>>>>> -
>>>>> - *)
>>>>> - msg_log "error: rpz: missing or incorrect parameter"
>>>>> - /usr/bin/printf "Usage:  rpzConfig.sh <ACTION> <NAME> <URL>\n"
>>>>> - exit 1
>>>>> - ;;
>>>>> +    #  add new rpz list
>>>>> +    add )
>>>>> + check_name "${theName}"             #  is this a valid name?
>>>>> +        #  does this config already exist?  If yes, then exit
>>>>> +        if [[ -f "${rpzConfig}" ]] ; then
>>>>> +            msg_log "error: rpz: duplicate - ${rpzConfig} already exists. exit"
>>>>> +            exit 104
>>>>> +        fi
>>>>> +
>>>>> +        #  is this a valid URL?
>>>>> +        regex='^https://[-[:alnum:]\+&@#/%?=~_|!:,.;]*[-[:alnum:]\+&@#/%=~_|]'
>>>>> +        if ! [[ "${theURL}" =~ $regex ]] ; then
>>>>> +            msg_log "error: rpz: the URL is not valid: \"${theURL}\". exit."
>>>>> +            exit 105
>>>>> +        fi
>>>>> +
>>>>> +        #  create the zone config file
>>>>> +        msg_log "info: rpz: add config file \"${theName}.rpz.conf\""
>>>>> +echo "rpz:
>>>>> +    name:                   ${theName}.rpz
>>>>> +    zonefile:               ${rpzFile}
>>>>> +    url:                    ${theURL}
>>>>> +    rpz-action-override:    nxdomain
>>>>> +    rpz-log:                yes
>>>>> +    rpz-log-name:           ${theName}
>>>>> +    rpz-signal-nxdomain-ra: yes
>>>>> +" > "${rpzConfig}"
>>>>> +        
>>>>> +        #  set-up zonefile
>>>>> +        #    create an empty rpz file
>>>>> +        touch "${rpzFile}"
>>>>> +        #  unbound requires these settings for rpz files
>>>>> +        chown nobody:nobody "${rpzFile}" "${rpzConfig}"
>>>>> +        chmod 644 "${rpzFile}" "${rpzConfig}"
>>>>> +        ;;
>>>>> +
>>>>> +    #  trash config file & rpz file
>>>>> +    remove )
>>>>> + check_name "${theName}"             #  is this a valid name?
>>>>> +        if ! [[ -f "${rpzConfig}" ]] ; then
>>>>> +            msg_log "error: rpz: cannot remove ${rpzConfig}, does not exist. exit"
>>>>> +            exit 106
>>>>> +        fi
>>>>> +
>>>>> +        msg_log "info: rpz: remove config file & rpz file \"${theName}\""
>>>>> +        rm "${rpzConfig}"
>>>>> +        rm "${rpzFile}"
>>>>> +
>>>>> +        check_unbound_conf
>>>>> +        ;;
>>>>> +
>>>>> +    #  make a new allow or block rpz file
>>>>> +    make )
>>>>> +        case "${theName}" in
>>>>> +            allow )
>>>>> +            make_rpz_file allow
>>>>> +            ;;
>>>>> +
>>>>> +        block )
>>>>> +            make_rpz_file block
>>>>> +            ;;
>>>>> +
>>>>> +        allowblock )
>>>>> +            make_rpz_file allow
>>>>> +            make_rpz_file block
>>>>> +            ;;
>>>>> +
>>>>> +        * )
>>>>> +            msg_log \
>>>>> +              "error: rpz: the NAME \"${theName}\" is not valid - \"allow\" or \"block\" only.  exit."
>>>>> +            exit 107
>>>>> +            ;;
>>>>> +        esac
>>>>> +
>>>>> +        check_unbound_conf
>>>>> +        ;;
>>>>> +        
>>>>> +    list)
>>>>> +        awk -F' ' '/^name:|\sname:/{ gsub(/.rpz/, "" ) ; printf $2} /\surl:|^url:/{print "="$2}' \
>>>>> +          /etc/unbound/local.d/*.rpz.conf
>>>>> +        exit
>>>>> +        ;;    
>>>>> +        
>>>>> +    *)
>>>>> +        msg_log "error: rpz: missing or incorrect parameter"
>>>>> +        printf "Usage:  rpzConfig <ACTION> <NAME> <URL>\n"
>>>>> +        printf "Version:  ${version}\n"
>>>>> +        exit 108
>>>>> +        ;;
>>>>> 
>>>>> esac
>>>>> 
>>>>> -# reload due to the changes
>>>>> +#  reload due to the changes
>>>>> msg_log  "rpz: running \"unbound-control reload\""
>>>>> -/usr/sbin/unbound-control reload
>>>>> +unbound-control reload
>>>>> exit_code=$?
>>>>> if [[ "${exit_code}" -ne 0 ]] ; then
>>>>> - msg_log "error: rpz: unbound-control \"${theName}\". exit."
>>>>> - exit "${exit_code}"
>>>>> +    msg_log "error: rpz: unbound-control \"${theName}\". exit."
>>>>> +    exit 109
>>>>> fi
>>>>> 
>>>>> exit
>>>>> diff --git a/config/rpz/rpz-metrics b/config/rpz/rpz-metrics
>>>>> index 4d932726e..d154f70b9 100644
>>>>> --- a/config/rpz/rpz-metrics
>>>>> +++ b/config/rpz/rpz-metrics
>>>>> @@ -19,62 +19,61 @@
>>>>> #                                                                             #
>>>>> ###############################################################################
>>>>> 
>>>>> -# v19 on 2024-07-30
>>>>> +version="2024-08-16" # v20
>>>>> 
>>>>> ###############       Main        ###############
>>>>> 
>>>>> -weeks="${1:-2}" # default to two message logs
>>>>> -sortBy="${2:-name}"   # by name or by hits
>>>>> +weeks="${1:-2}"         #   default to two message logs
>>>>> +sortBy="${2:-name}"     #   by name or by hits
>>>>> 
>>>>> -# get the list of message logs for N weeks
>>>>> -messageLogs=$( find /var/log/messages* -type f |
>>>>> - /usr/bin/sort --version-sort |
>>>>> - head -"${weeks}" )
>>>>> +#   get the list of message logs for N weeks
>>>>> +messageLogs=$( find /var/log/messages* -type f | sort --version-sort |
>>>>> +    head -"${weeks}" )
>>>>> 
>>>>> -# get the list of RPZ names & counts from the message log(s)
>>>>> +#   get the list of RPZ names & counts from the message log(s)
>>>>> rpzNameCount=$( for logf in ${messageLogs} ; do
>>>>> - /usr/bin/zgrep --text --extended-regexp 'info: rpz: applied.* A IN$' "${logf}" |
>>>>> - /usr/bin/awk '$10 ~ /\[\w*]/ { print $10 }' ;
>>>>> - done | /usr/bin/sort | /usr/bin/uniq --count )
>>>>> +    zgrep --text --extended-regexp 'info: rpz: applied.* A IN$' "${logf}" |
>>>>> +      awk '$10 ~ /\[\w*]/ { print $10 }' ;
>>>>> +    done | sort | uniq --count )
>>>>> 
>>>>> -# flip results and remove brackets `[` and `]`
>>>>> -rpzNameCount=$( /bin/echo "${rpzNameCount}" |
>>>>> - /usr/bin/awk '{ print $2, $1 }' |
>>>>> - /bin/sed --regexp-extended 's|^\[(.*)\]|\1|' )
>>>>> +#   flip results and remove brackets `[` and `]`
>>>>> +rpzNameCount=$( echo "${rpzNameCount}" |
>>>>> +    awk '{ print $2, $1 }' |
>>>>> +      sed --regexp-extended 's|^\[(.*)\]|\1|' )
>>>>> 
>>>>> -# grab only names
>>>>> -rpzNames=$( /bin/echo "${rpzNameCount}" | /usr/bin/awk '{ print $1 }' )
>>>>> +#   grab only names
>>>>> +rpzNames=$( echo "${rpzNameCount}" | awk '{ print $1 }' )
>>>>> 
>>>>> -# get list of RPZ files
>>>>> -rpzFileList=$( /bin/find /etc/unbound/zonefiles -type f -iname "*.rpz" )
>>>>> +#   get list of RPZ files
>>>>> +rpzFileList=$( find /etc/unbound/zonefiles -type f -iname "*.rpz" )
>>>>> 
>>>>> -# get basename of those files
>>>>> -rpzBaseNames=$( /bin/echo "${rpzFileList}" |
>>>>> - /bin/sed 's|/etc/unbound/zonefiles/||g ; s|\.rpz||g ;' )
>>>>> +#   get basename of those files
>>>>> +rpzBaseNames=$( echo "${rpzFileList}" |
>>>>> +    sed 's|/etc/unbound/zonefiles/||g ; s|\.rpz||g ;' )
>>>>> 
>>>>> -# add to rpzNames
>>>>> +#   add to rpzNames
>>>>> rpzNames="${rpzNames}"$'\n'"${rpzBaseNames}"
>>>>> 
>>>>> -# drop duplicate names
>>>>> -rpzNames=$( echo "${rpzNames}" | /usr/bin/sort --unique  )
>>>>> +#   drop duplicate names
>>>>> +rpzNames=$( echo "${rpzNames}" | sort --unique  )
>>>>> 
>>>>> -# get line count for each RPZ
>>>>> -lineCount=$( /bin/echo "${rpzFileList}" | /usr/bin/xargs wc -l )
>>>>> +#   get line count for each RPZ
>>>>> +lineCount=$( echo "${rpzFileList}" | xargs wc -l )
>>>>> 
>>>>> -# get comment line count and blank line count for each RPZ
>>>>> -commentCount=$( /bin/echo "${rpzFileList}" |
>>>>> - /usr/bin/xargs /bin/grep --count -e "^$" -e "^;" )
>>>>> +#   get comment line count and blank line count for each RPZ
>>>>> +commentCount=$( echo "${rpzFileList}" |
>>>>> +    xargs grep --count -e "^$" -e "^;" )
>>>>> 
>>>>> -# get modified date each RPZ
>>>>> -modDateList=$( /bin/echo "${rpzFileList}" | xargs stat -c '%.10y  %n' )
>>>>> +#   get modified date each RPZ
>>>>> +modDateList=$( echo "${rpzFileList}" | xargs stat -c '%.10y  %n' )
>>>>> 
>>>>> -ucListAuthZones=$( /usr/sbin/unbound-control list_auth_zones )
>>>>> +ucListAuthZones=$( unbound-control list_auth_zones )
>>>>> 
>>>>> -# get width of RPZ names
>>>>> -pWidth=$( /bin/echo "${rpzNames}" | /usr/bin/awk '{ print $1"   " }' | wc -L )
>>>>> +#   get width of RPZ names
>>>>> +pWidth=$( echo "${rpzNames}" | awk '{ print $1"   " }' | wc -L )
>>>>> pFormat="%-${pWidth}s %-8s %-8s %-8s %10s %12s\n"
>>>>> 
>>>>> -# print title line
>>>>> +#   print title line
>>>>> printf "${pFormat}" "name" "hits" "active" "lines" "hits/line%" "last update"
>>>>> printf -- "--------------"
>>>>> 
>>>>> @@ -83,60 +82,60 @@ totalLines=0
>>>>> totalHits=0
>>>>> while read -r theName
>>>>> do
>>>>> - printf -- "-" # pretend progress bar
>>>>> - # get hit count
>>>>> - theHits="0"
>>>>> - if output=$( /bin/grep "^${theName}\s" <<< "${rpzNameCount}" ) ; then
>>>>> - theHits=$( /bin/echo "${output}" |
>>>>> - /usr/bin/awk '{ print $2 }' )
>>>>> - totalHits=$(( totalHits + theHits ))
>>>>> - fi
>>>>> -
>>>>> - # is this RPZ list active?
>>>>> - theActive="disabled"
>>>>> - if /bin/grep --quiet "^${theName}\.rpz" <<< "${ucListAuthZones}"
>>>>> - then
>>>>> - theActive="enabled"
>>>>> - fi
>>>>> -
>>>>> - # get line count then subtract comment count and blank line count
>>>>> - #  from total line count
>>>>> - theLines="n/a"
>>>>> - hitsPerLine="0"
>>>>> - if output=$( /bin/grep --fixed-strings "/${theName}.rpz" <<< "${lineCount}" ) ; then
>>>>> - theLines=$( /bin/echo "${output}" | /usr/bin/awk '{ print $1 }' )
>>>>> - totalLines=$(( totalLines + theLines ))
>>>>> -
>>>>> - if [[ "${theLines}" -gt 2 ]] ; then
>>>>> - hitsPerLine=$(( 100 * theHits / theLines ))
>>>>> - fi
>>>>> - fi
>>>>> -
>>>>> - # get modification date
>>>>> - theModDate="n/a"
>>>>> - if output=$( /bin/grep --fixed-strings "/${theName}.rpz" <<< "${modDateList}" ) ; then
>>>>> - theModDate=$( /bin/echo "${output}" | /usr/bin/awk '{ print $1 }' )
>>>>> - fi
>>>>> -
>>>>> - # add to results list
>>>>> - theResults+="${theName} ${theHits} ${theActive} ${theLines} ${hitsPerLine} ${theModDate}"$'\n'
>>>>> +    printf -- "-"        #   pretend progress bar
>>>>> +    #   get hit count
>>>>> +    theHits="0"
>>>>> +    if output=$( grep "^${theName}\s" <<< "${rpzNameCount}" ) ; then
>>>>> +        theHits=$( echo "${output}" |
>>>>> +        awk '{ print $2 }' )
>>>>> +        totalHits=$(( totalHits + theHits ))
>>>>> +    fi
>>>>> +
>>>>> +    #   is this RPZ list active?
>>>>> +    theActive="disabled"
>>>>> +    if grep --quiet "^${theName}\.rpz" <<< "${ucListAuthZones}"
>>>>> +    then
>>>>> +        theActive="enabled"
>>>>> +    fi
>>>>> +
>>>>> +    #   get line count
>>>>> +    theLines="n/a"
>>>>> +    hitsPerLine="0"
>>>>> +    if output=$( grep --fixed-strings "/${theName}.rpz" <<< "${lineCount}" ) ; then
>>>>> +        theLines=$( echo "${output}" | awk '{ print $1 }' )
>>>>> +        totalLines=$(( totalLines + theLines ))
>>>>> +
>>>>> +        if [[ "${theLines}" -gt 2 ]] ; then
>>>>> +            hitsPerLine=$(( 100 * theHits / theLines ))
>>>>> +        fi
>>>>> +    fi
>>>>> +
>>>>> +    #   get modification date
>>>>> +    theModDate="n/a"
>>>>> +    if output=$( grep --fixed-strings "/${theName}.rpz" <<< "${modDateList}" ) ; then
>>>>> +        theModDate=$( echo "${output}" | awk '{ print $1 }' )
>>>>> +    fi
>>>>> +
>>>>> +    #   add to results list
>>>>> +    theResults+="${theName} ${theHits} ${theActive} ${theLines} ${hitsPerLine} ${theModDate}"$'\n'
>>>>> +    
>>>>> done <<< "${rpzNames}"
>>>>> 
>>>>> case "${sortBy}" in
>>>>> - names|name) sortArg=(-k3,3r -k1,1) ;; # sort by "active" then by "name"
>>>>> +    names|name) sortArg=(-k3,3r -k1,1)          ;;  #   sort by "active" then by "name"
>>>>> 
>>>>> - hits|hit) sortArg=(-k3,3r -k2,2nr -k1,1) ;; # sort by "active" then by "hits" then by "name"
>>>>> +    hits|hit)   sortArg=(-k3,3r -k2,2nr -k1,1)  ;;  #   sort by "active" then by "hits" then by "name"
>>>>> 
>>>>> - lines|line) sortArg=(-k3,3r -k4,4nr -k1,1) ;; # sort by "active" then by "lines" then by "name"
>>>>> +    lines|line) sortArg=(-k3,3r -k4,4nr -k1,1)  ;;  #   sort by "active" then by "lines" then by "name"
>>>>> esac
>>>>> 
>>>>> printf -- "--------------\n"
>>>>> -# remove blank lines, sort, print as columns
>>>>> -/bin/echo "${theResults}" |
>>>>> - /usr/bin/awk '!/^[[:space:]]*$/' |
>>>>> - /usr/bin/sort "${sortArg[@]}"    |
>>>>> - /usr/bin/awk --assign=width="${pWidth}" \
>>>>> - '{ printf "%-*s %-8s %-8s %-8s %10s %12s\n", width, $1, $2, $3, $4, $5, $6 }'
>>>>> +#   remove blank lines, sort, print as columns
>>>>> +echo "${theResults}" |
>>>>> +    awk '!/^[[:space:]]*$/' |
>>>>> +    sort "${sortArg[@]}"    |
>>>>> +    awk --assign=width="${pWidth}" \
>>>>> +        '{ printf "%-*s %-8s %-8s %-8s %10s %12s\n", width, $1, $2, $3, $4, $5, $6 }'
>>>>> 
>>>>> printf "${pFormat}" "" "=======" "" "========" "" ""
>>>>> printf "${pFormat}" "Totals -->" "${totalHits}" "" "${totalLines}" "" ""
>>>>> diff --git a/config/rpz/rpz-sleep b/config/rpz/rpz-sleep
>>>>> index eeef1174a..dd3603599 100644
>>>>> --- a/config/rpz/rpz-sleep
>>>>> +++ b/config/rpz/rpz-sleep
>>>>> @@ -19,38 +19,38 @@
>>>>> #                                                                             #
>>>>> ###############################################################################
>>>>> 
>>>>> -# v04 on 2024-07-05
>>>>> +version="2024-08-16"        # v05
>>>>> 
>>>>> ###############     Functions     ###############
>>>>> 
>>>>> -# send message to message log
>>>>> +#   send message to message log
>>>>> msg_log () {
>>>>> - /usr/bin/logger --tag "${tagName}" "$*"
>>>>> - if /usr/bin/tty --silent ; then
>>>>> - echo "${tagName}:" "$*"
>>>>> - fi
>>>>> +    logger --tag "${tagName}" "$*"
>>>>> +    if tty --silent ; then
>>>>> +        echo "${tagName}:" "$*"
>>>>> +    fi
>>>>> }
>>>>> 
>>>>> ###############       Main        ###############
>>>>> 
>>>>> tagName="unbound"
>>>>> 
>>>>> -sleepTime="${1:-5m}" # default to sleep for 5m (5 minutes)
>>>>> +sleepTime="${1:-5m}"            #  default to sleep for 5m (5 minutes)
>>>>> 
>>>>> -zoneList=$( /usr/sbin/unbound-control list_auth_zones | /usr/bin/awk '{print $1}' )
>>>>> +zoneList=$( unbound-control list_auth_zones | awk '{print $1}' )
>>>>> 
>>>>> for zone in ${zoneList} ; do
>>>>> - /usr/bin/printf "disable ${zone}\t"
>>>>> - /usr/sbin/unbound-control rpz_disable "${zone}"
>>>>> +    printf "disable ${zone}\t"
>>>>> +    unbound-control rpz_disable "${zone}"
>>>>> done
>>>>> 
>>>>> msg_log "info: rpz: disabled all zones for ${sleepTime}"
>>>>> 
>>>>> -/bin/sleep "${sleepTime}"
>>>>> +sleep "${sleepTime}"
>>>>> 
>>>>> for zone in ${zoneList} ; do
>>>>> - /usr/bin/printf "enable ${zone}\t"
>>>>> - /usr/sbin/unbound-control rpz_enable "${zone}"
>>>>> +    printf "enable ${zone}\t"
>>>>> +    unbound-control rpz_enable "${zone}"
>>>>> done
>>>>> 
>>>>> msg_log "info: rpz: enabled all zones"
>>>>> diff --git a/lfs/rpz b/lfs/rpz
>>>>> index 73f6f2b1b..16d1d0803 100644
>>>>> --- a/lfs/rpz
>>>>> +++ b/lfs/rpz
>>>>> @@ -62,25 +62,31 @@ $(TARGET) :
>>>>> @$(PREBUILD)
>>>>> @rm -rf $(DIR_APP)
>>>>> 
>>>>> - # install RPZ scripts
>>>>> + # install RPZ scripts
>>>>> install -v -m 755 \
>>>>> $(DIR_CONF)/rpz/{rpz-config,rpz-metrics,rpz-sleep} -t /usr/sbin
>>>>> 
>>>>> + # Add conf file to /etc directory
>>>>> + cp -vf $(DIR_CONF)/rpz/00-rpz.conf /etc/unbound/local.d
>>>>> + cp -vf $(DIR_CONF)/rpz/block.rpz.conf /etc/unbound/local.d
>>>>> +
>>>>> # Install settings folder and two empty files
>>>>> mkdir -pv /var/ipfire/dns/rpz
>>>>> touch /var/ipfire/dns/rpz/allowlist
>>>>> touch /var/ipfire/dns/rpz/blocklist
>>>>> 
>>>>> - # Add conf file to /etc directory
>>>>> - cp -vf $(DIR_CONF)/rpz/00-rpz.conf /etc/unbound/local.d
>>>>> -
>>>>> - # create zonefiles directory for the RPZ files and add two empty RPZ
>>>>> - #  files to avoid a unbound config error
>>>>> + # create zonefiles directory for the RPZ files and add two empty RPZ
>>>>> + #  files to avoid a unbound config error
>>>>> mkdir -pv /etc/unbound/zonefiles
>>>>> - chown -v nobody:nobody /etc/unbound/zonefiles
>>>>> touch /etc/unbound/zonefiles/allow.rpz
>>>>> touch /etc/unbound/zonefiles/block.rpz
>>>>> 
>>>>> + #  set owner for unbound related files
>>>>> + chown -vR nobody:nobody  \
>>>>> +  /var/ipfire/dns/rpz    \
>>>>> +  /etc/unbound/zonefiles \
>>>>> +  /etc/unbound/local.d
>>>>> +
>>>>> # Install backup definition
>>>>> cp -vf $(DIR_CONF)/backup/includes/rpz /var/ipfire/backup/addons/includes/rpz
>>>>> 
>>>>> diff --git a/src/paks/rpz/uninstall.sh b/src/paks/rpz/uninstall.sh
>>>>> index 4fb20e127..51edaa176 100644
>>>>> --- a/src/paks/rpz/uninstall.sh
>>>>> +++ b/src/paks/rpz/uninstall.sh
>>>>> @@ -27,5 +27,9 @@
>>>>> make_backup ${NAME}
>>>>> remove_files
>>>>> 
>>>>> +#  delete rpz config files.  Otherwise unbound will throw error:
>>>>> +#    "[1723428668] unbound-control[17117:0] error: connect: Connection refused for 127.0.0.1 port 8953"
>>>>> +/bin/rm -fv /etc/unbound/local.d/*.rpz.conf
>>>>> +
>>>>> #  start unbound to load unbound config file
>>>>> /etc/init.d/unbound start
>>>>> diff --git a/src/paks/rpz/update.sh b/src/paks/rpz/update.sh
>>>>> index 938a93a40..fd46a185a 100644
>>>>> --- a/src/paks/rpz/update.sh
>>>>> +++ b/src/paks/rpz/update.sh
>>>>> @@ -20,6 +20,24 @@
>>>>> ###############################################################################
>>>>> #
>>>>> . /opt/pakfire/lib/functions.sh
>>>>> +
>>>>> +#  from update.sh
>>>>> extract_backup_includes
>>>>> -./uninstall.sh
>>>>> -./install.sh
>>>>> +
>>>>> +#  stop unbound to delete RPZ conf file
>>>>> +/etc/init.d/unbound stop
>>>>> +
>>>>> +#  from uninstall.sh
>>>>> +make_backup ${NAME}
>>>>> +remove_files
>>>>> +
>>>>> +#  delete rpz config files.  Otherwise unbound will throw error:
>>>>> +#    "unbound-control[nn:0] error: connect: Connection refused for 127.0.0.1 port 8953"
>>>>> +/bin/rm --verbose --force /etc/unbound/local.d/*.rpz.conf
>>>>> +
>>>>> +#  from install.sh
>>>>> +extract_files
>>>>> +restore_backup ${NAME}
>>>>> +
>>>>> +#  restart unbound to load config files
>>>>> +/etc/init.d/unbound start
>>>>> -- 
>>>>> 2.30.2
>>>>> 
>>>> 
>>>> Jon
>>>> 
>>>> 
>>>> -- 
>>>> Jon Murphy
>>>> jon.murphy@ipfire.org
>>>> 
>>>> 
>>>> 
>>>> 
>>> 
>> 
>> Jon
>> 
>> 
>> -- 
>> Jon Murphy
>> jon.murphy@ipfire.org
>> 
>> 
>> 
>> 
> 

Jon
  

Patch

diff --git a/config/rootfiles/packages/rpz b/config/rootfiles/packages/rpz
index 183825362..73c3efa06 100644
--- a/config/rootfiles/packages/rpz
+++ b/config/rootfiles/packages/rpz
@@ -1,4 +1,5 @@ 
 etc/unbound/local.d/00-rpz.conf
+etc/unbound/local.d/block.rpz.conf
 etc/unbound/zonefiles
 etc/unbound/zonefiles/allow.rpz
 etc/unbound/zonefiles/block.rpz
diff --git a/config/rpz/00-rpz.conf b/config/rpz/00-rpz.conf
index 72c1d12e5..f005a4f2e 100644
--- a/config/rpz/00-rpz.conf
+++ b/config/rpz/00-rpz.conf
@@ -1,18 +1,10 @@ 
 server:
-	module-config: "respip validator iterator"
+    module-config: "respip validator iterator"
 
 rpz:
-    name:					allow.rpz
-    zonefile:				/etc/unbound/zonefiles/allow.rpz
-    rpz-action-override:	passthru
-    rpz-log:				yes
-    rpz-log-name:			allow
-    rpz-signal-nxdomain-ra:	yes
-
-rpz:
-    name:					block.rpz
-    zonefile:				/etc/unbound/zonefiles/block.rpz
-    rpz-action-override:	nxdomain
-    rpz-log:				yes
-    rpz-log-name:			block
-    rpz-signal-nxdomain-ra:	yes
+    name:                    allow.rpz
+    zonefile:                /etc/unbound/zonefiles/allow.rpz
+    rpz-action-override:     passthru
+    rpz-log:                 yes
+    rpz-log-name:            allow
+    rpz-signal-nxdomain-ra:  yes
diff --git a/config/rpz/block.rpz.conf b/config/rpz/block.rpz.conf
new file mode 100644
index 000000000..605684257
--- /dev/null
+++ b/config/rpz/block.rpz.conf
@@ -0,0 +1,7 @@ 
+rpz:
+    name:                    block.rpz
+    zonefile:                /etc/unbound/zonefiles/block.rpz
+    rpz-action-override:     nxdomain
+    rpz-log:                 yes
+    rpz-log-name:            block
+    rpz-signal-nxdomain-ra:  yes
diff --git a/config/rpz/rpz-config b/config/rpz/rpz-config
index a24a5c132..9278aa004 100644
--- a/config/rpz/rpz-config
+++ b/config/rpz/rpz-config
@@ -19,176 +19,186 @@ 
 #                                                                             #
 ###############################################################################
 
-#	v23 - 2024-07-30
+version="2024-08-18"	# v28
 
 ###############     Functions     ###############
 
 msg_log () {
-	/usr/bin/logger --tag "${tagName}" "$*"
-	if tty --silent ; then
-		echo "${tagName}:" "$*"
-	fi
+    logger --tag "${tagName}" "$*"
+    if tty --silent ; then
+        echo "${tagName}:" "$*"
+    fi
 }
 
 check_name () {
-	local testName="${1}"
-	#	check for a valid name
-	regex='^[a-zA-Z0-9_]+$'
-	if [[ ! "${testName}" =~ $regex ]] ; then
-		msg_log "error: rpz: the NAME is not valid: \"${testName}\". exit."
-		exit 1
-	fi
+    local testName="${1}"
+    #  check for a valid name
+    regex='^[a-zA-Z0-9_]+$'
+    if [[ ! "${testName}" =~ $regex ]] || [[ "${testName}" == "allow" ]] || [[ "${testName}" == "block" ]]; then
+        msg_log "error: rpz: the NAME is not valid: \"${testName}\". exit."
+        exit 101
+    fi
 }
 
 check_unbound_conf () {
-	#	check the above config files
-	msg_log "info: rpz: check for errors with \"unbound-checkconf\""
-	/usr/sbin/unbound-checkconf
-	exit_code=$?
-	if [[ "${exit_code}" -ne 0 ]] ; then
-		msg_log "error: rpz: unbound-checkconf. exit."
-		exit "${exit_code}"
-	fi
+    #  check the above config files
+    msg_log "info: rpz: check for errors with \"unbound-checkconf\""
+    unbound-checkconf
+    exit_code=$?
+    if [[ "${exit_code}" -ne 0 ]] ; then
+        msg_log "error: rpz: unbound-checkconf found invalid configuration."
+        msg_log "error: rpz: In the Terminal run the command \
+          \"unbound-checkconf\" for more information. exit."
+        exit 102
+    fi
 }
 
 make_rpz_file () {
-	local theType="${1}"	#	allow or block
-
-	theList="/var/ipfire/dns/rpz/${theType}list"			#	input custom list of domains
-	theZoneFile="/etc/unbound/zonefiles/${theType}.rpz"		#	output file for RPZ
-
-	theAction='.'
-	if [[ "${theType}" =~ "allow" ]] ; then
-		theAction='rpz-passthru.'
-	fi
-
-	#	does a list exist?
-	if [[ -s "${theList}" ]] ; then
-		#	drop any extra "blanks" and add "CNAME <RPZ action>." to each line
-		actionList=$( /usr/bin/awk '{$1=$1};1' "${theList}" |
-			/bin/sed "/^[^;].*[[:alnum:]]/ s|$|	CNAME	${theAction}|" )
-
-		msg_log "info: rpz: create zonefile for ${theList}"
-
-		/bin/cat <<-EOF > "${theZoneFile}"
-		; Name:				${theType} list
-		; Last modified:	$(date "+%Y-%m-%d at %H.%M.%S %Z")
-		;
-		;	domains with actions list
-		;
-		${actionList}
-		EOF
-
-		#	reload the zone that was just updated
-		zoneBase=$( basename "${theZoneFile}" )
-		/usr/sbin/unbound-control auth_zone_reload -q "${zoneBase}"
-	fi
+    local theType="${1}"    #   allow or block
+
+    theList="/var/ipfire/dns/rpz/${theType}list"         #  input custom list of domains
+    theZoneFile="/etc/unbound/zonefiles/${theType}.rpz"  #  output file for RPZ
+
+    #  does a list exist?
+    if ! [[ -s "${theList}" ]] ; then
+        msg_log "error: rpz: the ${theList} is empty. exit."
+        exit 103
+    fi
+
+    theAction='.'
+    if [[ "${theType}" =~ "allow" ]] ; then
+        theAction='rpz-passthru.'
+    fi
+
+    #  drop any extra "blanks" and add "CNAME <RPZ action>." to each line
+    actionList=$( awk '{$1=$1};1' "${theList}" |
+        sed "/^[^;].*[[:alnum:]]/ s|$|  CNAME   ${theAction}|" )
+
+    msg_log "info: rpz: create zonefile for ${theList}"
+
+echo "
+; Name:             ${theType} list
+; Last modified:    $(date "+%Y-%m-%d at %H.%M.%S %Z")
+;
+;   domains with actions list
+;
+${actionList}
+" > "${theZoneFile}"
+
 }
 
 ###############       Main        ###############
 
 tagName="unbound"
 
-theAction="${1}"										#	input action
-theName="${2}"											#	input RPZ name
-theURL="${3}"											#	input RPZ URL
-
-check_name "${theName}"									#	is this a valid name?
+theAction="${1}"                    #  input action
+theName="${2}"                      #  input RPZ name
+theURL="${3}"                       #  input RPZ URL
 
-rpzConfig="/etc/unbound/local.d/${theName}.rpz.conf"	#	output zone conf file
-rpzFile="/etc/unbound/zonefiles/${theName}.rpz"			#	output for RPZ file
+rpzConfig="/etc/unbound/local.d/${theName}.rpz.conf"    #  output zone conf file
+rpzFile="/etc/unbound/zonefiles/${theName}.rpz"         #  output for RPZ file
 
 case "${theAction}" in
 
-	#	add new rpz list
-	add )
-		#	does this config already exist?  If yes, then exit
-		if [[ -f "${rpzConfig}" ]] ; then
-			msg_log "info: rpz: ${rpzConfig} already exists. exit"
-			exit 1
-		fi
-
-		#	is this a valid URL?
-		regex='^https://[-[:alnum:]\+&@#/%?=~_|!:,.;]*[-[:alnum:]\+&@#/%=~_|]'
-		if ! [[ "${theURL}" =~ $regex ]] ; then
-			msg_log "error: rpz: the URL is not valid: \"${theURL}\". exit."
-			exit 1
-		fi
-
-		#	create the zone config file
-		msg_log "info: rpz: add config file \"${theName}.rpz.conf\""
-		cat <<-EOF > "${rpzConfig}"
-		rpz:
-			name:					${theName}.rpz
-			zonefile:				/etc/unbound/zonefiles/${theName}.rpz
-			url:  ${theURL}
-			rpz-action-override:	nxdomain
-			rpz-log:				yes
-			rpz-log-name:			${theName}
-			rpz-signal-nxdomain-ra:	yes
-		EOF
-
-		#	set-up zone file
-		/usr/bin/touch "${rpzFile}"
-		#	unbound requires these settings for rpz files
-		/bin/chown nobody:nobody "${rpzFile}"
-		/bin/chmod 644 "${rpzFile}"
-		;;
-
-	#	trash config file & rpz file
-	remove )
-		if ! [[ -f "${rpzConfig}" ]] ; then
-			msg_log "info: rpz: ${rpzConfig} does not exist. exit"
-			exit 1
-		fi
-
-		msg_log "info: rpz: remove config file & rpz file \"${theName}\""
-		/bin/rm "${rpzConfig}"
-		/bin/rm "${rpzFile}"
-
-		check_unbound_conf
-		;;
-
-	#	make a new allow or block rpz file
-	make )
-		case "${theName}" in
-			allow )
-				make_rpz_file allow
-			;;
-
-			block )
-				make_rpz_file block
-			;;
-
-			allowblock )
-				make_rpz_file allow
-				make_rpz_file block
-			;;
-
-			* )
-				msg_log "error: rpz: the NAME is not valid: \"${theName}\". exit."
-				exit 1
-			;;
-		esac
-
-		check_unbound_conf
-		;;
-
-	*)
-		msg_log "error: rpz: missing or incorrect parameter"
-		/usr/bin/printf "Usage:  rpzConfig.sh <ACTION> <NAME> <URL>\n"
-		exit 1
-		;;
+    #  add new rpz list
+    add )
+	check_name "${theName}"             #  is this a valid name?
+        #  does this config already exist?  If yes, then exit
+        if [[ -f "${rpzConfig}" ]] ; then
+            msg_log "error: rpz: duplicate - ${rpzConfig} already exists. exit"
+            exit 104
+        fi
+
+        #  is this a valid URL?
+        regex='^https://[-[:alnum:]\+&@#/%?=~_|!:,.;]*[-[:alnum:]\+&@#/%=~_|]'
+        if ! [[ "${theURL}" =~ $regex ]] ; then
+            msg_log "error: rpz: the URL is not valid: \"${theURL}\". exit."
+            exit 105
+        fi
+
+        #  create the zone config file
+        msg_log "info: rpz: add config file \"${theName}.rpz.conf\""
+echo "rpz:
+    name:                   ${theName}.rpz
+    zonefile:               ${rpzFile}
+    url:                    ${theURL}
+    rpz-action-override:    nxdomain
+    rpz-log:                yes
+    rpz-log-name:           ${theName}
+    rpz-signal-nxdomain-ra: yes
+" > "${rpzConfig}"
+        
+        #  set-up zonefile
+        #    create an empty rpz file
+        touch "${rpzFile}"
+        #  unbound requires these settings for rpz files
+        chown nobody:nobody "${rpzFile}" "${rpzConfig}"
+        chmod 644 "${rpzFile}" "${rpzConfig}"
+        ;;
+
+    #  trash config file & rpz file
+    remove )
+	check_name "${theName}"             #  is this a valid name?
+        if ! [[ -f "${rpzConfig}" ]] ; then
+            msg_log "error: rpz: cannot remove ${rpzConfig}, does not exist. exit"
+            exit 106
+        fi
+
+        msg_log "info: rpz: remove config file & rpz file \"${theName}\""
+        rm "${rpzConfig}"
+        rm "${rpzFile}"
+
+        check_unbound_conf
+        ;;
+
+    #  make a new allow or block rpz file
+    make )
+        case "${theName}" in
+            allow )
+            make_rpz_file allow
+            ;;
+
+        block )
+            make_rpz_file block
+            ;;
+
+        allowblock )
+            make_rpz_file allow
+            make_rpz_file block
+            ;;
+
+        * )
+            msg_log \
+              "error: rpz: the NAME \"${theName}\" is not valid - \"allow\" or \"block\" only.  exit."
+            exit 107
+            ;;
+        esac
+
+        check_unbound_conf
+        ;;
+        
+    list)
+        awk -F' ' '/^name:|\sname:/{ gsub(/.rpz/, "" ) ; printf $2} /\surl:|^url:/{print "="$2}' \
+          /etc/unbound/local.d/*.rpz.conf
+        exit
+        ;;    
+        
+    *)
+        msg_log "error: rpz: missing or incorrect parameter"
+        printf "Usage:  rpzConfig <ACTION> <NAME> <URL>\n"
+        printf "Version:  ${version}\n"
+        exit 108
+        ;;
 
 esac
 
-#	reload due to the changes
+#  reload due to the changes
 msg_log  "rpz: running \"unbound-control reload\""
-/usr/sbin/unbound-control reload
+unbound-control reload
 exit_code=$?
 if [[ "${exit_code}" -ne 0 ]] ; then
-	msg_log "error: rpz: unbound-control \"${theName}\". exit."
-	exit "${exit_code}"
+    msg_log "error: rpz: unbound-control \"${theName}\". exit."
+    exit 109
 fi
 
 exit
diff --git a/config/rpz/rpz-metrics b/config/rpz/rpz-metrics
index 4d932726e..d154f70b9 100644
--- a/config/rpz/rpz-metrics
+++ b/config/rpz/rpz-metrics
@@ -19,62 +19,61 @@ 
 #                                                                             #
 ###############################################################################
 
-#	v19 on 2024-07-30
+version="2024-08-16"	# v20
 
 ###############       Main        ###############
 
-weeks="${1:-2}"			#	default to two message logs
-sortBy="${2:-name}"  	#	by name or by hits
+weeks="${1:-2}"         #   default to two message logs
+sortBy="${2:-name}"     #   by name or by hits
 
-#	get the list of message logs for N weeks
-messageLogs=$( find /var/log/messages* -type f |
-	/usr/bin/sort --version-sort |
-	head -"${weeks}" )
+#   get the list of message logs for N weeks
+messageLogs=$( find /var/log/messages* -type f | sort --version-sort |
+    head -"${weeks}" )
 
-#	get the list of RPZ names & counts from the message log(s)
+#   get the list of RPZ names & counts from the message log(s)
 rpzNameCount=$( for logf in ${messageLogs} ; do
-	/usr/bin/zgrep --text --extended-regexp 'info: rpz: applied.* A IN$' "${logf}" |
-	/usr/bin/awk '$10 ~ /\[\w*]/ { print $10 }' ;
-	done | /usr/bin/sort | /usr/bin/uniq --count )
+    zgrep --text --extended-regexp 'info: rpz: applied.* A IN$' "${logf}" |
+      awk '$10 ~ /\[\w*]/ { print $10 }' ;
+    done | sort | uniq --count )
 
-#	flip results and remove brackets `[` and `]`
-rpzNameCount=$( /bin/echo "${rpzNameCount}" |
-	/usr/bin/awk '{ print $2, $1 }' |
-	/bin/sed --regexp-extended 's|^\[(.*)\]|\1|' )
+#   flip results and remove brackets `[` and `]`
+rpzNameCount=$( echo "${rpzNameCount}" |
+    awk '{ print $2, $1 }' |
+      sed --regexp-extended 's|^\[(.*)\]|\1|' )
 
-#	grab only names
-rpzNames=$( /bin/echo "${rpzNameCount}" | /usr/bin/awk '{ print $1 }' )
+#   grab only names
+rpzNames=$( echo "${rpzNameCount}" | awk '{ print $1 }' )
 
-#	get list of RPZ files
-rpzFileList=$( /bin/find /etc/unbound/zonefiles -type f -iname "*.rpz" )
+#   get list of RPZ files
+rpzFileList=$( find /etc/unbound/zonefiles -type f -iname "*.rpz" )
 
-#	get basename of those files
-rpzBaseNames=$( /bin/echo "${rpzFileList}" |
-	/bin/sed 's|/etc/unbound/zonefiles/||g ; s|\.rpz||g ;' )
+#   get basename of those files
+rpzBaseNames=$( echo "${rpzFileList}" |
+    sed 's|/etc/unbound/zonefiles/||g ; s|\.rpz||g ;' )
 
-#	add to rpzNames
+#   add to rpzNames
 rpzNames="${rpzNames}"$'\n'"${rpzBaseNames}"
 
-#	drop duplicate names
-rpzNames=$( echo "${rpzNames}" | /usr/bin/sort --unique  )
+#   drop duplicate names
+rpzNames=$( echo "${rpzNames}" | sort --unique  )
 
-#	get line count for each RPZ
-lineCount=$( /bin/echo "${rpzFileList}" | /usr/bin/xargs wc -l )
+#   get line count for each RPZ
+lineCount=$( echo "${rpzFileList}" | xargs wc -l )
 
-#	get comment line count and blank line count for each RPZ
-commentCount=$( /bin/echo "${rpzFileList}" |
-	/usr/bin/xargs /bin/grep --count -e "^$" -e "^;" )
+#   get comment line count and blank line count for each RPZ
+commentCount=$( echo "${rpzFileList}" |
+    xargs grep --count -e "^$" -e "^;" )
 
-#	get modified date each RPZ
-modDateList=$( /bin/echo "${rpzFileList}" | xargs stat -c '%.10y  %n' )
+#   get modified date each RPZ
+modDateList=$( echo "${rpzFileList}" | xargs stat -c '%.10y  %n' )
 
-ucListAuthZones=$( /usr/sbin/unbound-control list_auth_zones )
+ucListAuthZones=$( unbound-control list_auth_zones )
 
-#	get width of RPZ names
-pWidth=$( /bin/echo "${rpzNames}" | /usr/bin/awk '{ print $1"   " }' | wc -L )
+#   get width of RPZ names
+pWidth=$( echo "${rpzNames}" | awk '{ print $1"   " }' | wc -L )
 pFormat="%-${pWidth}s %-8s %-8s %-8s %10s %12s\n"
 
-#	print title line
+#   print title line
 printf "${pFormat}" "name" "hits" "active" "lines" "hits/line%" "last update"
 printf -- "--------------"
 
@@ -83,60 +82,60 @@  totalLines=0
 totalHits=0
 while read -r theName
 do
-	printf -- "-"									#	pretend progress bar
-	#	get hit count
-	theHits="0"
-	if output=$( /bin/grep "^${theName}\s" <<< "${rpzNameCount}" ) ; then
-		theHits=$( /bin/echo "${output}" |
-		/usr/bin/awk '{ print $2 }' )
-		totalHits=$(( totalHits + theHits ))
-	fi
-
-	#	is this RPZ list active?
-	theActive="disabled"
-	if /bin/grep --quiet "^${theName}\.rpz" <<< "${ucListAuthZones}"
-	then
-		theActive="enabled"
-	fi
-
-	#	get line count then subtract comment count and blank line count
-	#	  from total line count
-	theLines="n/a"
-	hitsPerLine="0"
-	if output=$( /bin/grep --fixed-strings "/${theName}.rpz" <<< "${lineCount}" ) ; then
-		theLines=$( /bin/echo "${output}" | /usr/bin/awk '{ print $1 }' )
-		totalLines=$(( totalLines + theLines ))
-
-		if [[ "${theLines}" -gt 2 ]] ; then
-			hitsPerLine=$(( 100 * theHits / theLines ))
-		fi
-	fi
-
-	#	get modification date
-	theModDate="n/a"
-	if output=$( /bin/grep --fixed-strings "/${theName}.rpz" <<< "${modDateList}" ) ; then
-		theModDate=$( /bin/echo "${output}" | /usr/bin/awk '{ print $1 }' )
-	fi
-
-	#	add to results list
-	theResults+="${theName} ${theHits} ${theActive} ${theLines} ${hitsPerLine} ${theModDate}"$'\n'
+    printf -- "-"        #   pretend progress bar
+    #   get hit count
+    theHits="0"
+    if output=$( grep "^${theName}\s" <<< "${rpzNameCount}" ) ; then
+        theHits=$( echo "${output}" |
+        awk '{ print $2 }' )
+        totalHits=$(( totalHits + theHits ))
+    fi
+
+    #   is this RPZ list active?
+    theActive="disabled"
+    if grep --quiet "^${theName}\.rpz" <<< "${ucListAuthZones}"
+    then
+        theActive="enabled"
+    fi
+
+    #   get line count
+    theLines="n/a"
+    hitsPerLine="0"
+    if output=$( grep --fixed-strings "/${theName}.rpz" <<< "${lineCount}" ) ; then
+        theLines=$( echo "${output}" | awk '{ print $1 }' )
+        totalLines=$(( totalLines + theLines ))
+
+        if [[ "${theLines}" -gt 2 ]] ; then
+            hitsPerLine=$(( 100 * theHits / theLines ))
+        fi
+    fi
+
+    #   get modification date
+    theModDate="n/a"
+    if output=$( grep --fixed-strings "/${theName}.rpz" <<< "${modDateList}" ) ; then
+        theModDate=$( echo "${output}" | awk '{ print $1 }' )
+    fi
+
+    #   add to results list
+    theResults+="${theName} ${theHits} ${theActive} ${theLines} ${hitsPerLine} ${theModDate}"$'\n'
+    
 done <<< "${rpzNames}"
 
 case "${sortBy}" in
-	names|name)	sortArg=(-k3,3r -k1,1)			;;	#	sort by "active" then by "name"
+    names|name) sortArg=(-k3,3r -k1,1)          ;;  #   sort by "active" then by "name"
 
-	hits|hit)	sortArg=(-k3,3r -k2,2nr -k1,1)	;;	#	sort by "active" then by "hits" then by "name"
+    hits|hit)   sortArg=(-k3,3r -k2,2nr -k1,1)  ;;  #   sort by "active" then by "hits" then by "name"
 
-	lines|line)	sortArg=(-k3,3r -k4,4nr -k1,1)	;;	#	sort by "active" then by "lines" then by "name"
+    lines|line) sortArg=(-k3,3r -k4,4nr -k1,1)  ;;  #   sort by "active" then by "lines" then by "name"
 esac
 
 printf -- "--------------\n"
-#	remove blank lines, sort, print as columns
-/bin/echo "${theResults}" |
-	/usr/bin/awk '!/^[[:space:]]*$/' |
-	/usr/bin/sort "${sortArg[@]}"    |
-	/usr/bin/awk --assign=width="${pWidth}" \
-		'{ printf "%-*s %-8s %-8s %-8s %10s %12s\n", width, $1, $2, $3, $4, $5, $6 }'
+#   remove blank lines, sort, print as columns
+echo "${theResults}" |
+    awk '!/^[[:space:]]*$/' |
+    sort "${sortArg[@]}"    |
+    awk --assign=width="${pWidth}" \
+        '{ printf "%-*s %-8s %-8s %-8s %10s %12s\n", width, $1, $2, $3, $4, $5, $6 }'
 
 printf "${pFormat}" "" "=======" "" "========" "" ""
 printf "${pFormat}" "Totals -->" "${totalHits}" "" "${totalLines}" "" ""
diff --git a/config/rpz/rpz-sleep b/config/rpz/rpz-sleep
index eeef1174a..dd3603599 100644
--- a/config/rpz/rpz-sleep
+++ b/config/rpz/rpz-sleep
@@ -19,38 +19,38 @@ 
 #                                                                             #
 ###############################################################################
 
-#	v04 on 2024-07-05
+version="2024-08-16"        # v05
 
 ###############     Functions     ###############
 
-#	send message to message log
+#   send message to message log
 msg_log () {
-	/usr/bin/logger --tag "${tagName}" "$*"
-	if /usr/bin/tty --silent ; then
-		echo "${tagName}:" "$*"
-	fi
+    logger --tag "${tagName}" "$*"
+    if tty --silent ; then
+        echo "${tagName}:" "$*"
+    fi
 }
 
 ###############       Main        ###############
 
 tagName="unbound"
 
-sleepTime="${1:-5m}"			#	default to sleep for 5m (5 minutes)
+sleepTime="${1:-5m}"            #  default to sleep for 5m (5 minutes)
 
-zoneList=$( /usr/sbin/unbound-control list_auth_zones | /usr/bin/awk '{print $1}' )
+zoneList=$( unbound-control list_auth_zones | awk '{print $1}' )
 
 for zone in ${zoneList} ; do
-	/usr/bin/printf "disable ${zone}\t"
-	/usr/sbin/unbound-control rpz_disable "${zone}"
+    printf "disable ${zone}\t"
+    unbound-control rpz_disable "${zone}"
 done
 
 msg_log "info: rpz: disabled all zones for ${sleepTime}"
 
-/bin/sleep "${sleepTime}"
+sleep "${sleepTime}"
 
 for zone in ${zoneList} ; do
-	/usr/bin/printf "enable ${zone}\t"
-	/usr/sbin/unbound-control rpz_enable "${zone}"
+    printf "enable ${zone}\t"
+    unbound-control rpz_enable "${zone}"
 done
 
 msg_log "info: rpz: enabled all zones"
diff --git a/lfs/rpz b/lfs/rpz
index 73f6f2b1b..16d1d0803 100644
--- a/lfs/rpz
+++ b/lfs/rpz
@@ -62,25 +62,31 @@  $(TARGET) :
 	@$(PREBUILD)
 	@rm -rf $(DIR_APP)
 
-	#	install RPZ scripts
+	# install RPZ scripts
 	install -v -m 755 \
 	  $(DIR_CONF)/rpz/{rpz-config,rpz-metrics,rpz-sleep} -t /usr/sbin
 
+	# Add conf file to /etc directory
+	cp -vf $(DIR_CONF)/rpz/00-rpz.conf /etc/unbound/local.d
+	cp -vf $(DIR_CONF)/rpz/block.rpz.conf /etc/unbound/local.d
+
 	# Install settings folder and two empty files
 	mkdir -pv /var/ipfire/dns/rpz
 	touch /var/ipfire/dns/rpz/allowlist
 	touch /var/ipfire/dns/rpz/blocklist
 
-	# Add conf file to /etc directory
-	cp -vf $(DIR_CONF)/rpz/00-rpz.conf /etc/unbound/local.d
-
-	#	create zonefiles directory for the RPZ files and add two empty RPZ
-	#	  files to avoid a unbound config error
+	# create zonefiles directory for the RPZ files and add two empty RPZ
+	#  files to avoid a unbound config error
 	mkdir -pv /etc/unbound/zonefiles
-	chown -v nobody:nobody /etc/unbound/zonefiles
 	touch /etc/unbound/zonefiles/allow.rpz
 	touch /etc/unbound/zonefiles/block.rpz
 
+	#  set owner for unbound related files
+	chown -vR nobody:nobody  \
+	  /var/ipfire/dns/rpz    \
+	  /etc/unbound/zonefiles \
+	  /etc/unbound/local.d
+
 	# Install backup definition
 	cp -vf $(DIR_CONF)/backup/includes/rpz /var/ipfire/backup/addons/includes/rpz
 
diff --git a/src/paks/rpz/uninstall.sh b/src/paks/rpz/uninstall.sh
index 4fb20e127..51edaa176 100644
--- a/src/paks/rpz/uninstall.sh
+++ b/src/paks/rpz/uninstall.sh
@@ -27,5 +27,9 @@ 
 make_backup ${NAME}
 remove_files
 
+#  delete rpz config files.  Otherwise unbound will throw error:
+#    "[1723428668] unbound-control[17117:0] error: connect: Connection refused for 127.0.0.1 port 8953"
+/bin/rm -fv /etc/unbound/local.d/*.rpz.conf
+
 #  start unbound to load unbound config file
 /etc/init.d/unbound start
diff --git a/src/paks/rpz/update.sh b/src/paks/rpz/update.sh
index 938a93a40..fd46a185a 100644
--- a/src/paks/rpz/update.sh
+++ b/src/paks/rpz/update.sh
@@ -20,6 +20,24 @@ 
 ###############################################################################
 #
 . /opt/pakfire/lib/functions.sh
+
+#  from update.sh
 extract_backup_includes
-./uninstall.sh
-./install.sh
+
+#  stop unbound to delete RPZ conf file
+/etc/init.d/unbound stop
+
+#  from uninstall.sh
+make_backup ${NAME}
+remove_files
+
+#  delete rpz config files.  Otherwise unbound will throw error:
+#    "unbound-control[nn:0] error: connect: Connection refused for 127.0.0.1 port 8953"
+/bin/rm --verbose --force /etc/unbound/local.d/*.rpz.conf
+
+#  from install.sh
+extract_files
+restore_backup ${NAME}
+
+#  restart unbound to load config files
+/etc/init.d/unbound start