User Access

How to create and manage access for users on a new server

Once your raw metal server is ready to host a Solana validator, the system administrator must provision access for the validator operators. This guide walks you through the process of running the Ansible script to provision users on a newly provisioned metal server.

Pre-Requisites

This page assumes you have access to a provisioned bare metal with the ubuntu user with sudo access as explained in the Choosing Your Metal page.

Since the user provisioning is done via an Ansible script, you must also have a running Ansible Control.

Architecture

Our security strategy follows the principle of least privilege.

We define IAM users and roles in advance, which serve as the blueprint during setup. Each role maps directly to an Ubuntu group, ensuring every user operating on the host has only the minimal access required for their tasks.

🎭 Roles

These are the roles with their asserted purpose and description:

ROLES (GROUPS)
DESCRIPTION

📂 sysadmin Purpose: Complete system administration

Sudo access. Break-glass access for critical system emergencies

📂 validator_operators Purpose: Validator admin, operations & process control

Complete validator management without OS access. Can do everything to the validator config either directly or via the Ansible Control, including install/uninstall, but nothing to the server or OS.

📂 validator_viewers Purpose: System observability & metrics

Observability without modification capability. This role is intended for users who need read-only access to the validator. Members can view service status, access logs, monitor, and inspect validator keys, but cannot perform any administrative or operational actions either directly or via the Ansible Control.

🚦 Permissions

These are the permissions that apply to each role along with some example commands that apply to each permission:

PERMISSION
sysadmin
validator_operators
validator_viewers

user_mgmt Example commands: sudo passwd forgetfuluser

sudo userdel baduser sudo useradd gooduser

pkg_mgmt Example commands: sudo apt update sudo apt install htop

pwd_selfsvc Example commands: sudo reset-my-password

validator_mgmt Example commands: sudo systemctl restart sol kill UID sol service

validator_monitoring Example commands: systemctl status sol

journalctl -u sol.service -f

👥 Users

Users belong to roles, except ubuntu and sol, which have special treatment as shown here:

USERS
DESCRIPTION
USAGE

⚙️ ubuntu

Provisioned by ASN with a server. Disabled after secure user setup.

To provision server users.

⚙️ sol

Primary validator service runner and owner of the validator files and data.

Runs the validator service. Restricted to everything else.

🧍Operator User: >>> alice, bob, etc.

Each human operator has his/her dedicated user.

Access the server via SSH, with no password. Can run some Ansible scripts from the Ansible Control depending on their role membership.

If a non-member user of a role, attempts to execute any of the commands within one of the permission, or attempts to run an Ansible Script with permissions they don't have, they will get an error that looks like this:

Sorry, user hugo is not allowed to execute '/usr/bin/apt udpate' as root on host-charlie.

Each role operates under the principle of least privilege with deny-by-default access control. Users are granted only the specific permissions explicitly defined using dedicated config files for each role. You can see the details later on this guide on under the User Access section.

User Setup

🎛️ Config File

The pb_setup_server_users.yml expects a CSV with users and groups meta that will be used for the identity and access management provisioning.

You can use the template below as a starting point and modify as needed. Once you are happy with the setup, put in this folder ~/new-metal-box/iam_setup.csv. You'll be using this path as a parameter when running the script.

❇️ Provisioning

Before running the user provisioning playbook, ensure that your inventory file (target_one_host.yml) is updated with the IP address of the target server where you will install the users. Your inventory file should look like this:

all:
  hosts:
    new-metal-box:
      ansible_host: 192.168.1.100
      ansible_port: 22

Replace <target_ip_address> with your actual values. Once the inventory is updated, you can run the playbook using:

ansible-playbook playbooks/pb_setup_server_users.yml \
  -i target_one_host.yml \
  -e "target_host=new-metal-box" \
  -e "ansible_user=ubuntu" \
  -e "csv_file=iam_setup.csv"

Upon running the playbook, you will see a confirmation asking you to verify the IP of the host you are about to change:

TASK [Show server IP and location for confirmation] ******************************
[Show server IP and location for confirmation]
IMPORTANT: You are about to run this playbook on the server with IP: 192.168.1.100

Location Information:
- City: Unknown
- Country: Unknown
- Organization: Unknown

To continue, please type exactly this IP: 192.168.1.100

If you are not sure, press Ctrl+C to cancel.

Type IP here

This step is a safety measure to ensure you are provisioning the correct server. Type the IP address shown to continue. If you are not sure, press Ctrl+C to cancel the process.

❌ Ubuntu User

Before the playbook completes, a final security warning is issued to inform the operator that the default ubuntu user will be disabled. This is a deliberate security measure, as many cloud providers (ASN) preconfigure servers with the ubuntu user by default, which poses a risk if left active.

A notification is displayed with the following message:

TASK [iam_manager : Notify about upcoming ubuntu user disablement] ******************************

MSG:

IMPORTANT WARNING: The ubuntu user will now be disabled.
After this task completes, you will LOSE CONNECTION to this server.
Please ensure you can connect with one of the newly created users:
- alan
- alice
- bob

TASK [iam_manager : Pause for warning] **********************************************************
[iam_manager : Pause for warning]
Press Enter to continue and disable ubuntu user (you will lose connection!), or Ctrl+C to abort:

🎟️ User Access

The playbook automatically configures the required sudo permissions for each role by deploying dedicated policy files under /etc/sudoers.d/. The system uses a hierarchical approach where higher roles inherit permissions from lower roles.

Role Hierarchy and Permissions

ROLE
FILE
INHERITANCE

sysadmin

10-sysadmin

None (top level)

validator_operators

20-validator-operators

Inherits from viewers

validator_viewers

40-validator-viewers

Base level

Password Self Service

Users who belong to the sysadmin or validator_operators groups are required to set their own password in order to perform privilege escalation via sudo.

Initial Access

After the user logs in using their SSH private key, they will see a welcome message guiding them through the self-service password setup:

ssh -p 2522 [email protected]
===============================================================================
                              WELCOME TO THE SYSTEM
===============================================================================
Welcome, alan!
===============================================================================

===============================================================================
                              PASSWORD MANAGEMENT
===============================================================================
To change your password, use: sudo reset-my-password

This command allows you to reset your password without entering your current
sudo password. It's a self-service feature for your convenience.
===============================================================================

Run the following command once logged in:

sudo reset-my-password

Security Question Setup

After running the sudo reset-my-password command for the first time, users will be prompted to configure a personal security question and answer. This is a mandatory step in the password self-service process.

First-time setup: create your personal security question.
 Question (min 10 chars): secure question > 10 chars
 Answer (min 6 chars, will not echo): 

The security question and answer are encrypted using AES-256 with a high number of iterations and a unique salt, ensuring robust protection against brute-force or dictionary attacks. This encrypted data is stored in the path /etc/password-security/alan, with strict file permissions accessible only by root.

If a user wishes to reset their password using the self-service script (sudo reset-my-password), they will be prompted to correctly answer their security question.

The user has a maximum of 3 attempts. After three failed answers, the script will deny further access for 24 hours, displaying the following message:

sudo reset-my-password
Security check: secure question > 10 chars...
 Answer: 
Incorrect answer. 2 attempts remaining.
 Answer: 
Incorrect answer. 1 attempts remaining.
 Answer: 
sdfsIncorrect answer. 0 attempts remaining.
Too many failed attempts. Account locked for 24 hours.
Contact administrator for immediate unlock.

To regain access before the lockout period ends, a user from the sysadmin group must either:

  • Manually reset the password using sudo passwd alan, or

  • Remove the lock file at /etc/password-security/password-reset-blocked-alan to re-enable the self-service flow.

Password Self-Service Logging

To ensure full traceability and auditability, all user interactions with the reset-my-password script are logged in the file /var/log/password-reset.log. This includes events such as security question setup, password resets, and encryption actions.

Each log entry includes a timestamp, session ID, username, event type, and source IP address. This enables administrators to monitor and audit all password self-service activity in a secure and structured format.

[2025-07-23 03:51:09] [INFO] [SESSION:95939d91] [USER:alan] [TYPE:SESSION_START] [IP:172.25.0.10] Password reset process initiated  
[2025-07-23 03:51:09] [INFO] [SESSION:95939d91] [USER:alan] [TYPE:FIRST_TIME_SETUP] [IP:172.25.0.10] User creating initial security question  
[2025-07-23 03:55:59] [INFO] [SESSION:6298dffe] [USER:alan] [TYPE:QUESTION_CREATED] [IP:172.25.0.10] Security question created successfully  
[2025-07-23 04:04:31] [INFO] [SESSION:e56002e8] [USER:alan] [TYPE:ANSWER_CREATED] [IP:172.25.0.10] Security answer created successfully  
[2025-07-23 04:04:32] [INFO] [SESSION:e85a52ef] [USER:alan] [TYPE:QUESTION_ENCRYPTED] [IP:172.25.0.10] Security question encrypted with AES-256  
[2025-07-23 04:04:32] [INFO] [SESSION:e85a52ef] [USER:alan] [TYPE:SECURITY_QUESTION_SAVED] [IP:172.25.0.10] Security question and answer stored successfully  

All log files are owned by root and are protected from unauthorized access. This logging system ensures compliance with internal audit policies and helps detect misuse or suspicious activity in password operations.

Discovering Your Sudo Permissions

To help users understand what commands they are allowed to run, a message is displayed at login as part of the system's welcome screen:

===============================================================================
                              WELCOME TO THE SYSTEM
===============================================================================
Welcome, alan!
===============================================================================

===============================================================================
                              USEFUL COMMANDS
===============================================================================
To check your current permissions, run:
  sudo -l -U alan

To view your permissions file:
  cat ~/permissions.txt
===============================================================================

Each user is provided with a permissions.txt file in their home directory. This file is:

  • Automatically generated by Ansible during provisioning

  • Updated based on the user’s assigned role and sudoers configuration

cat ~/permissions.txt
cat ~/permissions.txt

# REAL USER PERMISSIONS FOR alan
# ================================================
# This file shows the REAL permissions you have on this server
# based on your current sudoers configuration
#
# Automatically generated by Ansible
# Last updated: 2025-07-23T02:47:16Z

systemctl status sol.service
journalctl -u sol.service --no-pager
tail -f /home/sol/logs/agave-validator.log
tail -n 50 /home/sol/logs/agave-validator.log
pgrep -f sol.service
du -sh /mnt/ledger
du -sh /mnt/accounts

Last updated

Was this helpful?