sshmngr: avoid losing some backend changes on sshmngr restart

if backend uci is changed then restarting sshmngr can lead to
those changes being lost, try to preserve some of the changes

the following behaviour has been tested:

* if an extra option is set in backend UCI which is not set in
  sshmngr UCI for the same section, it will stay as it is
* manually added section in backend is not over-written
* sshmngr managed sections are properly added/modified/deleted
* option BbfSection is introduced which can be set to 0 to prevent
  sshmngr from modifying a backend section

the above works except for first boot (then sshmngr will override
backend UCI)
This commit is contained in:
Mohd Husaam Mehdi 2024-04-14 21:02:05 +05:30
parent fae8008269
commit df0e96d9ac
2 changed files with 92 additions and 26 deletions

View file

@ -5,7 +5,10 @@ USE_PROCD=1
. /lib/sshmngr/sshmngr.sh
service_triggers() {
start_service() {
configure_ssh
procd_add_reload_trigger sshmngr
}
service_triggers() {
procd_add_reload_trigger "sshmngr"
}

View file

@ -2,12 +2,13 @@
. /lib/sshmngr/backend.sh
TEMP_UCI_PATH="/tmp/sshmngr"
SSHMNGR_SECTIONS=""
handle_server_section()
{
local cfg="$1"
local enable
local TargetSectionType="$2"
local TargetUci="$3"
local PasswordAuth=""
local Port=""
local RootPasswordAuth=""
@ -17,11 +18,10 @@ handle_server_section()
local IdleTimeout=""
local MaxAuthTries=""
local ServerName="${cfg}"
local enable
config_get_bool enable $cfg enable 0
[ $enable -eq 0 ] && return
config_get PasswordAuth $cfg PasswordAuth
config_get Port $cfg Port
config_get RootPasswordAuth $cfg RootPasswordAuth
@ -31,34 +31,97 @@ handle_server_section()
config_get IdleTimeout $cfg IdleTimeout
config_get MaxAuthTries $cfg MaxAuthTries
# add section
uci -c "$TEMP_UCI_PATH" set $CONFIG.$ServerName=$CONFIG
# if someone does not want sshmngr to over-write/delete a section
# they can set option BbfSection to 0 in the BACKEND UCI
local BbfSection="$(uci -q get $TargetUci.$ServerName.BbfSection)"
# if section exists and its BbfSection flag is not 1 or true, then it means it was added by user
# so we don't modify it and return
if uci -q get $TargetUci.$ServerName >/dev/null 2>&1; then
if [ -z "$BbfSection" ] || [ "$BbfSection" == "0" ] || [ "$BbfSection" == "false" ]; then
return
fi
fi
# adding a section using uci set instead of uci add
# because if a section is not present then we want to add it
# if it is present then silently move on
uci -q set $TargetUci.$ServerName=$TargetSectionType
# set BbfSection to 1 so that in future we will know that this
# section was added by us
uci -q set $TargetUci.$ServerName.BbfSection=1
# set options
[ -n "$PasswordAuth" ] && uci -c "$TEMP_UCI_PATH" set $CONFIG.$ServerName.PasswordAuth=$PasswordAuth
[ -n "$Port" ] && uci -c "$TEMP_UCI_PATH" set $CONFIG.$ServerName.Port=$Port
[ -n "$RootPasswordAuth" ] && uci -c "$TEMP_UCI_PATH" set $CONFIG.$ServerName.RootPasswordAuth=$RootPasswordAuth
[ -n "$RootLogin" ] && uci -c "$TEMP_UCI_PATH" set $CONFIG.$ServerName.RootLogin=$RootLogin
[ -n "$Interface" ] && uci -c "$TEMP_UCI_PATH" set $CONFIG.$ServerName.Interface=$Interface
[ -n "$SSHKeepAlive" ] && uci -c "$TEMP_UCI_PATH" set $CONFIG.$ServerName.SSHKeepAlive=$SSHKeepAlive
[ -n "$IdleTimeout" ] && uci -c "$TEMP_UCI_PATH" set $CONFIG.$ServerName.IdleTimeout=$IdleTimeout
[ -n "$MaxAuthTries" ] && uci -c "$TEMP_UCI_PATH" set $CONFIG.$ServerName.MaxAuthTries=$MaxAuthTries
[ -n "$PasswordAuth" ] && uci -q set $TargetUci.$ServerName.PasswordAuth=$PasswordAuth
[ -n "$Port" ] && uci -q set $TargetUci.$ServerName.Port=$Port
[ -n "$RootPasswordAuth" ] && uci -q set $TargetUci.$ServerName.RootPasswordAuth=$RootPasswordAuth
[ -n "$RootLogin" ] && uci -q set $TargetUci.$ServerName.RootLogin=$RootLogin
[ -n "$Interface" ] && uci -q set $TargetUci.$ServerName.Interface=$Interface
[ -n "$SSHKeepAlive" ] && uci -q set $TargetUci.$ServerName.SSHKeepAlive=$SSHKeepAlive
[ -n "$IdleTimeout" ] && uci -q set $TargetUci.$ServerName.IdleTimeout=$IdleTimeout
[ -n "$MaxAuthTries" ] && uci -q set $TargetUci.$ServerName.MaxAuthTries=$MaxAuthTries
[ -n "$enable" ] && uci -q set $TargetUci.$ServerName.enable=$enable
# keep a list of sshmngr sections
# this will be compared with backend sections later
# so that extra sections can be deleted
SSHMNGR_SECTIONS="${SSHMNGR_SECTIONS} $ServerName"
}
# if a section has been deleted from sshmngr UCI
# then we cannot detect which section has been deleted by looping on sshmngr UCI
# so loop on backend UCI and delete sections that are not present in sshmngr UCI
remove_extra_sections()
{
local TargetUci="$1"
local TargetSectionType="$2"
local BbfSection=""
local TargetSection=""
# get a list of sections present in backend uci
TargetSections="$(uci -qX show $TargetUci | awk -F. '{print $2}' | sort -u | grep -vF "=${TargetSectionType}")"
for TargetSection in $TargetSections; do
# if someone does not want sshmngr to over-write a section
# they can set option BbfSection to 0 in the BACKEND UCI
BbfSection="$(uci -q get $TargetUci.$TargetSection.BbfSection)"
if [ "$BbfSection" == "1" ] || [ "$BbfSection" == "true" ]; then
if ! echo "$SSHMNGR_SECTIONS" | grep -q $TargetSection; then
uci -q delete $TargetUci.$TargetSection
fi
fi
done
}
configure_ssh()
{
# remove temp UCI
rm -rf "$TEMP_UCI_PATH"/$CONFIG 2>/dev/null
mkdir -p "$TEMP_UCI_PATH"
touch "$TEMP_UCI_PATH"/$CONFIG
local TargetUci="$CONFIG"
local CurrentUci="sshmngr"
local TargetSectionType="$CONFIG"
local CurrentSectionType="server"
# read all sshmngr server sections and then apply them to $CONFIG UCI
config_load sshmngr
config_foreach handle_server_section server
# if this is the first time sshmngr is running
if [ ! -f /etc/sshmngr/first_run_flag ]; then
# create first_run_flag
mkdir -p /etc/sshmngr/
echo "0" > /etc/sshmngr/first_run_flag
# when sshmngr runs for the first time
# it enforces its own UCI on the backend
# so, remove TargetUci
rm /etc/config/$TargetUci
fi
uci -c "$TEMP_UCI_PATH" commit $CONFIG
# if TargetUci is not present then create it
[ -f /etc/config/$TargetUci ] || touch /etc/config/$TargetUci
cp "$TEMP_UCI_PATH"/$CONFIG /etc/config/$CONFIG
# read all current sections and then apply them to target
config_load "$CurrentUci"
config_foreach handle_server_section "$CurrentSectionType" "$TargetSectionType" "$TargetUci"
/etc/init.d/$CONFIG reload
remove_extra_sections "$TargetUci" "$TargetSectionType"
# do not use single quotes, that gives error
ubus call uci commit "{\"config\":\"$TargetUci\"}"
}