Using a shared user directory for user authentication across server farms has been a common pattern since the 1990’s. Microsoft adopted it with Active Directory, but we’ve had NIS deployments for many years. Can Okta Advanced Server Access (ASA) work where user authentication is delegated to a central shared directory? Yes.
This article looks at how ASA can work with a shared directory for Linux servers (AD-Joined is a new feature that will be discussed in future articles).
Linux Servers and Directories
Whilst Linux servers can have local users and groups (e.g. entries in the /etc/passwd/ and /etc/group files) they can also be configured to delegate authentication to an external directory. Sun introduced NIS+ (Network Information Service) in 1992, and using a LDAP-compliant directory in Unix/Linux environments has been common since then. In the modern cloud world where servers are dynamically being spun up/down, having a persistent store of users for authentication makes even more sense.
To implement external authentication a Pluggable Authentication Module (PAM) is deployed to each server and the local authentication defers to that. One example is the System Security Services Daemon (SSSD) being configured to work with a specific PAM to integrate with an external datastore. The A Look at IAM in Red Hat Enterprise Linux article includes a discussion of how the SSSD and RedHat Directory Server can provide a centralised authentication solution.
From an end-user perspective they don’t know or care whether they are being authenticated locally or via a central shared directory. But what about ASA and it’s need to provision users and groups to servers it manages?
ASA and User/Group Provisioning
The default model for ASA with Linux servers is to centrally manage users and groups and provision them to servers in a project. The ASA (Server) Agent will, by default, periodically check with ASA and apply changes to /etc/passwd and /etc/group.
But this doesn’t have to be the case – a project can be configured to NOT provision users and groups to a server. The ASA SSH mechanism doesn’t care whether ASA has provisioned the users, only that the OS can authenticate the user passed from the SSH client. So as long as SSSD (or equivalent) is configured correctly and the ASA users are mapped to the directory users, then ASA SSH will work fine.
Let’s look at how we configure ASA to do this.
Configuring ASA to Work with a Shared Directory Deployment
The following figure shows the components and flows for this.
The primary flow (blue) is the user SSH’ing to the target Linux server, being authenticated against Okta and then validated against the shared directory via SSHR/PAM (or equivalent).
The backend flow is between Okta (the Okta Identity Cloud), the ASA Cloud Service and the ASA (Server) Agent on the target server. Users and groups are managed in Okta and are provisioned to ASA. There is some mapping of usernames between the Okta format and the Unix format so that the user can be authenticated to Okta and validated against the shared directory.
For this particular server there is a project configured to NOT perform user management (provisioning) to the servers. So when the ASA Agent connects to ASA it will only download/update the SSH certificate signing authority, not the users and groups.
The following sections describe how we configure both the ASA project and the username mapping to support this. The last section looks at the user experience (the blue flow).
To stop ASA provisioning users to managed servers, there is a flag that is set at the project level. It shows up as the Server User Management column in the project list.
Currently there is no Admin Console UI flag to disable provisioning users in a project – it must be done via an API call. The documentation is at https://developer.okta.com/docs/reference/api/asa/projects/#create-a-project and the body attribute needs to include
This could be done in code, a script, postman, Okta Workflows, or anything else that can call a REST API.
The following figure shows an example in Okta Workflows.
The flow sets up the bearer token for the API call (not shown) and sets the team name (also not shown). The two compose cards are defining the API URL and the new project name. The third card is setting up the HTTP Body object. Of note is the create_server_users value of False. The last card is making a HTTP Post call to that project API URL, passing the bearer token in the HTTP Header and the object from the third card in as the HTTP Body.
Note that you could theoretically turn off provisioning for an existing project with https://developer.okta.com/docs/reference/api/asa/projects/#updates-details-of-a-specific-project, but I have not tested it.
Setting the Correct Unix Username in ASA for the Directory
The unix username that the ASA client is passing via SSH needs to match the username known to the target Linux system. We may need to change how Okta and ASA map usernames to suit a shared directory. Let’s talk about how Okta and ASA do username mapping, then at how it can be implemented for this scenario.
User and Account Mapping
The ASA Client has two username-related functions – it must be able to authenticate the user against the ASA Cloud service/Okta, and it must pass a username over SSH. The user that the ASA Client is passing to the ASA Daemon on the Linux server must match the user that the Linux server can authenticate – in this scenario that’s the username for the account in the shared directory.
For the ASA username, the ASA application definition (in Okta) will use the Okta username by default (so ASA can authenticate the user with Okta).
For the Unix username there are two ways it can be set: automatically in ASA or from an Okta profile attribute mapping. There is an Okta user profile attribute, unixUserName, that can hold a value to be used by ASA, that is used to drive the username creation in ASA.
If the value of unixUserName is blank in Okta, then when the user is provisioned to ASA, ASA will build the local UNIX_USER_NAME from the Okta username, whilst applying some translation rules such as special characters being converted to underscores. For example kent.brockman (Okta username) becomes kent_brockman (UNIX_USER_NAME). See https://help.okta.com/asa/en-us/Content/Topics/Adv_Server_Access/docs/user-management-linux.htm for details on the translation.
If the unixUserName contains a value in Okta, then when the user is provisioned to ASA, this value will be passed in the user profile and set (with translation rules) as the UNIX_USER_NAME in ASA (and used for SSH).
This gives us the building blocks to work with usernames in a shared directory.
How to Apply Okta/ASA Mapping to the Shared Directory
Before implementing the projects and attribute mapping, we need to understand the username format in the shared directory. Is there a standard that’s been applied to naming (e.g. firstname_lastname) or is it somewhat arbitrary? How well does it align to what you have in your Okta usernames (e.g. kent.brockman in Okta == kent_brockman in the directory)?
If the directory username aligns directly with the Okta username, then you can let ASA build the unix username based off its standard transformation rules, i.e. the unixUserName attribute in the Okta profile is blank and not used, ASA takes the Okta username and translates it.
If the directory username does not align directly, but is easily (and consistently) derivable from user attributes (e.g. unix username is always
U<employee#> or unix username is always
<first 7 char of firstname><first char of lastname>, then you could use Okta Expression Language in the profile mapping. For example:
String.append("U", user.employeeNumber)for the first example
String.append(String.substring(user.firstName,0,7), String.substring(user.lastName,0,1))for the second example
The first example is shown below for the ASA application attribute mapping.
Otherwise, if you can’t guarantee a structure or uniqueness of the unix username in the shared directory, you will need to maintain the unixUserName attribute on the Okta user profile and rely on the default mapping of that to the unixUserName attribute in ASA.
For example, with a value of linux_12345 in the Okta user profile, this will be mapped to the UNIX_USER_NAME field in ASA.
How you maintain this unixUserName data in the Okta profiles will depend on the complexity of the usernames in the directory and whether there are exceptions. One option may be to deploy an Okta LDAP agent to the directory, have the users imported into Okta then have some automation (with Okta Workflows) to set the appropriate unixUserName values. Another option may be to apply a default standard naming based on the most-used naming standard in the directory, then manually manage the non-standard usernames.
The above expects that same unix username to be used across all ASA-managed servers. If you need multiple different standards such that some servers need one unix username format whilst others need another, you may need to look to multiple ASA teams with different profile attribute mapping, and potentially different Okta profile attributes, or maybe even different Okta orgs.
In summary, there will need to be some analysis of how the usernames are in the shared directory, and how Okta/ASA can effectively manage them (automatically or with some manual intervention).
The user experience for SSH’ing to a server that is connected to a shared directory is exactly the same as for any other Linux server. A list of servers will show the server the same as for the other servers.
When the user performs SSH (via sft, the following has
ssh aliased to
sft ssh) the experience is the same.
The above example shows that Kent Brockman SSH’s as kent_brockman. But there is no user kent_brockman in the local
/etc/passwd file – the user is in a directory (openLDAP) on another server connected to via SSSD.
This concludes the article. We have shown how ASA can operate in an environment where Linux users are authenticated via a shared directory not locally. We have explored how to configure Okta and ASA to support this model and what it looks like from an end-user perspective.