-
Notifications
You must be signed in to change notification settings - Fork 139
GSS API Authentication
This document outlines a design for GSS-API / SPNEGO authentication of external identities to Dogtag and handling authorisation for those principals.
When Dogtag is running in a Kerberised environment it is desirable to support GSS-API (Kerberos mechanism) authentication.
Additionally, it is desirable to be able to support external identities, i.e. identities that are defined not in Dogtag’s own database but in an external identity store, avoiding identity silos and reducing administrative overhead. A mechanism of mapping external groups/roles to Dogtag roles is required, or alternatively, conditional substitution of Dogtag’s built in ACL evaluator for an alternative access evaluator that can evaluate an authenticated principal’s authorisation to perform Dogtag operations using data or facilities provided by the external identity store.
Dogtag provides an HTTP interface, so SPNEGO over HTTP (RFC 4559) will be the protocol used.
The FreeIPA framework current uses a privileged RA Agent account to perform CA operations, in violation of the principle that the framework should only ever operate with the permissions of the currently-authenticated user (a kind of privilege separation). The FreeIPA framework possesses a certificate for the RA Agent account, and TLS client certificate authentication is used. The lack of privilege separation means that the IPA framework must make authorisation decisions about whether an operator has permission to commandeer the RA Agent certificate to perform the requested operation. Unfortunately, several security issues have occurred as a result of failure to correctly authorise such operations. Allowing the framework to authenticate to Dogtag with user credentials avoids the need to for the IPA framework to perform these authorisation checks.
In contrast, when the FreeIPA framework needs to perform LDAP operations, it uses S4U2Proxy (also known as constrained delegation) to acquire a ticket for the LDAP server on behalf of the user. We wish for the same to occur when FreeIPA talks to Dogtag to e.g. issue a certificate or create a new profile.
Barbican agent or service users should be able to access only Barbican secrets. Initially we expect those users/groups to exist in the Dogtag database. Eventually these could become IPA users/groups.
For regular requests, changes include:
-
Apache performs SPNEGO authentication (
mod_auth_gssapi
) andmod_lookup_identity
adds the principal’s groups and permissions to the request. -
ExternalAuthenticationValve
reads groups out of the request environment and adds them to thePrincipal
. -
AuthMethodInterceptor
updated to recognise external authentication method(s). -
ACLInterceptor
updated to consult theAuthzSubsystem
differently, depending on whether the principal is externally authenticated or not.
For certificate profile processing, a new profile constraint implementation will allow an external process to be invoked to perform additional authorisation and/or validation, according to the needs of the external system. The constraint can be added to profiles as appropriate.
The following diagram shows an overview of request processing, including new components (or new component instances):
Use Apache’s mod_auth_gssapi
to perform the GSS-API (SPNEGO) authentication. It is assumed that a service principal for Dogtag exists in the Kerberos database, and that a local keytab file is available.
Use Apache’s mod_lookup_identity
to look up user groups/roles and populate the request environment. Components in Tomcat or Dogtag will construct a principal from information in the request environment. Assuming AJP is used to connect Apache to Tomcat (this is the case for FreeIPA), this means that mod_lookup_identity must prefix the variables it wishes to convey to Dogtag with AJP_
. These are made available via ServletRequest.getAttribute(String name)
where name
has been stripped of the AJP_
prefix.
The choice of Kerberos principal(s) to use for Dogtag needs to be decided. Several possibilities exist:
-
Standalone Dogtag deployment: each Dogtag clone must have its own principal of the form
HTTP/<hostname>
, permitting access using standard HTTP clients supporting SPNEGO (including web browsers). -
IPA CA deployment (
ipa-ca.<ipadomain>
): instances of the IPA CA can be accessed atipa-ca.<ipadomain>
. Access using this domain name implies the principal nameHTTP/ipa-ca.<ipadomain>
. This principal’s key would have to be be shared among Dogtag clones. Standard HTTP clients can be used. -
IPA CA deployment (keytab shared with IPA framework): on each IPA server, both the IPA framework and Dogtag HTTP could use the keytab of the
HTTP/<hostname>
principal for GSS-API authentication. This approach is not ideal because from the KDC’s point of view, there is no distinction between the IPA web interface and Dogtag on a single server. In this scenario, the IPA framework would still use S4U2Proxy to acquire a ticket for communicating with Dogtag. Standard HTTP clients including web browsers can be used. -
IPA CA deployment (different domain names): each Dogtag instance would be accessed using a unique domain name and a corresponding
HTTP/<hostname>
service principal for authentication. Standard HTTP clients including web browsers will work. Additional DNS records and CA domain name bookkeeping is required, which more or less rules out this approach. -
IPA CA deployment (
dogtag
service type): Dogtag would be accessed using the hostname of the IPA service, but the service principal isdogtag/<hostname>
. There are no new DNS requirements, but HTTP clients that do not allow full control over which principal to acquire a service ticket for cannot be used for GSS-API authentication. The advantage of this approach is that fine-grained S4U2Proxy delegation authorisation rules can be expressed.
The following sssd.conf
snippet will make all group memberships (including indirect membership) available to mod_lookup_identity as the roles
attribute. The memberOf
attribute includes permissions that are inherited through FreeIPA’s RBAC rules. These are cached under a different attribute name because it appears that memberOf
is treated specially, and the D-Bus org.freedesktop.sssd.infopipe.GetUserGroups
method only returns direct memberships.
[domain/EXAMPLE.COM] ... ldap_user_extra_attrs = roles:memberOf [ifp] allowed_uids = apache user_attributes = +roles
The following is an example httpd.conf
snippet showing how mod_auth_gssapi
and mod_lookup_identity
can be configured to perform SPNEGO authentication and provide AJP attributes containing user groups, conditional on the request query string containing an attribute called gssapi
:
<If "%{QUERY_STRING} =~ /\bgssapi=/"> AuthType GSSAPI AuthName "Kerberos Login" GssapiCredStore keytab:/etc/httpd/conf/ipa.keytab GssapiCredStore client_keytab:/etc/httpd/conf/ipa.keytab GssapiDelegCcacheDir /var/run/httpd/ipa/clientcaches GssapiUseS4U2Proxy on GssapiAllowedMech krb5 Require valid-user LookupUserAttrIter roles +AJP_REMOTE_USER_GROUP </If>
Systems using SELinux must be configured to allow Apache to communicate with SSSD over D-Bus:
% sudo setsebool -P httpd_dbus_sssd 1
-
An alternative approach is to leverage Tomcat’s
SpnegoAuthenticator
and useJDNIRealm
to read the groups/roles of the authenticated principal. However, the TomcatAuthenticator
interface does not support "stacking" or "chaining" of authenticators, nor is it possible to configure different authenticators for different paths in the application; only one authenticator is supported perContext
. Therefore it would have been necessary to run multiple instances of the application; one using SPNEGO authentication and the other using the existing authenticator (SSLAuthenticatorWithFallback
). -
A variation of (1) this approach would be to modify
SSLAuthenticatorWithFallback
, which currently authenticates the client certificate (if present) otherwise falls back to BASIC authentication, to also support SPNEGO authentication. The existing pattern of usingHttpServletRequestWrapper
to attempt authentication and falling back to another method if authentication fails should apply, with some modifications, to usingSpnegoAuthenticator
alongsideSSLAuthenticator
andBasicAuthenticator
. This approach would support the existing deployment layout but retains the drawbacks of usingJNDIRealm
or additional behaviour inPKIRealm
to look up group membership. Realms, unlike Authenticators, can be composed usingCombinedRealm
.
Tomcat must provide a java.security.Principal
object representing the remote user. The principal can be retrieved via HTTPServletRequest.getUserPrincipal()
.
Currently, each PKI instance defines a single AJP 1.3 Connector (port 8009 by default), with the tomcatAuthentication
attribute not specified (defaulting to true
). If an AJP request carries remote user information, it is not propagated from the AJP request to the Catalina Request
. In order to propagate remote user information from an AJP request to the HTTPServletRequest
, it suffices to configure the connector with tomcatAuthentication="false"
.
When tomcatAuthentication="false"
, Tomcat Authenticators are still invoked, but all Authenticator classes shipped with Tomcat short-circuit if they observe that the HTTPServletRequest
already bears a Principal
. Dogtag’s SSLAuthenticatorWithFallback
exhibits the same behaviour, because it merely invokes Tomcat Authenticator instances.
Per the AJP Connector documentation, an externally authenticated Principal
does not have any roles associated with it. Group or role membership information provided in the request environment (by mod_lookup_identity
) must be added to the Principal
.
The class of the Principal
in the request is CoyotePrincipal
, which does not have any roles, nor any method to add roles.
A Valve
called ExternalAuthenticationValve
shall be implemented, which reads REMOTE_USER_GROUP_*
request attributes provided by mod_lookup_identity
and constructs a new principal value, copying data from the original Principal
and adding the roles and request attributes. It then calls org.apache.catalina.connector.Request.setUserPrincipal()
to replace the principal in the Request
. Due to the CMS dependencies of the PKIPrincipal
class, the new principal shall have the type ExternalPrincipal
, which is a new class that extends org.apache.catalina.realm.GenericPrincipal
with an attribute that stores the Coyote request attributes (so that the KRB5CCNAME
attribute that gets set by mod_auth_gssapi
can be propagated through the system).
The ExternalAuthenticationValve
shall cache the externally authenticated principal (if any) in the session.
Many parts of Dogtag assume or require that the principal is an instance of PKIPrincipal
(which has an IAuthToken
) or, roughly equivalently, that an IAuthToken
and IUser
are available in the SessionContext
.
Due to external authentication this assumption or requirement no longer holds; the externally authenticated principal will not be an instance of PKIPrincipal
and consequently will not provide an IAuthToken
.
It is proposed to provide a new implementation of IAuthToken
called ExternalAuthToken
that wraps a GenericPrincipal
and provides reasonable values for particular attribute keys where possible. Code that currently calls PKIPrincipal.getAuthToken()
will be updated to acquire or construct an IAuthToken
value according to the type of the principal.
The AuthMethodInterceptor
is used to restrict access to resources based on the authentication method (specifically, the name of the IAuthManager
instance) that was used to authenticate a PKIPrincipal
.
Because an externally authenticated GenericPrincipal
does not have an associated IAuthManager
, AuthMethodInterceptor
shall be enhanced to handle externally authenticated principals. Two approaches to deal with this are possible:
-
If it is satisfactory to treat all external authentication methods homogeneously, infer that any principal that is not a
PKIPrincipal
was externally authenticated and set the theauthManager
name toexternal
. -
If fine-grained access control based on different external authentication methods is needed, extend
GenericPrincipal
with a property to store the authentication type, and updateAuthMethodInterceptor
to read it (similarly to how it reads theTOKEN_AUTHMGR_INST_NAME
from theIAuthToken
of aPKIPrincipal
.)
In either case, the default auth-method.properties
file shall be updated to allow SPNEGO authentiction at all API endpoints.
Authorization is currently performed by asking the AuthzSubsystem
to use a named IAuthzManager
to evaluate whether a principal (represented by an IAuthToken
object) is allowed to perform a particular operation against a particular kind of resource.
When an operation needs to be authorised, if the principal is a PKIPrincipal
, whatever IAuthzManager
is currently used shall continue to be used. PKIPrincipal.getAuthToken()
provides the IAuthToken
object.
If the principal is an ExternalPrincipal
, the name of the IAuthzManager
to query shall be looked up via the AuthzSubsystem.getAuthzManagerNameByRealm
method. The realm is the part of the principal name after the @
symbol. Accordingly, IAuthzManager
plugin instances that will be used for external principals must have the realm configuration set in CS.cfg
, e.g to define an authorisation plugin instance for authenticating principals in the EXAMPLE.COM
realm:
authz.instance.IPAAuthz.pluginName=DirAclAuthz authz.instance.IPAAuthz.realm=EXAMPLE.COM authz.instance.IPAAuthz.ldap=internaldb authz.instance.IPAAuthz.searchBase=cn=IPA.LOCAL,cn=aclResources
The IAuthToken
created for externally authenticated principals shall be an instance of ExternalAuthToken
.
To support multiple DirAclAuthz
instances sharing a single ldap
connection whilst loading different sets of ACLs for different realms, DirAclAuthz
shall learn the searchBase
configuration parameter, which allows an alternative base DN to be specified.
-
AuthzSubsystem.checkRealm()
can check authorisation for an operation in a particular realm. TheIAuthzSubsystem
lookup by realm is performed internally (the caller must still provide the realm name). The realm name gets prepended to the resource*, thenIAuthzManager.authorize()
is invoked. The advantage of this approach is that no additional authz managers are required. The disadvantage is that all ACLs for all realms live alongside each other (in the case ofDirAclAuthz
, in a single LDAP entry, though they are distinguished by resource prefix).
For authorising FreeIPA principals to perform Dogtag administrative operations (e.g. managing certificate profiles or lightweight CAs), an additional instance of the DirAclAuthz
plugin can be defined in CS.cfg
and configured to load ACLs from a different entry (or entries).
The ACLs themselves shall be managed by FreeIPA and can contain references to FreeIPA users, groups and permissions, e.g. cn=admins,cn=groups,cn=accounts,dc=example,dc=com
or cn=System: Add CA,cn=permissions,cn=pbac,dc=example,dc=com
.
The main advantage of this approach is that it allows Dogtag to enforce access controls defined in the external IdP. A specific IAuthzManager
plugin is configured for each IdP. To authorise an operation, the plugin’s authorize
method is invoked with an IAuthToken
(which contains the principal’s name and groups), resource and operation, and it evaluates access. It is possible for the plugin to communicate with other systems.
The FreeIPA KRA backend currently uses pki.kra.KRAClient
, therefore we can use SPNEGO authentication for CA operations whilst continuing to use the KRA agent certificate to authenticate KRA operations; no special treatment of the KRA is required.
Eventually we may want to change the KRA client to use GSS-API proxy authentication. This work would be tackled in a separate design.
A new LDAP entry or entries containing ACLs for an external realm will be required in most external authentication use cases.
Some new configuration is needed; in particular:
-
Addition and instantiation of
SessionAuthentication
profile authenticator inCS.cfg
. -
Addition and instantiation of
ExternalProcessConstraint
profile constraint inregistry.cfg
.
It must be possible to log into the REST API (GET /ca/rest/account/login
) using SPNEGO. It is possible to offer SPNEGO login at a separate path or guarded by a query parameter if it is not possible to offer a single login resource that can handle SPNEGO, TLS client certificate or BASIC authentication.
Some parts of the API must be accessible by unauthenticated principals (e.g. OCSP responder). The authenticating proxy must be configured to not require SPNEGO authentication (or any other authentication) at these resources.
Some parts of the REST API are intended to be used with a cookie-based session, subsequent to prior authentication at the /ca/rest/account/login
resource. The login resource shall support SPNEGO authentication.
Dogtag (or Apache on behalf of Dogtag, or gssproxy
on behalf of both) must have access to the keytab for the Dogtag service principal. The keytab, being secret key material, must be appropriately secured.
The connection between Apache and Tomcat must be secure from external access because it carries identity assertions that are trusted by Dogtag. External hosts or processes other than Apache must not be able to send data to Tomcat that contains forged identity assertions.
We may wish to enhance the pki
CLI to be able to perform SPNEGO authentication, using standard credential caches available to the client program. (This is not an initial requirement for the FreeIPA use case).
Depending on the Dogtag principal name (HTTP/<hostname>
versus dogtag/<hostname>
) standard SPNEGO implementations may not be appropriate or may require enhancement, because the standard behaviour is to acquire a ticket for HTTP/<hostname>
.
A client may need to contact the KDC to acquire a service ticket for Dogtag. Tickets should be cached.
SPNEGO typically requires two round trips to the HTTP server, increasing latency and server load. Mitigations include:
-
Use SPNEGO to authenticate to a login resource that issues a session cookie. Present the cookie in subsequent requests, avoiding the need for SPNEGO authentication.
-
If the client knows that it will be required to perform SPNEGO, it can acquire a service ticket and send the
Authorization: Negotiate …
header in the first request, avoiding the initial 401 response that would otherwise occur.
If using GSS-API authentication, Apache must be set up in front of clones. This could (eventually) be done by pkispawn
. If initial use cases already require Apache (this is the case for FreeIPA), this work can be deferred.
If clones are accessed via different hostnames, each clone must have its own service principal in the Kerberos database, and Apache must use that principal’s keytab. If clones are accessed via a single hostname (e.g. load balanced or multiple DNS records) a shared keytab must be used. See Simo’s blog post for more detail about load balancing and Kerberos.
When deployed with FreeIPA, in addition to configuring Apache, constrained delegation support must be set up. The FreeIPA framework shall use S4U2Proxy to acquire a ticket for Dogtag, on behalf of the authenticated principal.
pkispawn
shall learn a new configuration option for deploying pki-tomcatd
with the AJP connector configured with tomcatAuthentication="false"
. The default behaviour shall be to deploy without this setting.
pkispawn
shall learn a new configuration option for deploying pki-tomcatd
with the AJP connector configured with a requiredSecret
. The default behaviour shall be to not set this option.
Developers cannot assume that the class of an authenticated Principal
is PKIPrincipal
.
-
Apache,
mod_auth_gssapi
,mod_lookup_identity
What is the impact on the docs of this change? Specifically, which docs and man pages need to be modified?
Tip
|
To find a page in the Wiki, enter the keywords in search field, press Enter, then click Wikis. |