Service accounts
A service account is a person entry used by a program (issue #221). It
is an ordinary entry under ou=users. The auxiliary class
stsServiceAccount and the attribute stsServiceAccount: TRUE mark it as a
service account. It is not an application.
A service account keeps everything a person entry has: a password, app
passwords, Kerberos keys, a sub, groups and roles. Shared Signals report
on it under the person’s iss_sub, and RISC’s account lifecycle applies to
it. The flag adds two things:
- A policy. The service-account policy is the third kind of policy on Directory → Policies, next to the password and authentication policies.
- Optional password rotation. Each new password is pushed to a secrets manager before this service starts accepting it.
Making one
You can set the flag in three ways:
- On the console, tick Service account on Users → New user, or use the Service account tab of an existing person’s page.
- Through the management API, call
POST /admin-api/users/set-service-accountwithuser,owners,destinationandsecretName. SendserviceAccount: falseto make the person an ordinary person again.POST /admin-api/users/createaccepts the same fields withserviceAccount: true. - Over LDAP, an administrator with Admin Write can modify the entry. The same rules apply, and the directory adds or removes the auxiliary class with the flag.
The person can never set the flag on their own entry, whatever
ldap.selfWritableAttributes says. SCIM does not carry the flag.
| Attribute | What it is |
|---|---|
stsServiceAccountOwner |
The DN of a person or group responsible for the account, one value per owner and at most 25. At least one is required while the policy’s requireOwner is on, which is the default. |
stsSecretDestination |
The DN of the push destination, an application entry. |
stsSecretName |
The secret’s name or path at that destination. The secret must already exist, because a push never creates one. |
stsPasswordRotatedAt |
When the password was last rotated. |
stsPreviousPassword, stsPreviousPasswordExpires |
The previous password’s hash and when it stops being accepted. The hash is never returned by any read. |
stsRotationFailures, stsRotationLastError, stsRotationLastAttempt |
The rotation’s state, kept on the entry so that any node can carry on from it. |
Users lists service accounts with a tag. The filter
?kind=service (or ?kind=person) shows only service accounts (or only
everyone else), on the console and at GET /admin-api/users.
Owners
An account can have more than one owner. Each owner is a person or a group
in the realm, and a group stands for everyone in it. You can name at most 25
owners. While the policy’s requireOwner is on, which is the default, an
account must have at least one.
On the console, the Service account tab lists the owners in a table. To add an owner:
- Choose people or groups and type part of a username or a group’s name.
- Press Find. The results show five at a time, with Previous and Next.
- Press Add beside the owner you want.
For an account that is already a service account, Add and Remove save the change as soon as you press them. The search results stay on the page, so you can add several owners one after another. You cannot remove the last owner while the policy requires one.
For a person who is not a service account yet, the table is a draft. Nothing is saved until you press Make them a service account with these owners and push destination, which saves the owners, the destination and the secret name together. On Users → New user you type the owners instead, one per line.
Push destination
The push destination is chosen with a search too, and the search finds only secret destinations (Directory → Secret destinations):
- Type the secret’s name or path in Secret name or path.
- Type part of a destination’s name, DN or provider and press Find.
- Press Use beside the destination you want.
For a service account, Use saves the destination and the secret name at once. The two go together, so type the secret name first. A destination that is set already is marked in use, and one that cannot be pushed to yet is marked not usable now. Once a destination is set, Save the secret name changes only the name, and Clear the push destination removes both. The password is then no longer rotated, and the owners stay.
Through the management API, owners is the whole list. It replaces the
owners the account has, so to add one you send the existing owners as well.
Each entry is a DN, a username or a group’s cn; a string is read as one
owner per line.
To change one owner or the destination without sending the rest, use the three actions the console uses:
| Action | Body | What it does |
|---|---|---|
add-service-account-owner |
user, owner |
Adds one owner and keeps the others and the destination. An owner already listed is ok with changed: false. |
remove-service-account-owner |
user, owner (the DN) |
Removes one owner. Refused for an owner the account does not list, and for the last owner while the policy requires one. |
set-service-account-destination |
user, destination, secretName |
Sets both, or clears both when both are empty, and keeps the owners. |
Each answers account, the service account as it now stands. To find a
destination, GET /admin-api/secret-destinations?q=<text> narrows the list to
destinations whose name, identifier, DN or provider contains the text, and
matched counts them.
The policy
Each realm has one profile, cn=default,ou=serviceAccountPolicies. A realm
without its own profile inherits the default realm’s profile, and if that
does not exist either, the built-in defaults below apply. Every default is
the more secure choice.
| Field | Default | What it does |
|---|---|---|
exemptFromSecondFactor |
off | When on, the authentication policy’s second-factor requirements do not apply, and neither does the rule that refuses a password alone at a password-only door. amr still says pwd. |
allowBrowserSignIn |
off | When off, the sign-in screen, the portal and the console refuse the account, whatever first factor it presents. Making a person a service account also ends their browser sessions. |
allowLdapBind, allowWsTrust, allowScim, allowSsf, allowEst, allowKerberos, allowPasswordGrant |
on | Which password doors accept the account. At a closed door, the right password is refused as a wrong password is. |
rotationEnabled |
off | Turns on automatic rotation, described below. It cannot be turned on while the realm has no push destination. |
rotationIntervalDays |
30 | How often the password rotates. |
rotationOverlapMinutes |
60 | How long the previous password is still accepted after a rotation. |
generatedLength |
32 | The length of a rotated password. It can never be shorter than the password policy’s minimum. |
requireOwner |
on | Whether an account must name at least one owner. |
rotationAlarmFailures |
3 | How many failed rotations in a row raise an alarm. |
The policy applies in both modes, as the authentication policy does. In development mode, which checks no password, a closed door still refuses the account.
Rotation
The service-accounts.rotate scheduler job runs hourly on one node. It
rotates every service account that names a destination and whose interval
has passed. Rotate now on the account’s page, or
POST /admin-api/users/rotate-password, queues the same work as
service-accounts.rotate-now. Each rotation goes through these steps:
- It takes a per-account claim, so the job and a rotation started by hand never rotate the same account at once.
- It draws a password with the password policy’s generator, at the policy’s length.
- It pushes that password to the destination as a new version. If the push fails, nothing changes: the failure is counted and the next run tries again.
- Only after the push succeeds does it commit the password. The previous password stays accepted for the overlap. The account’s Kerberos keys are derived again, and the KDC keeps the previous key version for at least as long, so tickets issued under it can still be decrypted. The old password itself never pre-authenticates.
- It sends a CAEP
credential-change(password,update, initiated bysystem) and writes an audit row.
While rotation is on, the account’s password cannot be set by hand, whether from the console, the API or an LDAP modify. The destination is the source of truth.
The overlap is checked when a password is read. The
service-accounts.previous-cleanup job only clears hashes that have already
expired. A sign-in with the previous password is logged, because it means a
consumer has not picked up the new password yet. It is not recorded as a
failure for risk scoring.
Monitoring → Service accounts (GET /admin-api/service-accounts) lists
every account with its destination, last and next rotation, how long the
previous password remains valid, and the failure count. An account that has
reached the alarm threshold is flagged. A failed rotation logs
STS-SVCACCT-0044 or STS-SVCACCT-0046. The alarm is STS-SVCACCT-0042. A
password that was pushed but could not be committed is STS-SVCACCT-0045,
which is raised as an alarm at once.
Destinations are application entries registered under Directory → Secret destinations. Each one holds the write credential for its secrets manager.
A service account is not a Kerberos service principal
The two are easy to confuse, because Active Directory puts a service’s SPN on a user account (or a managed service account) and calls that account the service account. In iya-sts they are two different kinds of entry, and a Kerberos service principal is never a service account.
| Service account | Kerberos service principal | |
|---|---|---|
| The entry | A person entry under ou=users with stsServiceAccount: TRUE |
An application entry whose identifier is <spn>@<realm>, e.g. HTTP/apigw1.example.com@EXAMPLE.COM |
| Its Kerberos role | A client. It gets a TGT with an AS exchange, pre-authenticated with its password, where the policy’s allowKerberos door is open |
A service. It holds an SPN, accepts tickets (AP-REQ), and asks the KDC for S4U2Self and S4U2Proxy tickets with its own TGT |
| Its key | Derived from its password, as every person’s is, and derived again when the password changes or rotates; the KDC keeps the previous key version for the overlap | Random, one per enctype, made on the Kerberos Principals page (/admin/kerberos/principals) or by POST /admin-api/kerberos/principals/create-service; replaced by Rotate (rotate-service) |
| What a program is given | Its password, read from the push destination when rotation is on, used with kinit <name>@<REALM> |
A keytab, handed over once by the create or a rotate, used with kinit -k and by the acceptor |
| Delegation rules | It delegates nothing: it is a client. Like any person it can be protected from delegation (stsNotDelegated, delegation.protectedGroups) |
appAllowedToDelegateTo, appAllowedToActOnBehalfOf and appDelegationSemantics on the entry, read by the KDC and by the one delegation policy every protocol shares (Delegation and impersonation) |
| Governed by | The service-account policy on Directory → Policies | The application’s own entry, like any other application |
Three consequences are worth knowing:
- An SPN cannot be put on a service account. A service that accepts Kerberos tickets is registered as a service principal, whatever program runs it. Its owner and its rules sit on that application entry.
- A rotating service account has no keytab. Reset password and download keytab sets the password, and only the rotation may set a rotating account’s password (STS-SVCACCT-0013). Its consumers read the password from the destination and authenticate with it; a rotation pushes the password, never a keytab.
- The same program can be both. A service that accepts tickets for its SPN and signs in somewhere else as a client of its own holds two identities: the service principal’s keytab for the first, and a service account’s password for the second.
See Kerberos, KKDCP and SPNEGO for service principals, their keys and delegation.
What it is not
- Not an application. An OAuth client is a non-human identity of its own kind, with its own Shared Signals subject (see CAEP events).
- Not a Kerberos service principal. An SPN lives on an application entry (see above).
- Not exempt from the opt-out rule silently. A service account has no holder to opt out of RISC (RISC section 2.8), and the portal and the register say so rather than applying the rule.