|
| 1 | +# Declarative Local User Management |
| 2 | + |
| 3 | +## Revision |
| 4 | + |
| 5 | +| Rev | Date | Author | Change Description | |
| 6 | +|:---:|:--------------:|:-----------:|:----------------------------------------------------------------------------------------------------------------------------------------------------:| |
| 7 | +| 1.0 | June 16, 2025 | Manoharan Sundaramoorthy | Initial HLD | |
| 8 | + |
| 9 | + |
| 10 | +## 1. Scope |
| 11 | +This document describes the high-level design for a new feature that provides a declarative and persistent method for managing local user accounts on a SONiC device. This feature allows administrators to define local users, including their roles, password hashes, SSH keys, and **security policies** including **login attempt limits**. |
| 12 | + |
| 13 | +This ensures that local user accounts are consistently applied with robust security postures and persist across reboots and upgrades. |
| 14 | + |
| 15 | +## 2. Definitions/Abbreviations |
| 16 | + |
| 17 | +| Abbreviation | Definition | |
| 18 | +|:------------:|:--------------------------------------------------------------------------| |
| 19 | +| **`userd`** | The new User Daemon proposed in this design. | |
| 20 | +| **PAM** | Pluggable Authentication Modules | |
| 21 | + |
| 22 | + |
| 23 | +## 3. Overview |
| 24 | +This feature introduces a new dedicated daemon, **`userd`**, which manages the full lifecycle of local users based on definitions in `CONFIG_DB`. It simplifies management by providing a user-friendly CLI, mapping abstract roles (`administrator`, `operator`) to specific Linux groups, and enforcing security policies. This provides a solution for managing secure, persistent local user accounts. |
| 25 | + |
| 26 | +## 4. Requirements |
| 27 | +### 4.1 Functional Requirements |
| 28 | +1. The system must allow an administrator to define a local user account declaratively. |
| 29 | +2. The user definition must support: |
| 30 | + * Username and a pre-hashed password. |
| 31 | + * A role, limited to either **`administrator`** or **`operator`**. |
| 32 | + * Authorized SSH keys (statically defined). |
| 33 | +3. The system will **auto-generate** a unique UID for each new user. |
| 34 | +4. **Special User Management:** |
| 35 | + * The **`admin`** user must always exist and be manageable through CONFIG_DB to modify attributes (password, SSH keys, security policies). |
| 36 | + * The **`admin`** user cannot be deleted but can be disabled by setting the password to `!`. |
| 37 | + * If the **`admin`** user is removed from CONFIG_DB, the system must reset it to the default password `YourPaSsWoRd`. |
| 38 | +5. **Security Policy Requirements:** |
| 39 | + * **Login Attempts:** The system must support configuring a global maximum number of failed login attempts per role before accounts are temporarily locked. |
| 40 | +6. The system must map roles to underlying Linux groups: |
| 41 | + * `administrator`: members of `sudo`, `docker`, `redis` and `admin` groups. |
| 42 | + * `operator`: members of a standard, non-privileged and would belong to `users` group. |
| 43 | +7. User accounts and their configurations must persist across system reboots and upgrades. |
| 44 | +8. **System Consistency:** On startup, the system must perform a consistency check to ensure Linux users match CONFIG_DB definitions and automatically remove any users that were added directly to Linux (bypassing CONFIG_DB). |
| 45 | + |
| 46 | +## 5. Architecture Design |
| 47 | +The architecture centers on the new `userd` daemon. This daemon will now interact with several core Linux subsystems to enforce the configured security policies. |
| 48 | + |
| 49 | +**`userd`'s Points of Interaction:** |
| 50 | +1. **CONFIG_DB:** Single source of truth for user configuration and global security policies. |
| 51 | +2. **Core User Files:** `/etc/passwd`, `/etc/shadow`, `/etc/group` for basic user identity. |
| 52 | +3. **PAM Configuration (`/etc/security/faillock.conf`):** To manage global failed login attempt policies per role via `pam_faillock`. |
| 53 | + |
| 54 | +## 6. High-Level Design |
| 55 | + |
| 56 | +### 6.1 `userd` Daemon |
| 57 | +The `userd` daemon's logic will be expanded to manage security configurations idempotently. |
| 58 | + |
| 59 | +**Startup Consistency Check:** |
| 60 | +* **System Reconciliation:** On startup, `userd` will perform a consistency check to ensure that all local users in the Linux system (`/etc/passwd`, `/etc/shadow`, `/etc/group`) match the definitions in CONFIG_DB. |
| 61 | +* **Cleanup of Unmanaged Users:** Any users found in the Linux system that are not defined in CONFIG_DB (except for system users like `root`, `admin`, `daemon`, etc.) will be automatically removed to maintain consistency. |
| 62 | +* **CONFIG_DB as Source of Truth:** This ensures that CONFIG_DB remains the single source of truth for user management and prevents configuration drift. |
| 63 | + |
| 64 | +**Special Admin User Handling:** |
| 65 | +* **Admin User Management:** `userd` will ensure the `admin` user always exists in the system. If not present in CONFIG_DB, it will reset the admin user to the default system password `YourPaSsWoRd`. |
| 66 | +* **Admin User Protection:** `userd` will prevent deletion of the `admin` user even if the entry is removed from CONFIG_DB, but will allow modification of its attributes. |
| 67 | +* **Admin User Disabling:** When the `admin` user's password is set to `!` in CONFIG_DB, `userd` will update `/etc/shadow` accordingly to disable password-based login while preserving SSH key access. |
| 68 | + |
| 69 | +**New Logic for Security Policies:** |
| 70 | + |
| 71 | +* **To enforce Login Attempts:** |
| 72 | + * `userd` will manage the PAM configuration file at `/etc/security/faillock.conf`. |
| 73 | + * For global role-based limits (e.g., administrators with limit of 5), it will ensure appropriate configuration in the global section. |
| 74 | + * `userd` will configure PAM to apply different limits based on user group membership (administrator vs operator roles). |
| 75 | + * `userd` will be responsible for ensuring the PAM stack is configured to use `pam_faillock` with role-based policies. |
| 76 | + |
| 77 | +## 7. SAI API |
| 78 | +No SAI API changes are required. |
| 79 | + |
| 80 | +## 8. Configuration and Management |
| 81 | +### 8.1 Config DB Enhancements |
| 82 | +The `USER` table schema is defined for individual user accounts, and a new `USER_SECURITY_POLICY` table is added for global role-based security policies. |
| 83 | + |
| 84 | +**Schema:** |
| 85 | +```json |
| 86 | +// Example for CONFIG_DB |
| 87 | +{ |
| 88 | + "USER": { |
| 89 | + "admin": { |
| 90 | + "role": "administrator", |
| 91 | + "password": "hashed_password", |
| 92 | + "ssh_keys": ["ssh-rsa AAA..."] |
| 93 | + }, |
| 94 | + "newadmin": { |
| 95 | + "role": "administrator", |
| 96 | + "password": "hashed_password", |
| 97 | + "ssh_keys": ["ssh-rsa BBB..."] |
| 98 | + }, |
| 99 | + "showuser": { |
| 100 | + "role": "operator", |
| 101 | + "password": "!", |
| 102 | + "ssh_keys": ["ssh-rsa CCC..."] |
| 103 | + } |
| 104 | + }, |
| 105 | + "USER_SECURITY_POLICY": { |
| 106 | + "administrator": { |
| 107 | + "max_login_attempts": 5 |
| 108 | + }, |
| 109 | + "operator": { |
| 110 | + "max_login_attempts": 3 |
| 111 | + } |
| 112 | + } |
| 113 | +} |
| 114 | +``` |
| 115 | + |
| 116 | +**USER Table:** |
| 117 | +* `role` (string, required): `administrator` or `operator`. |
| 118 | +* `password` (string, required): The hashed password. |
| 119 | +* `ssh_keys` (optional): List of SSH public keys for the user. |
| 120 | + |
| 121 | +**USER_SECURITY_POLICY Table:** |
| 122 | +* **`max_login_attempts`** (integer, optional): Number of failed login attempts before accounts with this role are locked. |
| 123 | + |
| 124 | +**Notes:** |
| 125 | +* Session timeouts are managed by the system's default timeout policy and are not configurable per user. |
| 126 | +* Password hashes can be generated using `mkpasswd` command-line utility or programmatically using libraries like `passlib` in Python for NETCONF/RESTCONF implementations. |
| 127 | +* The CLI provides both `--password-hash` (for pre-hashed passwords) and `--password-prompt` (for interactive secure password entry) options for improved security and usability. |
| 128 | +* **CONFIG_DB as Source of Truth:** On startup, `userd` performs a consistency check and removes any users that were added directly to Linux (bypassing CONFIG_DB) to ensure CONFIG_DB remains the authoritative source for user management. |
| 129 | +* The **`admin`** user is a special case: it must always exist and can be managed through CONFIG_DB but cannot be deleted. To disable the admin user, set the password to `!`. If the admin user is removed from CONFIG_DB, it will be reset to the default system password `YourPaSsWoRd`. |
| 130 | + |
| 131 | +### 8.2 Yang Model Enhancements |
| 132 | +``` |
| 133 | +module sonic-user { |
| 134 | + yang-version 1.1; |
| 135 | + namespace "http://sonicproject.com/sonic-user"; |
| 136 | + prefix "sonic-user"; |
| 137 | +
|
| 138 | + import sonic-ext { |
| 139 | + prefix "sonic-ext"; |
| 140 | + } |
| 141 | +
|
| 142 | + // Common typedef for user roles |
| 143 | + typedef user-role { |
| 144 | + type enumeration { |
| 145 | + enum "administrator" { |
| 146 | + description "Grants administrative privileges (e.g., member of sudo, docker groups)."; |
| 147 | + } |
| 148 | + enum "operator" { |
| 149 | + description "Grants operator-level (read-only or limited) privileges."; |
| 150 | + } |
| 151 | + } |
| 152 | + description "User role that determines group memberships, privileges, and applicable security policies."; |
| 153 | + } |
| 154 | +
|
| 155 | + // Top-level container for the User feature |
| 156 | + container sonic-user { |
| 157 | + description "Top-level container for local user management configuration"; |
| 158 | +
|
| 159 | + list USER_LIST { |
| 160 | + key "username"; |
| 161 | + description "List of declaratively managed local users."; |
| 162 | +
|
| 163 | + leaf username { |
| 164 | + type string { |
| 165 | + pattern '[a-z_][a-z0-9_-]*[$]?' { |
| 166 | + error-message "Invalid username. Must start with a lowercase letter or underscore, followed by lowercase letters, numbers, underscores, or hyphens."; |
| 167 | + } |
| 168 | + must ". != 'root'" { |
| 169 | + error-message "Username cannot be 'root'."; |
| 170 | + }; |
| 171 | + length 1..32; |
| 172 | + } |
| 173 | + description "The username for the local account."; |
| 174 | + } |
| 175 | +
|
| 176 | + leaf role { |
| 177 | + type user-role; |
| 178 | + mandatory true; |
| 179 | + description "The role assigned to the user, which determines their group memberships and privileges."; |
| 180 | + } |
| 181 | +
|
| 182 | + leaf password { |
| 183 | + type string; |
| 184 | + mandatory true; |
| 185 | + description "The hashed password string for the user, as found in /etc/shadow. To lock an account from password login, use '!'. Password hashes can be generated using 'mkpasswd' utility or programmatically using libraries like 'passlib'."; |
| 186 | + } |
| 187 | +
|
| 188 | + leaf-list ssh_keys { |
| 189 | + type string; |
| 190 | + description "A list of full public SSH key strings."; |
| 191 | + } |
| 192 | + } |
| 193 | +
|
| 194 | + list USER_SECURITY_POLICY_LIST { |
| 195 | + key "role"; |
| 196 | + description "Global security policies applied to users based on their role."; |
| 197 | +
|
| 198 | + leaf role { |
| 199 | + type user-role; |
| 200 | + description "The role for which this security policy applies."; |
| 201 | + } |
| 202 | +
|
| 203 | + leaf max_login_attempts { |
| 204 | + type uint32 { |
| 205 | + range "1..1000"; |
| 206 | + } |
| 207 | + description "Maximum number of failed login attempts before accounts with this role are locked. If not set, system defaults apply."; |
| 208 | + } |
| 209 | + } |
| 210 | + } |
| 211 | +} |
| 212 | +``` |
| 213 | + |
| 214 | +### 8.3 CLI Enhancements |
| 215 | +The CLI is enhanced with user management and global security policy commands. |
| 216 | + |
| 217 | +**User Add Command:** |
| 218 | +``` |
| 219 | +config user add <username> --role <role> |
| 220 | + [--password-hash <hash> | --password-prompt] |
| 221 | + [--ssh-key <key>] |
| 222 | +``` |
| 223 | +* Multiple `--ssh-key` flags can be provided to build a list. |
| 224 | +* Use `--password-hash` to provide a pre-hashed password directly. |
| 225 | +* Use `--password-prompt` to enter the password securely through an interactive prompt (password will be hashed automatically). |
| 226 | +* If neither password option is provided, the user account will be created with password login disabled (password set to `!`). |
| 227 | + |
| 228 | +**User Delete Command:** |
| 229 | +``` |
| 230 | +config user del <username> |
| 231 | +``` |
| 232 | +* The `admin` user cannot be deleted and will return an error if attempted. |
| 233 | + |
| 234 | +**User Modify Command:** |
| 235 | +``` |
| 236 | +config user modify <username> [--password-hash <hash> | --password-prompt] |
| 237 | + [--ssh-key <key>] |
| 238 | +``` |
| 239 | +* The `admin` user can be modified like any other user, including disabling by setting password to `!`. |
| 240 | +* Use `--password-hash` to provide a pre-hashed password directly. |
| 241 | +* Use `--password-prompt` to enter the password securely through an interactive prompt (password will be hashed automatically). |
| 242 | + |
| 243 | +**Security Policy Commands:** |
| 244 | +``` |
| 245 | +config user security-policy <role> --max-login-attempts <count> |
| 246 | +``` |
| 247 | +* Configure global login attempt limits for a specific role (`administrator` or `operator`). |
| 248 | + |
| 249 | +``` |
| 250 | +show user security-policy [<role>] |
| 251 | +``` |
| 252 | +* Display current security policies for all roles or a specific role. |
| 253 | + |
| 254 | +**Password Prompt Implementation:** |
| 255 | +When `--password-prompt` is used, the CLI will: |
| 256 | +1. Display a secure password prompt (e.g., "Enter password for user <username>:") |
| 257 | +2. Hide password input (no echo to terminal) |
| 258 | +3. Prompt for password confirmation (e.g., "Confirm password:") |
| 259 | +4. Validate that both entries match |
| 260 | +5. Hash the password using the same algorithm as `mkpasswd` (e.g., SHA-512) |
| 261 | +6. Store only the hashed password in CONFIG_DB |
| 262 | +7. Clear the plaintext password from memory immediately after hashing |
| 263 | + |
| 264 | +**Other commands (e.g., `show user`) remain as previously defined.** |
| 265 | + |
| 266 | +## 9. Testing Requirements |
| 267 | +### New System Test cases |
| 268 | +* **Global Login Attempts Policy:** |
| 269 | + * Configure `administrator` role with `max_login_attempts` of 5 and `operator` role with 3. |
| 270 | + * Create users with both roles and verify that login attempt limits are enforced according to their role. |
| 271 | + * Attempt to log in with incorrect passwords and verify accounts are locked based on their role's policy. |
| 272 | + * Verify `faillock --user <username>` shows the correct state for users of different roles. |
| 273 | + |
| 274 | +* **Password Management:** |
| 275 | + * Test user creation with `--password-hash` option and verify the hash is stored correctly. |
| 276 | + * Test user creation with `--password-prompt` option and verify the password is hashed and stored securely. |
| 277 | + * Verify that passwords entered via prompt are not logged in command history. |
| 278 | + * Test password modification using both hash and prompt methods. |
| 279 | + |
| 280 | +* **Admin User Management:** |
| 281 | + * Verify the `admin` user exists by default and can be modified through CONFIG_DB. |
| 282 | + * Attempt to delete the `admin` user and verify it fails with an appropriate error. |
| 283 | + * Remove the `admin` user from CONFIG_DB and verify it resets to the default password `YourPaSsWoRd`. |
| 284 | + * Set the `admin` user password to `!` and verify password login is disabled while SSH key access remains functional. |
| 285 | + |
| 286 | +* **Startup Consistency Check:** |
| 287 | + * Create a user directly in Linux using `useradd` (bypassing CONFIG_DB). |
| 288 | + * Restart the `userd` daemon and verify the manually created user is automatically removed. |
| 289 | + * Verify that users defined in CONFIG_DB are preserved and properly configured. |
| 290 | + * Ensure system users (root, daemon, etc.) are not affected by the cleanup process. |
| 291 | + |
| 292 | +* **User Management:** Create users with different roles and verify all security policies are enforced correctly based on role-based policies. |
| 293 | + |
| 294 | +## 10. Future Enhancements |
| 295 | + |
| 296 | +### 10.1 Remote SSH Key Management |
| 297 | +Support for dynamically fetching SSH keys from remote URLs could be added in future versions to enable centralized SSH key management. |
| 298 | + |
| 299 | +## 11. Open/Resolved Issues |
| 300 | + |
| 301 | +None |
0 commit comments