-
Notifications
You must be signed in to change notification settings - Fork 1.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Optional and Configurable LDAP User/Session Management Support and Reworked Pluggable Auth Driver Interface #9750
Conversation
I see that you haven't updated any CHANGELOG files. Would it make sense to do so? |
SonarQube Quality Gate Reliability Rating on New Code (is worse than A) See analysis details on SonarQube Fix issues before they fail your Quality Gate with SonarLint in your IDE. |
da94c4c
to
a91c69c
Compare
Fix config sesion timeout interface naming (r -> l) Typos fix in ldap.go docstring Rework logic in checkErr for FindUser logic of testing admin table query before failing (rework to avoid error shadowing) Invert logic for err != nil in case for local admin user found Fix missing errors.Is comparison for sessions.ErrUserSessionExpired in ldap module Run docs generate Fix typo in txtar test LDAP config
…rrors in ldap.go and sync.go Run go mod tidy
… wrapper interfaces and mocks New LDAPClient and LDAPConn interfaces allow test mocks to handle Bind and Search functionality. the ldap implementation has been updated to store the ldapClient (still ephemeral single use, like a factory) in the struct such that the test harness can swap the implementation with the mocks. Create helpers_test.go following codebase convention to allow a Setter method to be defined for the ldapClient field, but separated from the production build. This allows the ldap struct and field properties to remain unexported. Test helper contains test mock configand helper constructor function New ldap_test.go module with cases for ldap query functionality and local admin support assertions ldap.go module improvements, return struct in constructor instead of interface type for authentication provider, define user facing error consts (test assertion), store ldapClient in struct, nicer error handling for user not found in FindUser, fix err shadowing reuse error in token expired case, fix typos in ListUsers LDAP Sync rework for new ldapclient field, use new interface to support mocking Remove dangling commited localauth orm.mock, which was being imported by a missed test. Test now imports the correct mock (new authentication provider mocks)
…support godoc render Remove redundant LDAP prefix for UniqueMemberAttribute
…[WebServer].AuthenticationMethod in changelog
492df72
to
4a9ada5
Compare
…onnection as it is required for the sync functionality, flip return flow in TestPassword for admin fallback
LocalAdminUsersORM -> BasicAdminUsersORM, regenerate mocks Save indent in WebServer ValidateConfig when ldapauth Update comments and rename CreateEphemeralClient -> CreateEphemeralConnection
Overview
This PR expands on the core authentication implementation by abstracting user management calls to a new interface, allowing for plugable authentication drivers.
In addition, this introduces a new 'LDAP' authentication method that allows user sessions to be created utilizing an upstream remote identity provider, authenticating and authorizing users based on the proxied login credentials. This allows for user management of the multiple instances of the Core Node to be handled in one place, and a low friction way of changing and managing roles. Removing or adding any role of user for node management when using the
ldap
authentication module removes the need to connect to and update each running instance individually.Key Changes
App SessionORM is Now Interface
Previously, the top level application struct stored an initialized sessions/orm where functions defined in the original
type ORM interface {
were made directly in various places throughout the codebase (router controllers, graphql, shell.go)This has since been updated such that sessions contains folders of modules that implement a new "sessions.AuthenticationProvider interface". This new interface is the same shape as the previous one, with a one new TestPassword method. Tests and app initialization call sites have been updated to conditionally create the auth driver based on a new constant string type 'AuthenticationProviderName'
New Config Section
The WebServer config block now has a new field to define which authentication system should be used for user sessions and management.
AuthenticationMethod = 'local'
also defaults to local (the existing local users and sessions table implementation).When
AuthenticationMethod = 'local'
is set to LDAP, the new optional[WebServer.LDAP]
block of config is scanned.This is forward compatible as the field defaults to 'local', which when set does not require LDAP fields to be present and defined. Config fields are expanded on below.
New DB Tables
In order to provide a more robust, low friction user experience, two new tables were created as part of the LDAP user management implementation to handle and store short lived sessions. Originally the implementation maintained a validated login session in the memory store, and validated the user against the upstream identity service every request. This introduced high cost latency to every single request made through the Operator UI. As many identity providers have defined rate limiting, this approach was implemented to support configurable local sessions as well.
The two new tables are specific to the LDAP implementation, storing the cached state of the authenticated user's email and role. One table is for API sessions, and the other for API token support.
The idea here is that the session lifespan and background state sync config fields can be dialed to the level of risk an operator chooses to assume. As sessions are initially created and authenticated through the LDAP server, saved and active sessions will not be purged immediately if that user is removed , however there is also a configurable new background sync that checks the state of the upstream server on a user defined interval, see below.
LDAP Module
The bulk of the change is all of the functionality implemented in
core/sessions/ldapauth/sync.go
andcore/sessions/ldapauth/sync.go
The first implements the functionality for all functions in the new
AuthenticationProvider
interface. As the identity provider's role is to securely handle user management, the implementation assumes the upstream LDAP server credentials are read only. Actions mutating the user such as creation and deletion are not supported, and stubbed with an error message stating such. The auth functionality is abstracted form the router controllers, the login session request is propagated through and handled by the LDAP module'sCreateSession
function. This makes use of the LDAP bind methodology, sending forward the user provided credentials the backend. This is a blocking call, so the upstream LDAP provider can synchronously block the request while waiting for the user to respond to a 2FA push notification for example (sufficient request time out should accommodate for a blocking auth call for 2FA).Upon receiving a successful response for achieving an LDAP bind with the user credentials, the user email is then used to query LDAP group membership for the configured group names. There are currently four supported roles within the core node, and this update adds new config fields to allow parameterization of these group names. For example, groups should be created in the LDAP server to map to Core Node Admins, Editors, Runners, and Viewers.
The module also supports optionally asserting the user's 'Is Active' property (which is implemented as a further upstream query call) as some LDAP providers retain user groups even after the user is deactivated. All of these fields are parameterized via the new WebServer LDAP TOML config.
While full session is support with user authentication handled by a separate, remote service, local admin features are supported as well by the LDAP module directly. This is important because the core node still needs to be able to be managed locally, and if there are any issues with the config or upstream server the node should still be accessible. The local users table is still taken into consideration for calls such as ListUser, FindUser, and when logged in as a local admin change password is supported this account.
Lastly, the sync module provides the implementation for reaping both expired sessions and API tokens, in addition to pulling and fully syncing all saved local state (in the session and API token LDAP DB Tables) against the upstream server. This can be defined to run on a user defined interval, but also runs when any auth related event happens. Whenever a user logs in or out, the Work function is invoked, which first checks the optional rate limit for last synced time. If enough time has elapsed, a full query of all active local sessions and API tokens is performed against he LDAP server to assert LDAP group membership to check roles, if the user is still active (optional), and if the user belongs to any group at all anymore. The state is resolved by purging users if not present upstream, or updating their roles if they were removed from certain groups and added to others.
Rate limiting is implemented and configurable for the sync service to address metered API calls. The background task implementation is through a recursive
time.AfterFunc
call, which delays the goroutine cost.Documentation and Use
To provision a node to use LDAP as an identity providers, after creating the four required LDAP groups within the LDAP server, simply populate the following TOML config and secret values:
config.toml
:secrets.toml
:Replace the
ou
users and groups values depending on the LDAP server, and populate the BaseDN. This additional filter criteria is applied on all queries performed by the core node.Adding a user to the identity provider and assigning them the relevant groups will be effective immediately as the check is performed on login.
Note: User changes in the upstream LDAP server such as user removal or role change do not immediately update any active sessions. The background LDAP sync module will pull the latest state and update all active sessions and API tokens when discrepancies are found. The interval at which this sync runs can be configured through the
UpstreamSyncInterval
toml config. A short sessions duration and fast sync interval time can be set if prolonged session access after user removal is seen as a high and sensitive risk. The sessions in these tables (ldap_sessions
andldap_user_api_tokens
).