Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support Reset factory #14105

Merged
merged 12 commits into from
Jul 11, 2023
8 changes: 8 additions & 0 deletions build_debian.sh
Original file line number Diff line number Diff line change
Expand Up @@ -596,6 +596,14 @@ export built_by="$USER@$BUILD_HOSTNAME"
export sonic_os_version="${SONIC_OS_VERSION}"
j2 files/build_templates/sonic_version.yml.j2 | sudo tee $FILESYSTEM_ROOT/etc/sonic/sonic_version.yml

# Default users info
export password_expire="$( [[ "$CHANGE_DEFAULT_PASSWORD" == "y" ]] && echo true || echo false )"
export username="${USERNAME}"
export password="$(sudo grep ^${USERNAME} $FILESYSTEM_ROOT/etc/shadow | cut -d: -f2)"
j2 files/build_templates/default_users.json.j2 | sudo tee $FILESYSTEM_ROOT/etc/sonic/default_users.json
Copy link
Collaborator

@qiluo-msft qiluo-msft Jul 3, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

default_users.json.j2

Please check my comment on HLD PR: sonic-net/SONiC#1231 (review) #Closed

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please see my answer: sonic-net/SONiC#1231 (comment)

sudo LANG=c chroot $FILESYSTEM_ROOT chmod 600 /etc/sonic/default_users.json
Mohammedz93 marked this conversation as resolved.
Show resolved Hide resolved
sudo LANG=c chroot $FILESYSTEM_ROOT chown root:shadow /etc/sonic/default_users.json

## Copy over clean-up script
sudo cp ./files/scripts/core_cleanup.py $FILESYSTEM_ROOT/usr/bin/core_cleanup.py

Expand Down
8 changes: 8 additions & 0 deletions files/build_templates/default_users.json.j2
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{%- set users_dict = {
username: {
"password": password,
"expire": password_expire
}
}
-%}
{{ users_dict | tojson(indent=4) }}
7 changes: 6 additions & 1 deletion files/build_templates/sonic_debian_extension.j2
Original file line number Diff line number Diff line change
Expand Up @@ -607,12 +607,17 @@ sudo bash -c "echo enabled=false > $FILESYSTEM_ROOT/etc/sonic/updategraph.conf"
# Generate initial SONiC configuration file
j2 files/build_templates/init_cfg.json.j2 | sudo tee $FILESYSTEM_ROOT/etc/sonic/init_cfg.json

# Copy config-setup script and service file
# Copy config-setup script, conf file and service file
j2 files/build_templates/config-setup.service.j2 | sudo tee $FILESYSTEM_ROOT_USR_LIB_SYSTEMD_SYSTEM/config-setup.service
sudo cp $IMAGE_CONFIGS/config-setup/config-setup $FILESYSTEM_ROOT/usr/bin/config-setup
sudo mkdir -p $FILESYSTEM_ROOT/etc/config-setup
sudo cp $IMAGE_CONFIGS/config-setup/config-setup.conf $FILESYSTEM_ROOT/etc/config-setup/config-setup.conf
Copy link
Collaborator

@qiluo-msft qiluo-msft May 15, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

config-setup.conf

Let's remove all the filename if it is the same as source file. There is extreme corner case that there is a folder with the same name existing before copy, and the behavior is not expected. #Closed

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I’m not sure if I am understanding correctly.
What do you mean by removing the filename ?
"/etc/config-setup" is a folder that was first introduced by by feature: SONiC-config-setup
It wasn't used until now but I think that this is correct path for locating the conf file
If the concern is that conf file name and folder name have the same prefix, we can change it to "config_setup.conf"

echo "config-setup.service" | sudo tee -a $GENERATED_SERVICE_FILE
sudo LANG=C chroot $FILESYSTEM_ROOT systemctl enable config-setup.service

# Copy reset-factory script and service
sudo cp $IMAGE_CONFIGS/reset-factory/reset-factory $FILESYSTEM_ROOT/usr/bin/reset-factory

# Add delayed tacacs application service
sudo cp files/build_templates/tacacs-config.timer $FILESYSTEM_ROOT_USR_LIB_SYSTEMD_SYSTEM/
echo "tacacs-config.timer" | sudo tee -a $GENERATED_SERVICE_FILE
Expand Down
58 changes: 53 additions & 5 deletions files/image_config/config-setup/config-setup
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ CONFIG_SETUP_VAR_DIR=/var/lib/config-setup
CONFIG_SETUP_PRE_MIGRATION_FLAG=${CONFIG_SETUP_VAR_DIR}/pending_pre_migration
CONFIG_SETUP_POST_MIGRATION_FLAG=${CONFIG_SETUP_VAR_DIR}/pending_post_migration
CONFIG_SETUP_INITIALIZATION_FLAG=${CONFIG_SETUP_VAR_DIR}/pending_initialization
CONFIG_SETUP_CONF=/etc/config-setup/config-setup.conf

TACACS_JSON_BACKUP=tacacs.json

Expand All @@ -56,17 +57,31 @@ usage()
EOF
}

# Factory command usage and help
usage_factory()
Copy link
Collaborator

@qiluo-msft qiluo-msft May 15, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

usage_factory

Could you merge this function to above usage()? #Closed

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the comments.

usage_factory() is used for "config-setup factory" help command.
while useage() is used for "config-setup" help command.
Do you mean by merge, to combine the two functions into one function with parameter ?
if so, why do you think it is necessary ?

{
cat << EOF
Usage: config-setup factory < keep-basic >

Create factory default configuration and save it to
to ${CONFIG_DB_JSON}.

keep-basic - Preserves basic configurations only.
EOF
}

# run given script
run_hook() {
local script="$1"
local script_param="$2"
local exit_status=0

if [ -f $script ]; then
# Check hook for syntactical correctness before executing it
/bin/bash -n $script
/bin/bash -n $script $script_param
exit_status=$?
if [ "$exit_status" -eq 0 ]; then
. $script
. $script $script_param
fi
exit_status=$?
fi
Expand All @@ -82,6 +97,7 @@ run_hook() {
run_hookdir() {
local dir="$1"
local progress_file="$2"
local script_param="$3"
local exit_status=0

if [ -d "$dir" ]; then
Expand All @@ -94,7 +110,7 @@ run_hookdir() {
fi

for script in $SCRIPT_LIST; do
run_hook $script
run_hook $script $script_param
exit_status=$((exit_status|$?))
script_name=$(basename $script)
sed -i "/$script_name/d" $progress_file
Expand Down Expand Up @@ -215,10 +231,33 @@ generate_config()
if [ "$1" = "ztp" ]; then
/usr/lib/ztp/ztp-profile.sh create ${DEST_FILE}
elif [ "$1" = "factory" ]; then
FACTORY_TYPE=$3
rv=1

if [ "$FACTORY_TYPE" = "keep-basic" ]; then
TMP_FILE="/tmp/tmp_keep_basic.$$.json"
# Verify the DEST_FILE exists and KEEP_BASIC_TABLES was defined in CONFIG_SETUP_CONF
if [ ! -f ${DEST_FILE} ] || [ -z "${KEEP_BASIC_TABLES}" ]; then
# Create empty valid json file
echo {} > ${TMP_FILE}
else
# Create filtered json file with keep-basic tables only
jq 'with_entries(select([.key] | inside($tables)))' --argjson tables "$KEEP_BASIC_TABLES" ${DEST_FILE} > ${TMP_FILE}
fi
# Create factory default
sonic-cfggen -H -k ${HW_KEY} --preset ${DEFAULT_PRESET} > ${DEST_FILE}
rv=$?
if [ $rv -ne 0 ]; then
rm -f ${TMP_FILE}
return $rv
fi
# Merge factory default config with filtered json
jq --indent 4 -s '.[0] * .[1]' ${DEST_FILE} ${TMP_FILE} > tmp.$$.json && mv tmp.$$.json ${DEST_FILE}
rm -f ${TMP_FILE}
fi

# Execute config initialization hooks
run_hookdir ${FACTORY_DEFAULT_HOOKS} ${CONFIG_SETUP_INITIALIZATION_FLAG}
run_hookdir ${FACTORY_DEFAULT_HOOKS} ${CONFIG_SETUP_INITIALIZATION_FLAG} ${FACTORY_TYPE}

# Use preset defined in default_sku
if [ ! -e ${DEST_FILE} ]; then
Expand Down Expand Up @@ -402,6 +441,9 @@ boot_config()
# read SONiC immutable variables
[ -f /etc/sonic/sonic-environment ] && . /etc/sonic/sonic-environment

# read config-setup.conf
[ -f $CONFIG_SETUP_CONF ] && . $CONFIG_SETUP_CONF

### Execution starts here ###
PLATFORM=${PLATFORM:-`sonic-cfggen -H -v DEVICE_METADATA.localhost.platform`}
# Parse the device specific asic conf file, if it exists
Expand All @@ -426,7 +468,13 @@ fi

# Process factory default configuration creation request
if [ "$CMD" = "factory" ]; then
generate_config factory ${CONFIG_DB_JSON}
FACTORY_TYPE=$2
if [ "$FACTORY_TYPE" = "help" ] || [ "$FACTORY_TYPE" = "-h" ] || \
[ "$FACTORY_TYPE" = "--help" ]; then
usage_factory
exit 1
fi
generate_config factory ${CONFIG_DB_JSON} ${FACTORY_TYPE}
fi

# Take a backup of current configuration
Expand Down
4 changes: 4 additions & 0 deletions files/image_config/config-setup/config-setup.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# conf file for config-setup
# file: /etc/config-setup/config-setup.conf
#
KEEP_BASIC_TABLES='["MGMT_PORT","MGMT_INTERFACE","MGMT_VRF_CONFIG","PASSW_HARDENING"]'
197 changes: 197 additions & 0 deletions files/image_config/reset-factory/reset-factory
Original file line number Diff line number Diff line change
@@ -0,0 +1,197 @@
#!/bin/bash
###########################################################################
# SONIC Factory reset script #
# /usr/bin/reset-factory #
# This script is used to reset the system to factory settings. #
# It creates factory default configuration and save it to config_db.json. #
# Also, it clear logs, tech-support, reboot-cause files, warmboot files, #
# docker containers non-default users, users history files and #
# home directories. #
###########################################################################

# Initialize constants
CONFIG_DB_JSON=/etc/sonic/config_db.json
DEFAULT_USERS_FILE=/etc/sonic/default_users.json
PERMLOG=/var/log/systemlog
SONIC_VERSION=$(sonic-cfggen -y /etc/sonic/sonic_version.yml -v build_version)
SONIC_OVERLAY_UPPERDIR="/host/image-$SONIC_VERSION/rw/etc/sonic"

SERVICES_STOPPED=0
trap "error_cleanup" HUP INT QUIT PIPE TERM

# Command usage and help
usage()
{
cat << EOF
Usage: reset-factory < keep-all-config | only-config | keep-basic >

Create factory default configuration and save it to
to ${CONFIG_DB_JSON}.
Clears logs, system files and reboot the system.

Default - Reset configurations to factory default. Logs and files will be deleted.
keep-all-config - Preserves all configurations after boot. Logs and files will be deleted.
only-config - Reset configurations to factory default. Logs and files will be preserved.
keep-basic - Preserves basic configurations only after boot. Logs and files will be deleted.
EOF
}

run_reboot()
{
reboot
# If for any reason we reach this code, then force reboot
rc=$?
if [ $rc -ne 0 ]; then
# Force reboot
reboot -f
fi
}

error_cleanup()
{
if [ ! -z "${TEMP_CFG}" ]; then
# Recover config_db.json file
mv ${TEMP_CFG} ${CONFIG_DB_JSON}
fi

if [ $SERVICES_STOPPED -eq 0 ]; then
ERRMSG="reset-factory: halted with error before stopping critical services; exiting"
logger $ERRMSG
echo $ERRMSG
exit 1
else
ERRMSG="reset-factory: halted with error after stopping critical services; rebooting"
logger $ERRMSG
echo $ERRMSG
run_reboot
fi
}

# Restore original /etc/sonic folder by clearing the folder in overlayfs upperdir
clear_sonic_dir()
{
EXCLUDE_LIST="${CONFIG_DB_JSON}\|/etc/sonic/sonic-environment"
find $SONIC_OVERLAY_UPPERDIR -type f | grep -ve ${EXCLUDE_LIST} | xargs rm -rf
# remount root
mount -o remount /
}

# Get list of defaults users names and passwords from DEFAULT_USERS_FILE
# Delete non-default users and restore default password of default users
reset_users()
{
if [ ! -f "${DEFAULT_USERS_FILE}" ]; then
echo "Error: Failed to get default users information"
return
fi
# Get default user accounts
default_users=$(jq -r '. | keys[]' $DEFAULT_USERS_FILE)
EXCLUDE_LIST=$(echo $default_users | tr ' ' '|')
# Get non-default user accounts
other_users=$(getent passwd | awk -F: '($3>=1000 && $3<=60000) {print $1}' | grep -E -v $EXCLUDE_LIST)
echo "Remove non-default users"
for user in ${other_users[@]}
do
# avoid printing home directory and mail spool errors
userdel -rf $user 2> /dev/null
done
echo "Restore default users passwords"
for user in ${default_users[@]}
do
# Restore default password
user_pass=$(jq -r '.[$user].password' --arg user "${user}" $DEFAULT_USERS_FILE)
echo "$user:$user_pass" | chpasswd -e
# Check if we need to expire password
expire=$(jq -r '.[$user].expire' --arg user "${user}" $DEFAULT_USERS_FILE)
[ "${expire}" == "true" ] && passwd -e ${user}
done
}

# Only root can run reset factory
if [ $UID != 0 ]; then
echo "You must be root to reset system to factory settings"
exit 1
fi

CMD=$1
FACTORY_TYPE=

if [ "$CMD" = "keep-all-config" ] || [ "$CMD" = "only-config" ] || \
[ "$CMD" = "keep-basic" ] || [ -z "$CMD" ]; then
FACTORY_TYPE=$CMD
else
usage
exit 1
fi

SERVICES_STOPPED=1
echo "Stop critical services"
monit unmonitor container_checker
systemctl stop sonic.target --job-mode replace-irreversibly

rc=$?
if [ $rc -ne 0 ]; then
error_cleanup
fi

DATE=$(date "+%Y/%m/%d %H:%M:%S")
HOSTNAME=$(hostname | sed 's/\./ /' | awk '{print $1}')
printf "%s %s reset-factory: resetting system to factory defaults\n" "$DATE" "$HOSTNAME" >> $PERMLOG

# Backup and delete config_db.json
TEMP_CFG="/tmp/temp_config_db.$$"
cp ${CONFIG_DB_JSON} ${TEMP_CFG}
if [ "$FACTORY_TYPE" != "keep-basic" ] && [ "$FACTORY_TYPE" != "keep-all-config" ]; then
rm -f ${CONFIG_DB_JSON}
fi

echo "Call config-setup factory"
config-setup factory $FACTORY_TYPE
rc=$?
if [ $rc -ne 0 ]; then
error_cleanup
fi

if [ "$FACTORY_TYPE" != "only-config" ]; then

if [ "$FACTORY_TYPE" != "keep-basic" ]; then

# Delete non-default users and restore default users passwords
reset_users

echo "Delete bash, python and vim history files"
find /home /root -type f -name ".bash_history" -o -name ".python_history" -o -name ".viminfo" | xargs rm -rf

echo "Delete any non-dotfiles in users home directories"
find /home/ /root -type f ! -iname ".*" -delete
fi

echo "Remove all docker containers except the database"
database_pattern=($(docker ps -a -q -f "name=database" | paste -sd '|' -))
docker rm -f $(docker ps -a -q | egrep -v ${database_pattern}) > /dev/null

echo "Clear sonic directory"
clear_sonic_dir

echo "Clear warmboot folder"
find /host/warmboot/ -type f -delete

echo "Delete reboot-cause files and symlinks"
find /host/reboot-cause/ -type l,f -delete

echo "Delete tech-support files"
rm -rf /var/dump/*

echo "Delete logs files"
find /var/log/ -type f ! -iname "wtmp" ! -iname "btmp" ! -iname "lastlog" ! -iname "systemlog" -delete

# Clear wtmp, utmp and lastlog files
rm -rf /var/log/btmp.*
cat /dev/null > /var/log/btmp
rm -rf /var/log/wtmp.*
cat /dev/null > /var/log/wtmp
rm -rf /var/log/lastlog.*
cat /dev/null > /var/log/lastlog
fi

run_reboot