Users, roles and authenticator codes
Create accounts on the server, choose their roles, and enroll authenticator apps for sign-in and secret changes.
harnessd has no sign-up endpoint: accounts are created only from the server's command line. Each account has one role, viewer, operator or admin, and can have an authenticator app (TOTP) as a second factor. Admins need a code from their authenticator for every change to secrets and server settings.
Roles#
Roles are ordered: an operator can do everything a viewer can, and an admin everything an operator can.
| Role | Can |
|---|---|
viewer | See projects, the board, live state, run history (without transcripts), the server's info, settings and sandboxes |
operator | Also: refresh; create, move, comment on and dispatch tasks; stop runs; un-park tasks; release linked-task waits; read live transcripts and run transcripts; read the workflow; list workspaces; read container logs and stop containers |
admin | Also: start, stop, pause and resume projects; save the workflow; manage variables, secrets and test logins; change server settings; run connection tests; build images; remove workspaces; add projects from the app and remove them |
Transcripts and container logs need operator because they can contain tool output, such as file contents and command output. They're redacted of known secret values either way.
Role changes apply immediately, even to sessions that are already signed in.
Manage accounts#
All commands take --config /etc/harness/harnessd.yaml (or HARNESSD_CONFIG) and should run as the harness user.
# Create an account (prompts for the password twice)
harnessd user add maria --role operator
# Read the password from stdin instead (for scripts)
printf '%s\n' "$PASSWORD" | harnessd user add ci-viewer --role viewer --password-stdin
# Enroll an authenticator app, or remove it
harnessd user totp maria
harnessd user totp maria --disable
# Change a password (signs out all of the user's sessions)
harnessd user passwd maria
# Disable, re-enable or delete
harnessd user disable maria
harnessd user enable maria
harnessd user remove maria
# List accounts: name, role, and "disabled" / "totp" flags
harnessd user listThere's no command to change a role: remove the account and add it again with the new role. Every user command is recorded in the audit log with the actor cli.
Usernames and passwords#
- Usernames: 2 to 64 characters, letters, digits and
. _ - @, starting with a letter. - Passwords: at least 12 characters, not containing the username, not too repetitive (at least 5 distinct characters), and without common words such as
password,123456,qwerty,letmein,adminorwelcome. - Passwords are hashed with scrypt (N=2¹⁵, r=8, p=1) and a random 16-byte salt, in
data_dir/users.json(mode0600).
Authenticator codes#
- Enroll
Run
harnessd user totp <name>. It prints anotpauth://totp/SI%20Fabrik:<name>?…URI and the base32 secret. Add either to an authenticator app (1Password, Google Authenticator and others). - Confirm
Type the current 6-digit code at the
Code:prompt. TOTP is only enabled if it matches. - Sign in
From now on, signing in from the app asks for the code after the password.
Codes are RFC 6238 (SHA-1, 6 digits, 30 seconds). The server accepts the current step and one either side, and each code works only once: a code used to sign in can't be reused for a secret change, and vice versa.
Where codes are required#
| Action | Needs a code |
|---|---|
| Sign in, for accounts with TOTP | Yes |
Sign in as an admin, with require_totp_for_admins: true | Yes; admins without TOTP can't sign in |
| Set or remove a variable or secret | Yes (admin) |
| Save Server Settings | Yes, one code per Save |
| Add or remove a test login | Yes |
| Add a project from the app, or remove one | Yes |
An admin without an authenticator gets totp_not_enrolled for those changes, with the command to run. Wrong step-up codes count towards a lock on further attempts (429 locked).
Sessions#
- A sign-in returns a random 256-bit token (
hs_…). The server stores only its SHA-256 hash, in memory, so a restart signs everyone out. The app keeps the token in your Keychain; your password isn't stored. - Sessions end after
session.idle_minuteswithout a request (default 60) andsession.absolute_hoursafter sign-in (default 12). - Signing out, a password change, or disabling the account revokes sessions.
Lockout#
After lockout.max_failures failed sign-ins (default 5) for one account within lockout.lock_minutes (default 15), the account is locked for that long. Per IP address the threshold is four times higher, because many users can share one VPN address. Locked attempts get 429 locked.
Unknown users and wrong passwords get identical responses with equal timing, so sign-in can't be used to discover accounts. Only after the right password does the server say a code is needed (totp_required).