Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion build_debian.sh
Original file line number Diff line number Diff line change
Expand Up @@ -320,6 +320,7 @@ fi
## Note: ca-certificates is needed for easy_install
## Note: don't install python-apt by pip, older than Debian repo one
## Note: fdisk and gpg are needed by fwutil
## Note: whois is needed for mkpasswd by sonic-utilities
sudo LANG=C DEBIAN_FRONTEND=noninteractive chroot $FILESYSTEM_ROOT apt-get -y install \
file \
ifmetric \
Expand Down Expand Up @@ -386,7 +387,8 @@ sudo LANG=C DEBIAN_FRONTEND=noninteractive chroot $FILESYSTEM_ROOT apt-get -y in
wireless-regdb \
ethtool \
zstd \
nvme-cli
nvme-cli \
whois

sudo cp files/initramfs-tools/pzstd $FILESYSTEM_ROOT/etc/initramfs-tools/hooks/pzstd
sudo chmod +x $FILESYSTEM_ROOT/etc/initramfs-tools/hooks/pzstd
Expand Down
2 changes: 2 additions & 0 deletions files/build_templates/sonic_debian_extension.j2
Original file line number Diff line number Diff line change
Expand Up @@ -346,6 +346,8 @@ install_deb_package {{deb}}
# Install sonic-db-cli
install_deb_package $debs_path/sonic-db-cli_*.deb

# Install SONiC host userd package (requires swss-common packages)
install_deb_package $debs_path/sonic-host-userd_*.deb

{% if include_system_eventd == "y" and build_reduce_image_size != "y" %}
# Install sonic-rsyslog-plugin
Expand Down
1 change: 1 addition & 0 deletions platform/vs/docker-sonic-vs.mk
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ $(DOCKER_SONIC_VS)_DEPENDS += $(SYNCD_VS) \
$(LIBYANG_PY3) \
$(SONIC_UTILITIES_DATA) \
$(SONIC_HOST_SERVICES_DATA) \
$(SONIC_HOST_USERD) \
$(SYSMGR)

$(DOCKER_SONIC_VS)_PYTHON_WHEELS += $(SONIC_PY_COMMON_PY3) \
Expand Down
2 changes: 2 additions & 0 deletions platform/vs/docker-sonic-vs/Dockerfile.j2
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ RUN apt-get install -y net-tools \
iptables \
jq \
uuid-dev \
# For using mkpasswd by sonic-utilities
whois \
# For installing Python m2crypto package
# (these can be uninstalled after installation)
build-essential \
Expand Down
2 changes: 1 addition & 1 deletion rules/sonic-host-services.dep
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
SPATH := $($(SONIC_HOST_SERVICES_PY3)_SRC_PATH)
DEP_FILES := $(SONIC_COMMON_FILES_LIST) rules/sonic-host-services.mk rules/sonic-host-services.dep
DEP_FILES += $(SONIC_COMMON_BASE_FILES_LIST)
SMDEP_FILES := $(addprefix $(SPATH)/,$(shell git -C $(SPATH) ls-files | grep -v ^data))
SMDEP_FILES := $(addprefix $(SPATH)/,$(shell git -C $(SPATH) ls-files | grep -v ^data | grep -v ^userd))

$(SONIC_HOST_SERVICES_PY3)_CACHE_MODE := GIT_CONTENT_SHA
$(SONIC_HOST_SERVICES_PY3)_DEP_FLAGS := $(SONIC_COMMON_FLAGS_LIST)
Expand Down
8 changes: 8 additions & 0 deletions rules/sonic-host-userd.dep
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
SPATH := $($(SONIC_HOST_USERD)_SRC_PATH)
DEP_FILES := $(SONIC_COMMON_FILES_LIST) rules/sonic-host-userd.mk rules/sonic-host-userd.dep
DEP_FILES += $(SONIC_COMMON_BASE_FILES_LIST)
DEP_FILES += $(addprefix $(SPATH)/,$(shell git -C $(SPATH) ls-files))

$(SONIC_HOST_USERD)_CACHE_MODE := GIT_CONTENT_SHA
$(SONIC_HOST_USERD)_DEP_FLAGS := $(SONIC_COMMON_FLAGS_LIST)
$(SONIC_HOST_USERD)_DEP_FILES := $(DEP_FILES)
16 changes: 16 additions & 0 deletions rules/sonic-host-userd.mk
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# SONiC host userd binary package

SONIC_HOST_USERD = sonic-host-userd_1.0-1_$(CONFIGURED_ARCH).deb
$(SONIC_HOST_USERD)_SRC_PATH = $(SRC_PATH)/sonic-host-services/userd
$(SONIC_HOST_USERD)_DEPENDS += $(LIBSWSSCOMMON_DEV)
$(SONIC_HOST_USERD)_RDEPENDS += $(SONIC_HOST_SERVICES_DATA) $(LIBSWSSCOMMON)
SONIC_DPKG_DEBS += $(SONIC_HOST_USERD)

# Debug symbols package
SONIC_HOST_USERD_DBG = sonic-host-userd-dbgsym_1.0-1_$(CONFIGURED_ARCH).deb
$(SONIC_HOST_USERD_DBG)_DEPENDS += $(SONIC_HOST_USERD)
$(SONIC_HOST_USERD_DBG)_RDEPENDS += $(SONIC_HOST_USERD)
$(eval $(call add_derived_package,$(SONIC_HOST_USERD),$(SONIC_HOST_USERD_DBG)))

# Add to debug source archive for debugging
DBG_SRC_ARCHIVE += sonic-host-services/userd
1 change: 1 addition & 0 deletions slave.mk
Original file line number Diff line number Diff line change
Expand Up @@ -1398,6 +1398,7 @@ $(addprefix $(TARGET_PATH)/, $(SONIC_INSTALLERS)) : $(TARGET_PATH)/% : \
$(SONIC_UTILITIES_DATA) \
$(SONIC_CTRMGRD_RS) \
$(SONIC_HOST_SERVICES_DATA) \
$(SONIC_HOST_USERD) \
$(BASH) \
$(BASH_TACPLUS) \
$(AUDISP_TACPLUS) \
Expand Down
1 change: 1 addition & 0 deletions src/sonic-yang-models/setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,7 @@
'sonic-smart-switch.yang',
'sonic-spanning-tree.yang',
'sonic-srv6.yang',
'sonic-user.yang',
]

class my_build_py(build_py):
Expand Down
7 changes: 7 additions & 0 deletions src/sonic-yang-models/tests/files/sample_config_db.json
Original file line number Diff line number Diff line change
Expand Up @@ -3111,6 +3111,13 @@
"DEBUG_4|DIP_LINK_LOCAL": {},
"DEBUG_4|SIP_LINK_LOCAL": {}
},
"LOCAL_USER": {
"admin": {
"role": "administrator",
"password_hash": "$y$j9T$mYVqMrB/TF29nNg.IK63H1$4k1Th1bqfrLXo50z3.KJvsKpffgD7NPMExVmDTrnUc8",
"enabled": "true"
}
},
"HIGH_FREQUENCY_TELEMETRY_PROFILE": {
"dummy": {
"stream_state": "disabled",
Expand Down
10 changes: 10 additions & 0 deletions src/sonic-yang-models/yang-models/sonic-device_metadata.yang
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,16 @@ module sonic-device_metadata {
}
}

leaf local_user_management {
type enumeration {
enum enabled;
enum disabled;
}
default disabled;
description "Enable or disable local user management feature.
When enabled, the system will manage local users through the LOCAL_USER table.";
}

leaf frr_mgmt_framework_config {
type boolean;
description "FRR configurations are handled by sonic-frr-mgmt-framework module when set to true,
Expand Down
102 changes: 102 additions & 0 deletions src/sonic-yang-models/yang-models/sonic-user.yang
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
module sonic-user {
yang-version 1.1;
namespace "http://github.com/sonic-net/sonic-user";
prefix "sonic-user";

description "SONIC User Management YANG Module";

revision 2025-09-12 {
description "Initial revision for declarative user management";
}

// Common typedef for user roles
typedef user-role {
type enumeration {
enum "administrator" {
description "Grants administrative privileges (e.g., member of sudo, docker, admin, redis groups).";
}
enum "operator" {
description "Grants operator-level (read-only or limited) privileges.";
}
}
description "User role that determines group memberships, privileges, and applicable security policies.";
}

// Top-level container for the User feature
container sonic-user {
description "Top-level container for local user management configuration";

container LOCAL_USER {
description "LOCAL_USER part of config_db.json";

list LOCAL_USER_LIST {
key "username";
description "List of declaratively managed local users.";

must "count(../LOCAL_USER_LIST[role='administrator' and (not(enabled) or enabled='true')]) >= 1" {
error-message "At least one administrator user must remain enabled.";
}

leaf username {
type string {
pattern '[a-z_][a-z0-9_-]*[$]?' {
error-message "Invalid username. Must start with a lowercase letter or underscore, followed by lowercase letters, numbers, underscores, or hyphens.";
}
length 1..32;
}
must ". != 'root'" {
error-message "Username cannot be 'root'.";
}
description "The username for the local account.";
}

leaf role {
type user-role;
mandatory true;
description "The role assigned to the user, which determines their group memberships and privileges.";
}

leaf password_hash {
type string;
mandatory true;
must "not(starts-with(., '!'))" {
error-message "Password hash cannot start with '!'. Use the 'enabled' attribute to disable user accounts.";
}
description "The hashed password string for the user, as found in /etc/shadow. Password hashes can be generated using 'mkpasswd' utility or programmatically using libraries like 'passlib'. To disable an account, use the 'enabled' attribute instead of prepending '!' to the password hash.";
}

leaf-list ssh_keys {
type string;
description "A list of full public SSH key strings.";
}

leaf enabled {
type boolean;
default true;
description "Whether the user account is enabled. When false, the password is disabled by prepending '!' to prevent password-based login while preserving SSH key access.";
}
}
}

container LOCAL_ROLE_SECURITY_POLICY {
description "LOCAL_ROLE_SECURITY_POLICY part of config_db.json";

list LOCAL_ROLE_SECURITY_POLICY_LIST {
key "role";
description "Global security policies applied to users based on their role.";

leaf role {
type user-role;
description "The role for which this security policy applies.";
}

leaf max_login_attempts {
type uint32 {
range "1..1000";
}
description "Maximum number of failed login attempts before accounts with this role are locked. If not set, system defaults apply.";
}
}
}
}
}
Loading