LDAP and Active Directory
The LDAP and Active Directory Connectors use enterprise LDAP and Active Directory (respectively) directory service to provide authentication and attributes used for:
- migrating user profile information from legacy to cloud identity systems,
- adding HTTP headers consumed by on-premises applications, or
- maintaining state for user sessions as they move across identity systems and applications.
Configuration options
The following values can be provided to the LDAP connector via the Maverics configuration file.
URL
The url
(s) of the LDAP server that Maverics connects with. Both a single URL and a list of URLs are supported. When multiples URLs are provided, a round-robin load balancing scheme will be used to distribute traffic.
Service Account Username
The serviceAccountUsername
used to connect to the LDAP server for query operations.
Service Account Password
The serviceAccountPassword
used to connect to the LDAP server for query operations.
Base DN
The baseDN
specifies the location in which to perform the LDAP search.
Username Search Key
The usernameSearchKey
to filter on during query and bind operations.
Attribute delimiter
The attributeDelimiter
used to separate multi-valued attributes. This is an optional field and is only necessary if an attribute is multi-valued. If no value is provided, a default of “,” will be used for the delimiter.
Enable Authentication
Set enableAuthentication
to true in order to use LDAP as an IDP.
connectors:
- type: ldap
enableAuthentication: true
# ...
Authentication Search Scope
The authenticationSearchScope
config is an optional configuration which must be used with
serviceAccountUsername
, serviceAccountPassword
, usernameSearchKey
and
enableAuthentication
to support authenticating users contained in nested
organizational units inside the specified baseDN
. If you need to authenticate with users
which exist in nested organizational units then authenticationSearchScope
must be set to
support their authentication.
connectors:
- type: ldap
enableAuthentication: true
usernameSearchKey: uid
baseDN: ou=Engineering,ou=People,dc=example,dc=com
authenticationSearchScope: singleLevel
serviceAccountUsername: cn=exampleUsername,dc=example,dc=com
serviceAccountPassword: <examplePassword>
# ...
The following configurations are supported:
Base Object
baseObject
is the scope which constrains the search to the baseDN
.
Single Level
singleLevel
is the scope which constrains the search to the immediate
subordinates of the entry named by baseDN
.
Whole Subtree
wholeSubtree
is the scope which constrains the search to the baseDN
and all
of its subordinates.
Login URL
When using the LDAP connector as an IDP, the loginURL
setting (optional) can be used to set a custom endpoint for posting the user’s credentials. If unset, the form is will be submitted to a default location of /.ldap-login
.
This endpoint is hosted by the Orchestrator so the domain specified should resolve to a domain the Orchestrator hosts, or be set to just a path. In either case, it must not conflict with any paths to protected applications.
When specifying a loginURL
, use absolute (full) URL including scheme, hostname, and path to ensure status requests are routed correctly.
Always specify custom loginURL
value if the routePattern
includes a hostname.
For example, if the routePattern
is defined as follows:
routePatterns:
- api.example.com
then the loginURL
value might be:
loginURL: https://api.example.com/login
connectors:
- type: ldap
enableAuthentication: true
loginURL: "https://example.com/ldap-login"
# ...
Custom Login HTML
When using the LDAP connector as an IDP, customLoginHTML
(optional) can present a custom page to prompt the user for authentication.
It should contain the file system location of an HTML page. If the value is unset a default login page will be used.
connectors:
- type: ldap
enableAuthentication: true
loginURL: "https://example.com/ldap-login"
# Using a static HTML page.
customLoginHTML: /var/www/html/ldap-login.html
# ...
By defining customLoginHTML
, the Orchestrator can deliver a custom HTML page stored in the filesystem, as shown above.
The Orchestrator uses Go Templates to provide relevant arguments to be rendered.
This page will need to POST the username and password content to the login URL, which will be delivered in the LoginURL
template value.
The originally requested page can be found in the RedirectURL
template value, and should be posted to the login URL along with the username and password.
<html>
<body>
<form action={{.LoginURL}} method=POST>
<label for="username">Username</label>
<input type="text" id="username" name="username" />
<br>
<label for="password">Password</label>
<input type="password" id="password" name="password" />
<br>
<input type="hidden" name="redirectURL" value="{{.RedirectURL}}" />
<br>
<input type="submit" name="submit" value="Submit" />
</form>
</body>
</html>
Health Check
healthCheck
defines an optional health check for the connector. This option is
required when using the connector in an IDP-continuity scenario. For more info on how
to define the health check, please see the docs.
Examples
Loading Attributes From LDAP (LDAP as an attribute provider)
connectors:
- name: ldap-example
type: ldap
url:
- "ldap://node1.ldap.com"
- "ldap://node2.ldap.com"
baseDN: ou=People,o=Example,c=US
serviceAccountUsername: uid=admin,ou=Admins,o=Example,c=US
serviceAccountPassword: <examplePassword>
usernameSearchKey: uid
attributeDelimiter: ^
Sample Active Directory Connector Configuration
connectors:
- name: ad-example
type: activedirectory
url:
- "ldap://node1.ldap.com"
- "ldap://node2.ldap.com"
baseDN: ou=People,o=Example,c=US
serviceAccountUsername: uid=admin,ou=Admins,o=Example,c=US
serviceAccountPassword: password
usernameSearchKey: uid
attributeDelimiter: ^
Authentication with LDAP Connector (LDAP as an IDP)
When LDAP is used as an IDP, the Orchestrator presents a form for the user to enter their username and password. To use Active Directory as an IDP, replace the connector type with activedirectory
tls:
maverics:
certFile: certs/maverics.sonarsystems.co.crt
keyFile: certs/maverics.sonarsystems.co.key
http:
address: :443
tls: maverics
apps:
- name: Sonar
type: proxy
routePatterns:
- /
tls: sonar-app
upstream: https://app.sonarsystems.com:8443
policies:
- location: /
authentication:
idps:
- ldap
authorization:
allowAll: true
connectors:
- name: ldap
type: ldap
url: "ldap://ldap.example.com:389"
serviceAccountPassword: <examplePassword>
serviceAccountUsername: cn=exampleUsername,dc=example,dc=com
baseDN: ou=People,dc=example,dc=com
usernameSearchKey: uid
enableAuthentication: true
authenticationSearchScope: wholeSubtree
loginURL: https://example.com/ldap-login
customLoginHTML: /var/www/html/ldap-login.html
Using LDAP attributes in a service extension
Attributes for an LDAP connector are only loaded if used in a policy evaluation or set in headers directly.
For example, in the configuration example below, ldap.groupMembership
is used in an authorization policy.
apps:
- name: exampleApp
type: proxy
routePatterns:
- /
# ...
policies:
- location: /
authentication:
idps:
- azure
authorization:
rules:
- and:
- equals: [ "{{azure.authenticated}}", "true" ]
- contains: [ "{{ldap.groupMembership}}", "cn=Joe Smith,ou=East,dc=MyDomain" ]
headers:
- createHeaderSE:
funcName: CreateHeader
file: "/etc/maverics/extensions/createHeader.go"
The ldap.groupMembership
attribute can then be referenced in the createHeader.go
service extension.
/etc/maverics/extensions/createHeader.go
package main
import (
"net/http"
"strings"
"github.com/strata-io/service-extension/orchestrator"
)
// CreateHeader adds a prefix to each group claim.
func CreateHeader(
api orchestrator.Orchestrator,
_ http.ResponseWriter,
_ *http.Request,
) (http.Header, error) {
logger := api.Logger()
logger.Debug("se", "building custom header")
sess, err := api.Session()
if err != nil {
logger.Error("se", "unable to retrieve session", "error", err.Error())
return nil, err
}
groupclaims, err := sess.GetString("ldap.groupMembership")
if err != nil {
logger.Error("se", "unable to retrieve string", "error", err.Error())
return nil, err
}
splitHeaders := strings.Split(groupclaims, "^")
var outHeaders []string
for _, h := range splitHeaders {
outHeaders = append(outHeaders, "myPrefix-"+h)
}
headers := make(http.Header)
headers["GROUPS"] = []string{strings.Join(outHeaders, "^")}
return headers, nil
}
If ldap.groupMembership
were not used in a policy or header, it would be empty when referenced in the service extension.