#!/bin/bash # Copyright (C) 2023 Pädagogisches Landesinstitut Rheinland-Pfalz # Copyright (C) 2023 Mike Gabriel # Copyright (C) 2023 Daniel Teichmann # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the # Free Software Foundation, Inc., # 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. # postinst script for debian-edu-router-plugin.mdns-reflector # # see: dh_installdeb(1) set -e . /usr/share/debconf/confmodule || exit 255 if [ -e /etc/debian-edu/router.conf ]; then source /etc/debian-edu/router.conf fi PRODUCTNAME="${PRODUCTNAME:-"Debian Edu Router"}" PRODUCTNAME_PLUGIN_SUFFIX="${PRODUCTNAME_PLUGIN_SUFFIX:-"Plugin"}" PRODUCTNAME_PLUGIN="${PRODUCTNAME_PLUGIN:-"${PRODUCTNAME} ${PRODUCTNAME_PLUGIN_SUFFIX}: mDNS reflector"}" PRODUCTVERSION=$(dpkg-query --show --showformat='${Version}' "${DPKG_MAINTSCRIPT_PACKAGE}" 2>/dev/null || echo "UNKNOWN") db_title "${PRODUCTNAME_PLUGIN}" common_file="/usr/share/debian-edu-router/debian-edu-router.common" # Load common functions, variables and stuff. if [ -s "$common_file" ]; then source "$common_file" else echo "Could not load common file at "$common_file"." exit 0; fi db_version 2.0 db_capb backup escape # summary of how this script can be called: # * `configure' # * `abort-upgrade' # * `abort-remove' `in-favour' # # * `abort-remove' # * `abort-deconfigure' `in-favour' # `removing' # # for details, see https://www.debian.org/doc/debian-policy/ or # the debian-policy package CONFIG_BACKUP_DIR="$(mktemp -d --suffix -debian-edu-router-plugin.mdns-reflector_CONFIG_BACKUP)" DEBUG_CONFIG_DIR="$(mktemp -d --suffix -debian-edu-router-plugin.mdns-reflector_DEBUG_CONFIG)" # Load mDNS dialog answers db_get debian-edu-router-plugin.mdns-reflector/mdns-reflector-enabled || true mdns_reflector_enabled="${RET}" db_get debian-edu-router-plugin.mdns-reflector/service-mdns-reflector-networks || true service_int_mdns_reflector_networks=( $(echo ${RET} | tr -d ',') ) # # DNS-SD SERVICES (STREAMING) # db_get debian-edu-router-plugin.mdns-reflector/service-airplay-networks || true services_networks_airplay=( $(echo ${RET} | tr -d ',') ) db_get debian-edu-router-plugin.mdns-reflector/service-googlecast-networks || true services_networks_googlecast=( $(echo ${RET} | tr -d ',') ) db_get debian-edu-router-plugin.mdns-reflector/service-miracast-networks || true services_networks_miracast=( $(echo ${RET} | tr -d ',') ) # # DNS-SD SERVICES (PRINTERS) # db_get debian-edu-router-plugin.mdns-reflector/service-airprint-networks || true services_networks_airprint=( $(echo ${RET} | tr -d ',') ) db_get debian-edu-router-plugin.mdns-reflector/service-ippeverywhere-networks || true services_networks_ippeverywhere=( $(echo ${RET} | tr -d ',') ) function create_config_backup() { chmod -R 0700 "${CONFIG_BACKUP_DIR}" # Creating a backup of old d-e-r config files. mkdir -p ${CONFIG_BACKUP_DIR}/etc/mdns-reflector/conf.d/ cp /etc/mdns-reflector/conf.d/??_d-e-r-p.m-r_* ${CONFIG_BACKUP_DIR}/etc/mdns-reflector/conf.d/ &> /dev/null || true mkdir -p ${CONFIG_BACKUP_DIR}/etc/uif/uif.conf.d/ cp /etc/uif/uif.conf.d/??_d-e-r-p.m-r_* ${CONFIG_BACKUP_DIR}/etc/uif/uif.conf.d/ &> /dev/null || true } function restore_config_backup() { error_log "New configuration for '$1' failed while restarting the service." error_log "Restoring configuration backup now..." if [ -n "${D_E_R_DEBUG}" ]; then config_files=( "/etc/mdns-reflector/conf.d" "/etc/uif/uif.conf.d" ) for conf_file in "${config_files[@]}"; do mkdir -p "${DEBUG_CONFIG_DIR}/$conf_file" # Move new and broken conf files into tmp folder. mv -v "$conf_file"/??_d-e-r-p.m-r_* "${DEBUG_CONFIG_DIR}$conf_file" || true done debug_log "Generated config files are now located at: '${cyan}${DEBUG_CONFIG_DIR}${green}'." else # Delete new and broken d-e-r config files. rm -fv /etc/mdns-reflector/conf.d/??_d-e-r-p.m-r_* || true rm -fv /etc/uif/uif.conf.d/??_d-e-r-p.m-r_* || true debug_log "Generated config files were purged from the system." fi # Copying old and hopefully working d-e-r config files. cp -fv ${CONFIG_BACKUP_DIR}/etc/mdns-reflector/conf.d/* /etc/mdns-reflector/conf.d/ || true cp -fv ${CONFIG_BACKUP_DIR}/etc/uif/uif.conf.d/* /etc/uif/uif.conf.d/ || true debug_log "Old config files are installed again." # Hopefully everything should be alright now. manage_unit restart mdns-reflector || error_log "Something is terribly broken since the old config files for 'mdns-reflector' don't work either!" manage_unit restart uif || error_log "Something is terribly broken since the old config files for 'uif' don't work either!" exit 1 } function process_firewall_answers() { # # mDNS (Firewall rules) # # When running in production mode, make sure all (not-to-be-configured) # uif configurations files are removed from /etc/uif/uif.conf.d/ rm -f /etc/uif/uif.conf.d/??_d-e-r-p.m-r_* # Not needed, at this point we can be sure that we have at least 2 interfaces # configured for mdns-reflector. (Commented because we might wanna do some # changes even if there aren't enough interfaces configured.) # if [[ -z "${service_int_mdns_reflector_networks[@]}" ]]; then # return 0; # fi # IPv4 network file if [ "$IPV4" == true ]; then uif_cfg_file="/etc/uif/uif-ipv4-networks.inc.d/05_d-e-r-p.m-r_mDNS-Address-v4" echo "###" 1> "${uif_cfg_file}" echo "### $PRODUCTNAME_PLUGIN - Firewall" >> "${uif_cfg_file}" echo "###" >> "${uif_cfg_file}" echo >> "${uif_cfg_file}" echo "### Managed by debian-edu-router-plugin.mdns-reflector." >> "${uif_cfg_file}" echo "### Don't modify this file." >> "${uif_cfg_file}" echo >> "${uif_cfg_file}" echo "network {" >> "${uif_cfg_file}" echo " mdns_addr 224.0.0.251" >> "${uif_cfg_file}" echo "}" >> "${uif_cfg_file}" fi # IPv6 network file if [ "$IPV6" == true ]; then uif_cfg_file="/etc/uif/uif-ipv6-networks.inc.d/05_d-e-r-p.m-r_mDNS-Address-v6" echo "###" 1> "${uif_cfg_file}" echo "### $PRODUCTNAME_PLUGIN - Firewall" >> "${uif_cfg_file}" echo "###" >> "${uif_cfg_file}" echo >> "${uif_cfg_file}" echo "### Managed by debian-edu-router-plugin.mdns-reflector." >> "${uif_cfg_file}" echo "### Don't modify this file." >> "${uif_cfg_file}" echo >> "${uif_cfg_file}" echo "network {" >> "${uif_cfg_file}" echo " mdns_addr ff02::fb" >> "${uif_cfg_file}" echo "}" >> "${uif_cfg_file}" fi # Actual uif conf file uif_cfg_file="/etc/uif/uif.conf.d/05_d-e-r-p.m-r_mDNS-Reflection" echo "###" 1> "${uif_cfg_file}" echo "### $PRODUCTNAME_PLUGIN - Firewall" >> "${uif_cfg_file}" echo "###" >> "${uif_cfg_file}" echo >> "${uif_cfg_file}" echo "### Managed by debian-edu-router-plugin.mdns-reflector." >> "${uif_cfg_file}" echo "### Don't modify this file." >> "${uif_cfg_file}" echo >> "${uif_cfg_file}" echo "service {" >> "${uif_cfg_file}" echo " mdns udp(/5353)" >> "${uif_cfg_file}" echo "}" >> "${uif_cfg_file}" echo >> "${uif_cfg_file}" mdns_define="mdns_addr" if [[ "$IPV4" == "true" || "$IPV6" != "true" ]]; then mdns_define="mdns_addr(4)" elif [[ "$IPV4" != "true" || "$IPV6" == "true" ]]; then mdns_define="mdns_addr(6)" fi echo "# Allows interfaces to receive and send mDNS packages." >> "${uif_cfg_file}" echo "filter {" >> "${uif_cfg_file}" for network in "${service_int_mdns_reflector_networks[@]}"; do echo " out+ o=iface_$network p=mdns d=$mdns_define" >> "${uif_cfg_file}" echo " in+ i=iface_$network p=mdns d=$mdns_define" >> "${uif_cfg_file}" echo >> "${uif_cfg_file}" done echo "}" >> "${uif_cfg_file}" echo >> "${uif_cfg_file}" process_services_firewall_answers } function process_services_firewall_answers() { local UIF_DIR="/etc/uif/uif.conf.d" local MDNS_FW_FILE_PREFIX="60_d-e-r-p.m-r_mDNS" # Remove any existing firewall rules rm -fv "${UIF_DIR}/${MDNS_FW_FILE_PREFIX}"* # Function to create firewall rules for a specific service create_service_rules() { local service="$1" local ports="${@:2}" local networks="" # Skip if service name is empty if [ -z "$service" ]; then error_log "create_service_rules(): Service name cannot be empty, skipping..." return fi local uif_cfg_file="${UIF_DIR}/${MDNS_FW_FILE_PREFIX}-${service}" # Get selected networks from debconf db_get "debian-edu-router-plugin.mdns-reflector/service-${service}-networks" networks="${RET}" # Skip if no networks selected or only one network if [[ $(echo "$networks" | tr ',' '\n' | wc -l) -le 1 ]]; then notice_log "Service '${service}' won't be shared across networks (too few networks selected), skipping..." return fi { echo "###" echo "### $PRODUCTNAME_PLUGIN - Firewall" echo "###" echo echo "### Managed by debian-edu-router-plugin.mdns-reflector." echo "### Don't modify this file." echo if [[ -n "${ports}" ]]; then echo "service {" echo " ${service} ${ports}" echo "}" echo fi generate_unique_pairs ${networks[@]} | while read -r pair; do net1="$(awk '{print $1}' <<< "${pair}")" net2="$(awk '{print $2}' <<< "${pair}")" # Do not tolerate empty net names. [ -z "$net1" ] && continue [ -z "$net2" ] && continue echo "# Allow service-sharing between '$net1' and '$net2'." echo "filter {" echo " out+ p=${service} o=iface_${net1}" echo " out+ p=${service} o=iface_${net2}" echo " in+ p=${service} i=iface_${net1}" echo " in+ p=${service} i=iface_${net2}" echo " fw+ p=${service} o=iface_${net1} i=iface_${net2}" echo " fw+ p=${service} i=iface_${net1} o=iface_${net2}" echo "}" echo done } > "${uif_cfg_file}" # Set permissions chmod 644 "${uif_cfg_file}" } # AirPlay Ports (AirPlay and RAOP) create_service_rules "airplay" \ "tcp(/5000)" "tcp(/5001)" "udp(/6010)" "udp(/6011)" "udp(6012/)" \ "tcp(/7000)" "tcp(/7100)" "tcp(/49152:49172)" # Google Cast Ports create_service_rules "googlecast" "tcp(/8008) tcp(/8009)" # Miracast Ports create_service_rules "miracast" "tcp(/7236)" # AirPrint Ports create_service_rules "airprint" "tcp(/515) tcp(/631) tcp(/9100)" # IPP Everywhere Ports (same as AirPrint) create_service_rules "ippeverywhere" "tcp(/515) tcp(/631) tcp(/9100)" } function process_services_answers() { # # mDNS (mdns-reflector service) # # When running in production mode, make sure all (not-to-be-configured) # mdns-reflector configurations files are removed from /etc/mdns-reflector/conf.d/ rm -f /etc/mdns-reflector/conf.d/??_d-e-r-p.m-r_* # Not needed, at this point we can be sure that we have at least 2 interfaces # configured for mdns-reflector. (Commented because we might wanna do some # changes even if there aren't enough interfaces configured.) # if [[ -z "${service_int_mdns_reflector_networks[@]}" ]]; then # return 0; # fi mkdir -p "/etc/mdns-reflector/conf.d" || true mdns_reflector_cfg_file="/etc/mdns-reflector/conf.d/10_d-e-r-p.m-r_mDNS-Reflection" echo "###" 1> "${mdns_reflector_cfg_file}" echo "### $PRODUCTNAME_PLUGIN" >> "${mdns_reflector_cfg_file}" echo "###" >> "${mdns_reflector_cfg_file}" echo >> "${mdns_reflector_cfg_file}" echo "### Managed by debian-edu-router-plugin.mdns-reflector." >> "${mdns_reflector_cfg_file}" echo "### Don't modify this file." >> "${mdns_reflector_cfg_file}" echo >> "${mdns_reflector_cfg_file}" internal_networks_interfaces=() daemon_args="" if [[ "$IPV4" == "true" || "$IPV6" != "true" ]]; then daemon_args="-4" elif [[ "$IPV4" != "true" || "$IPV6" == "true" ]]; then daemon_args="-6" fi # debug_log "mDNS participating networks: '${service_int_mdns_reflector_networks[@]}'" # debug_log "Internal networks: '${!internal_networks[@]}'" # debug_log "Internal networks ifaces: '${internal_networks[@]}'" # Fill $internal_networks_interfaces with interface (not network names) internal_networks_interfaces=() for _i_n_name in "${service_int_mdns_reflector_networks[@]}"; do _iface="${internal_networks[$_i_n_name]}" internal_networks_interfaces+=("${_iface}") done config_file_content=$( cat <<- ENDOFMESSAGE INTERFACES="${internal_networks_interfaces[@]}" LOGGING_LEVEL=warning #EXTRA_OPTIONS= DAEMON_ARGS="${daemon_args}" ENDOFMESSAGE ) echo "$config_file_content" >> "${mdns_reflector_cfg_file}" } function check_plugin_enabled() { if [ "$mdns_reflector_enabled" = "false" ]; then if [ "${D_E_R_LOGINMENU}" = "true" ] && [ "$CONFIGURE_ONLY" != "ONOFF" ]; then error_log "$PRODUCTNAME_PLUGIN is turned off! Please re-enable it via 't', before retrying." exit 0 fi manage_unit disablenow mdns-reflector rm -f /var/lib/debian-edu-router/d-e-r-p.m-r/enabled notice_log "Deactivating ${PRODUCTNAME_PLUGIN}..." exit 0 else echo "${PRODUCTNAME_PLUGIN} is enabled." > /var/lib/debian-edu-router/d-e-r-p.m-r/enabled fi } function main() { check_plugin_enabled # may never come back (exit 0)... # Only execute anything in this script, if there are enough mDNS Reflector networks # configured (at least 2). Otherwise, take the early exit. if [[ "${#service_int_mdns_reflector_networks[@]}" -lt 2 ]]; then notice_log "There are not enough internal networks configured," notice_log "so setting up an mDNS reflector doesn't make much sense." notice_log "Please use the loginmenu to reconfigure the Router." notice_log "You could also execute 'dpkg-reconfigure debian-edu-router-config'." notice_log "" notice_log "$PRODUCTNAME_PLUGIN: skipping configuration." exit 0 fi parse_ip_versions # Making sure temporary backup folder isn't accessible to others if [ -n "${CONFIG_BACKUP_DIR}" ]; then create_config_backup else error_log "\${CONFIG_BACKUP_DIR} is not initialized! Can't create backup!" exit 1 fi # Collect all ifaces of internal networks declare -A internal_networks get_internal_networks_ifaces "false" debug_log "Interfaces of internal networks found for mDNS reflection:" for network in "${service_int_mdns_reflector_networks[@]}"; do _iface="${internal_networks[$network]}" # » -> debug_log "$(printf " » %-18s-> $network" "$_iface")" if ! is_supported "$network"; then error_log "Internal network '$network' is not supported." \ "Please reconfigure the mdns-reflector plugin" \ "manually either using the loginmenu or with" \ "'dpkg-reconfigure debian-edu-router-plugin.mdns-reflector'." exit 1; fi done # process firewall settings and restart firewall process_firewall_answers if [ "${SKIP_SERVICE_RESTARTS_POSTINST}" != "1" ]; then manage_unit restart uif || restore_config_backup "uif" && \ debug_log "Firewall 'uif' reconfigured and restarted." fi # process service settings and restart related services process_services_answers if [ "${SKIP_SERVICE_RESTARTS_POSTINST}" != "1" ]; then { manage_unit enable mdns-reflector manage_unit restart mdns-reflector debug_log "mDNS Reflector service 'mdns-reflector' successfully reconfigured, enabled and restarted." } || restore_config_backup "mdns-reflector" fi debug_log "Configuration of '$PRODUCTNAME_PLUGIN' finished." } configure_package="" if [[ "$1" == "triggered" ]]; then debug_log "Reconfiguring since trigger '$2' got activated. This $( )probably means, the admin reconfigures d-e-r-c currently." configure_package="true" fi if [[ "$1" == "configure" ]] || [[ "$configure_package" == "true" ]]; then main else error_log "postinst called with unknown argument \`$1'" >&2 exit 1 fi # dh_installdeb will replace this with shell code automatically # generated by other debhelper scripts. exit 0