diff --git a/files/build_templates/init_cfg.json.j2 b/files/build_templates/init_cfg.json.j2 index 1d1c1817b49d..e55bc1194e02 100644 --- a/files/build_templates/init_cfg.json.j2 +++ b/files/build_templates/init_cfg.json.j2 @@ -181,6 +181,11 @@ "logout": "" } }, + "LOCAL_USERS_PASSWORDS_RESET": { + "global": { + "state": "disabled" + } + }, "SYSTEM_DEFAULTS" : { {%- if include_mux == "y" %} "mux_tunnel_egress_acl": { diff --git a/files/build_templates/sonic_debian_extension.j2 b/files/build_templates/sonic_debian_extension.j2 index 2217859dd827..4c743e52de6f 100644 --- a/files/build_templates/sonic_debian_extension.j2 +++ b/files/build_templates/sonic_debian_extension.j2 @@ -645,6 +645,15 @@ sudo cp $IMAGE_CONFIGS/hostname/hostname-config.service $FILESYSTEM_ROOT_USR_LIB echo "hostname-config.service" | sudo tee -a $GENERATED_SERVICE_FILE sudo cp $IMAGE_CONFIGS/hostname/hostname-config.sh $FILESYSTEM_ROOT/usr/bin/ +{% if enable_local_users_passwords_reset == "y" %} +# Copy local-users-passwords-reset configuration scripts +sudo cp $IMAGE_CONFIGS/local-users-passwords-reset/local-users-passwords-reset.service $FILESYSTEM_ROOT_USR_LIB_SYSTEMD_SYSTEM +echo "local-users-passwords-reset.service" | sudo tee -a $GENERATED_SERVICE_FILE +sudo cp $IMAGE_CONFIGS/local-users-passwords-reset/local-users-passwords-reset.py $FILESYSTEM_ROOT/usr/bin/ +# Set execute permissions only +sudo chmod 100 $FILESYSTEM_ROOT/usr/bin/local-users-passwords-reset.py +{% endif %} + # Copy banner configuration scripts sudo cp $IMAGE_CONFIGS/bannerconfig/banner-config.service $FILESYSTEM_ROOT_USR_LIB_SYSTEMD_SYSTEM echo "banner-config.service" | sudo tee -a $GENERATED_SERVICE_FILE diff --git a/files/image_config/local-users-passwords-reset/local-users-passwords-reset.py b/files/image_config/local-users-passwords-reset/local-users-passwords-reset.py new file mode 100644 index 000000000000..aeec7f2d3392 --- /dev/null +++ b/files/image_config/local-users-passwords-reset/local-users-passwords-reset.py @@ -0,0 +1,62 @@ +#!/usr/bin/env python + +import syslog +from swsscommon.swsscommon import ConfigDBConnector, DBConnector +from swsscommon import swsscommon + + +STATE_DB = "STATE_DB" + + +def get_platform_local_users_passwords_reset(): + try: + from sonic_platform.local_users_passwords_reset import LocalUsersConfigurationReset + local_users_password_reset_class = LocalUsersConfigurationReset() + except ImportError: + syslog.syslog(syslog.LOG_WARNING, "LocalUsersConfigurationReset: sonic_platform package not installed. Unable to find platform local users passwords reset implementation") + raise Exception('Local users passwords reset implementation is not defined') + + return local_users_password_reset_class + + +class LocalUsersConfigurationResetService: + def __init__(self): + state_db_conn = DBConnector(STATE_DB, 0) + # Wait if the Warm/Fast boot is in progress + if swsscommon.RestartWaiter.isAdvancedBootInProgress(state_db_conn): + swsscommon.RestartWaiter.waitAdvancedBootDone() + + self.config_db = ConfigDBConnector() + self.config_db.connect(wait_for_init=True, retry_on=True) + syslog.syslog(syslog.LOG_INFO, 'ConfigDB connect success') + + def get_feature_state(self): + ''' + Check if the feature is enabled by reading the redis table + ''' + table = self.config_db.get_table(swsscommon.CFG_LOCAL_USERS_PASSWORDS_RESET) + if table: + state = table.get('global', {}).get('state') + return True if state == 'enabled' else False + + return False + + def start(self): + ''' + If the feature is enabled then reset the password's using the platform + specific implementation + ''' + local_users_password_reset = get_platform_local_users_passwords_reset() + feature_enabled = self.get_feature_state() + syslog.syslog(syslog.LOG_INFO, 'Feature is {}'.format('enabled' if feature_enabled else 'disabled')) + should_trigger = local_users_password_reset.should_trigger() + if should_trigger and feature_enabled: + local_users_password_reset.start() + + +def main(): + LocalUsersConfigurationResetService().start() + + +if __name__ == "__main__": + main() diff --git a/files/image_config/local-users-passwords-reset/local-users-passwords-reset.service b/files/image_config/local-users-passwords-reset/local-users-passwords-reset.service new file mode 100644 index 000000000000..849b87cdd002 --- /dev/null +++ b/files/image_config/local-users-passwords-reset/local-users-passwords-reset.service @@ -0,0 +1,14 @@ +[Unit] +Description=Update Local users' passwords config based on configdb +Requires=config-setup.service +After=config-setup.service +Before=systemd-logind.service sshd.service getty.target serial-getty@ttyS0.service + + +[Service] +Type=oneshot +RemainAfterExit=no +ExecStart=/usr/bin/local-users-passwords-reset.py + +[Install] +WantedBy=sonic.target \ No newline at end of file diff --git a/platform/mellanox/mlnx-platform-api/sonic_platform/local_users_passwords_reset.py b/platform/mellanox/mlnx-platform-api/sonic_platform/local_users_passwords_reset.py new file mode 100644 index 000000000000..756533112cb5 --- /dev/null +++ b/platform/mellanox/mlnx-platform-api/sonic_platform/local_users_passwords_reset.py @@ -0,0 +1,51 @@ +# +# Copyright (c) 2019-2024 NVIDIA CORPORATION & AFFILIATES. +# Apache-2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +############################################################################# +# Mellanox +# +# Module contains an implementation of SONiC Platform Base API and +# provides the local users' passwords reset functionality implementation. +# +############################################################################# + +try: + from sonic_platform_base.local_users_passwords_reset_base import LocalUsersConfigurationResetBase + from sonic_py_common.logger import Logger + from . import utils +except ImportError as e: + raise ImportError (str(e) + "- required module not found") + + +# Global logger class instance +logger = Logger() + + +LONG_REBOOT_PRESS_FILEPATH = '/var/run/hw-management/system/reset_long_pb' + + +class LocalUsersConfigurationReset(LocalUsersConfigurationResetBase): + def should_trigger(self): + ''' + The condition for triggering passwords reset is by checking if the + reboot cause was a long reboot press. + ''' + try: + status = utils.read_int_from_file(LONG_REBOOT_PRESS_FILEPATH, raise_exception=True) + return True if status == 1 else False + except (ValueError, IOError) as e: + logger.log_error(f"Failed to read long reboot press from {LONG_REBOOT_PRESS_FILEPATH} - {e}") + return False diff --git a/platform/mellanox/mlnx-platform-api/tests/test_local_users_passwords_reset.py b/platform/mellanox/mlnx-platform-api/tests/test_local_users_passwords_reset.py new file mode 100644 index 000000000000..98265504aabe --- /dev/null +++ b/platform/mellanox/mlnx-platform-api/tests/test_local_users_passwords_reset.py @@ -0,0 +1,44 @@ +# +# Copyright (c) 2020-2024 NVIDIA CORPORATION & AFFILIATES. +# Apache-2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +import os +import pytest +import sys +from mock import patch, mock_open + +test_path = os.path.dirname(os.path.abspath(__file__)) +modules_path = os.path.dirname(test_path) +sys.path.insert(0, modules_path) + +from sonic_platform.local_users_passwords_reset import LocalUsersConfigurationReset + + +class TestLocalUsersConfigurationReset: + @patch('sonic_platform.utils.read_int_from_file') + def test_should_trigger_method(self, mock_read_int): + ''' + Validate should_trigger() method + ''' + local_users_reset_class = LocalUsersConfigurationReset() + + mock_read_int.return_value = int(1) + assert local_users_reset_class.should_trigger() == True + mock_read_int.return_value = int(0) + assert local_users_reset_class.should_trigger() == False + mock_read_int.return_value = int(2) + assert local_users_reset_class.should_trigger() == False + mock_read_int.side_effect = ValueError() + assert local_users_reset_class.should_trigger() == False diff --git a/rules/config b/rules/config index 3b4fd5851a3b..a50bfb26d31e 100644 --- a/rules/config +++ b/rules/config @@ -54,6 +54,9 @@ DEFAULT_PASSWORD = YourPaSsWoRd # ENABLE_ZTP - installs Zero Touch Provisioning support. # ENABLE_ZTP = y +# ENABLE_LOCAL_USERS_PASSWORDS_RESET - enable local users' passwords reset during init on switch +ENABLE_LOCAL_USERS_PASSWORDS_RESET ?= n + # INCLUDE_PDE - Enable platform development enviroment # INCLUDE_PDE = y # SHUTDOWN_BGP_ON_START - if set to y all bgp sessions will be in admin down state when diff --git a/slave.mk b/slave.mk index f3d9ad3e237e..68d77fbb3d23 100644 --- a/slave.mk +++ b/slave.mk @@ -429,6 +429,7 @@ $(info "ENABLE_ORGANIZATION_EXTENSIONS" : "$(ENABLE_ORGANIZATION_EXTENSIONS)") $(info "HTTP_PROXY" : "$(HTTP_PROXY)") $(info "HTTPS_PROXY" : "$(HTTPS_PROXY)") $(info "NO_PROXY" : "$(NO_PROXY)") +$(info "ENABLE_LOCAL_USERS_PASSWORDS_RESET" : "$(ENABLE_LOCAL_USERS_PASSWORDS_RESET)") $(info "ENABLE_ZTP" : "$(ENABLE_ZTP)") $(info "INCLUDE_PDE" : "$(INCLUDE_PDE)") $(info "SONIC_DEBUGGING_ON" : "$(SONIC_DEBUGGING_ON)") @@ -1430,6 +1431,7 @@ $(addprefix $(TARGET_PATH)/, $(SONIC_INSTALLERS)) : $(TARGET_PATH)/% : \ export sonic_asic_platform="$(patsubst %-$(CONFIGURED_ARCH),%,$(CONFIGURED_PLATFORM))" export enable_organization_extensions="$(ENABLE_ORGANIZATION_EXTENSIONS)" export enable_ztp="$(ENABLE_ZTP)" + export enable_local_users_passwords_reset="$(ENABLE_LOCAL_USERS_PASSWORDS_RESET)" export include_teamd="$(INCLUDE_TEAMD)" export include_router_advertiser="$(INCLUDE_ROUTER_ADVERTISER)" export sonic_su_dev_signing_key="$(SECURE_UPGRADE_DEV_SIGNING_KEY)" diff --git a/src/sonic-yang-models/setup.py b/src/sonic-yang-models/setup.py index 60dba6c410aa..3985fa241b30 100644 --- a/src/sonic-yang-models/setup.py +++ b/src/sonic-yang-models/setup.py @@ -87,6 +87,7 @@ def run(self): './yang-models/sonic-auto_techsupport.yang', './yang-models/sonic-bgp-bbr.yang', './yang-models/sonic-banner.yang', + './yang-models/sonic-local-users-passwords-reset.yang', './yang-models/sonic-bgp-common.yang', './yang-models/sonic-bgp-device-global.yang', './yang-models/sonic-bgp-global.yang', @@ -210,6 +211,7 @@ def run(self): './yang-models/sonic-smart-switch.yang',]), ('cvlyang-models', ['./cvlyang-models/sonic-acl.yang', './cvlyang-models/sonic-banner.yang', + './cvlyang-models/sonic-local-users-passwords-reset.yang', './cvlyang-models/sonic-bgp-common.yang', './cvlyang-models/sonic-bgp-global.yang', './cvlyang-models/sonic-bgp-monitor.yang', diff --git a/src/sonic-yang-models/tests/files/sample_config_db.json b/src/sonic-yang-models/tests/files/sample_config_db.json index 6f7e90c229ff..078e40484a1b 100644 --- a/src/sonic-yang-models/tests/files/sample_config_db.json +++ b/src/sonic-yang-models/tests/files/sample_config_db.json @@ -2760,6 +2760,11 @@ "midplane_interface": "dpu1" } }, + "LOCAL_USERS_PASSWORDS_RESET": { + "global": { + "state": "disabled" + } + }, "XCVRD_LOG": { "Y_CABLE": { "log_verbosity": "notice" @@ -2794,6 +2799,11 @@ } } }, + "LOCAL_USERS_PASSWORDS_RESET": { + "global": { + "state": "disabled" + } + }, "SAMPLE_CONFIG_DB_UNKNOWN": { "UNKNOWN_TABLE": { "Error": "This Table is for testing, This Table does not have YANG models." diff --git a/src/sonic-yang-models/tests/yang_model_tests/tests/local-users-passwords-reset.json b/src/sonic-yang-models/tests/yang_model_tests/tests/local-users-passwords-reset.json new file mode 100644 index 000000000000..af4ff540b06c --- /dev/null +++ b/src/sonic-yang-models/tests/yang_model_tests/tests/local-users-passwords-reset.json @@ -0,0 +1,5 @@ +{ + "LOCAL_USERS_PASSWORDS_RESET_TEST_STATE": { + "desc": "Configure Local users' passwords reset feature state." + } +} \ No newline at end of file diff --git a/src/sonic-yang-models/tests/yang_model_tests/tests_config/local-users-passwords-reset.json b/src/sonic-yang-models/tests/yang_model_tests/tests_config/local-users-passwords-reset.json new file mode 100644 index 000000000000..f48199da3caa --- /dev/null +++ b/src/sonic-yang-models/tests/yang_model_tests/tests_config/local-users-passwords-reset.json @@ -0,0 +1,11 @@ +{ + "LOCAL_USERS_PASSWORDS_RESET_TEST_STATE": { + "sonic-local-users-passwords-reset:sonic-local-users-passwords-reset": { + "sonic-local-users-passwords-reset:LOCAL_USERS_PASSWORDS_RESET": { + "global": { + "state": "enabled" + } + } + } + } +} \ No newline at end of file diff --git a/src/sonic-yang-models/yang-models/sonic-local-users-passwords-reset.yang b/src/sonic-yang-models/yang-models/sonic-local-users-passwords-reset.yang new file mode 100644 index 000000000000..5b418069b288 --- /dev/null +++ b/src/sonic-yang-models/yang-models/sonic-local-users-passwords-reset.yang @@ -0,0 +1,27 @@ +module sonic-local-users-passwords-reset { + yang-version 1.1; + namespace "http://github.com/sonic-net/local-users-passwords-reset"; + prefix local_users_passwords_reset; + + import sonic-types { + prefix stypes; + } + + description "LOCAL_USERS_PASSWORDS_RESET YANG Module for SONiC-based OS"; + revision 2024-01-04 { + description "First Revision"; + } + + container sonic-local-users-passwords-reset { + container LOCAL_USERS_PASSWORDS_RESET { + description "LOCAL_USERS_PASSWORDS_RESET part of config_db.json"; + container global { + leaf state { + type stypes:admin_mode; + description "Local users' passwords reset feature state"; + default disabled; + } + } /* end of container global */ + } /* end of container LOCAL_USERS_PASSWORDS_RESET */ + } /* end of top level container */ +} \ No newline at end of file