This work relates to QPID-7092.
The Java Broker model has an authentication provider associated with each port. This means that a ssingle Broker may be configured to use more than one provider at once. For instance is is possible to use LDAP for messaging authentication and use OAUTH2 for management.
Currently the user's identity is represented by a simple string which contains the user's common name. This gives rise to the possibility that a user with common name fred from one authentication provider is indistinguishable for a fred from another authentication provider.
This problem manifests currently:
- Groups are produced by mapping a common name to a group name. Currently we have no way of preventing all fred (a messaging user) from being put in the admins group because there is also fred who is an admin.
- The audit trail produced in the log contains only the common name. An action by one fred is indistinguishable from actions of another fred.
- The audit attributes of the configured object capture the common name . An queue created by fred carries a createdBy value 'fred'.
- The
owner
attribute of Queue also contains a common name. - An application producing a message captures the authenticated identity and records this in the
JMSXUserId
property of each message. If two applications, using separate AMQP ports using separate authentication provides both have a user called fred, the consuming application has no way to distinguish which fred produced the message. - For messages produced by client authenticating with
X-OAuth2
currently have noJMSXUserId
. This is because the messaging client is not aware of the authenticated common name - Sometimes common names aren't easily interpretted by a human user. For instance, in the case of Google's OAuth the common name is a 14 digit number. A operator examining using Qpid has no way to discover the human name of the user who performed an action.
The problem has a bearing on the High Availability (HA) use case too. There is already an implict restriction when using HA that all Brokers have authentication providers configured in the same way. If this were not the case, applications could not fail over between nodes in the cluster. It could also mean that the identities associated with configured objects (for instance a queue's createdBy attribute) or a message's JMSXUser becomes 'unresolvable' after the group has changed mastership.
The problem will also have a bearing on the federation case too.
Constraints
- User identities need to be representable as a string (so that they an be serialised as JSON)
- The user identity belongs to source identification system. Qpid should be capable of using these identifiers as they stand. It should not invent its own unique user id.
- The identification system may or may not permit an arbitrary user identity to resolved into the corresponding human name. For instance, some (LDAP) Directories require that that user authenticates (binds) before that information is returned.
- The messaging client must continue to authenticate using a common name
- We need the ability to group users together for purposes of access control (the admin group etc)
- Some authentication providers are capable of producing group membership information for the authenticated user. We want to be able to exploit these as a source of groups too.
Ideas
User identity
We discussed the idea of adopting RFC-4120 naming conventions where each common name would be qualified by a realm name:
<encoded common name>@<realm>
where realm name would be as per Section 6 of RFC-4120.
domain: ATHENA.MIT.EDU X500: C=US/O=OSF other: NAMETYPE:rest/of.name=without-restrictions
e.g.
keith@ATHENA.MIT.EDU
The AuthenticationProvider would change so that they contribute identities from one or more realms. Some AuthenticationProviders (e.g. Kerberos) would do this 'natively'. For other authentication providers that don't share the same notion, the the user would assign a synthetic realm. This would mean that all identities within Qpid followed the same realm qualified user identity scheme.
Perhaps an identity from a config backed SHA256 might look like:
keith@qpid:SCRAM-SHA256/myscramprovider
A business rule would ensure that no two providers yield identities from the same realm.
The AuthenticationProvider would acquire a responsibility to resolve an arbitrary realm qualified user identity into its human readable name. For some authentication providers, they may be able to perform a live lookup against the identity backend. For others this may be disallowed. To accomodate this case the authentication provider may cache certain details about users as they login, so later, those details can be returned. This information will allow the operator to know it was Joe Bloggs who created a queue or sent a message. This part of the system needs to be aware that some identities may not have human readable names (common in ids used for system to system communication).
Groups
Some authentication providers (such as Directories (LDAP) and Open ID) are able to produce group membership information about the user who is authenticating. For instance, in LDAP a query (&(objectCategory=group)(member=cn=Fletcher Christian,ou=people,o=sevenSeas
))
might give cn=HMS Bounty,ou=crews,ou=groups,o=sevenseas.
We need group identities to be unique and belong to the source system in the same was as user identities. We said we'd adopt the same realm qualified scheme:
admin@ATHENA.MIT.EDU
HMS%20Bounty@o=sevenseas/ou=groups/ou=crews
The subject of the user would comprise its principal representing his user identity and principals representing all of the groups to which he belongs.
The some applications it might be sufficient for ACLs to be expressed in terms of the realm qualified group name produced by the authentication provider i.e. no group provider would be required.
ALLOW admin@ATHENA.MIT.EDU ACCESS BROKER
To support more complicate use-cases, it will be desirable to have a group provider that can contribute additional identities to a user's identity based on rules. This would allow logical expressions to be formed such as 'add identity X if she is already a Y or Z' or 'add identity T if the user is not an S'. The group provider would produces realm qualified identities too.
We recognise some sources of group information are separate from authentication providers, for instance, the CloudFoundryDashboardManagementGroupProvider. We have the general notion that a GroupProvider is something capable of contributing additional identities for a user based on identities that already hold. After authentication, Qpid should interrogate each group provider in turn until no a complete round where additional identities are produced, at which point we will no the Subject is complete.
Messaging
The messaging client currently knows only the common name which it used to identify itself to Qpid. It also uses this common name when populating the JMSXUser property in the message header of produced messages. We will want the JMSXUser to be populated with the realm qualified identity.
The Broker would need to be aware that older clients may in use and this would still send the
common name. The Broker should tolerate such messages (perhaps configurable).
RG: One aspect of this is that all clients will need to behave in a compliant way... which really means we should probably propose something for AMQP 1.0 about how clients should populate fields on the message based on the credentials they authenticated with.
Upgrading from older versions
The configuration upgrader should upgrade the authentication providers to have realm (how would we manufacture the realm names). The lastUpdatedBy
and createdBy
attributes of existing objects and the owner
attribute of Queue could be upgraded too in the common case where only one provider is in use.
The store upgrader could apply the same rules and upgrade message headers (it is worth it?)
ACL are currently written in terms of common names (user ids or group id) - how would we update existing ACL rules? We talked about a notion of a default realm to which common names could be assumed to belong if they are not realm qualified. This might help in the common case where Qpid has one authentication provider.
Open questions
How does Kerberos handle groups?(see below)- KW thinks that the ACL parser won't currently like all realm qualified names. Parse will accept at-sign, dots, but not % sign (which would appear if user ids were URI escaped), equals nor slashes which would appear in an realm using an X500 name. Perhaps the ACL module needs to change to use quoted names and remove the restrictions on characters appearing within the quotes.
- ACL rules does not distinguish between user and group names. This means we cannot simply, to aid upgrade, assign an fallback realm to the AccessControlProvider and let it assume that any names without realms belong to the default.
Other issues
Currently the model is completely statically defined. A type (category instance) cannot have a private hierarchy of its own managed objects. This is pertinent to the Identity discussion as currently group providers have child and grandchild categories of Group and GroupMember but these only make sense to the group provider implementations that have total knowledge of all the members of a group as a FileGroupProvider. This is not normally the case. GroupProviders primary role is provide additional identities of the user. Most GroupProviders won't have the ability to manage the whole group. We talked about changing the REST API url to be fully hierarchal so that private categories could be accommodated.
Authentication
We might want to permit authentication where the user passes a realm qualified user name. This would be useful in the case where the client is not a Qpid client and would other populate the JMSXUserId with the 'wrong' value.
Background Info
Kerberos/Groups
A Kerberos ticket does convey authorization information but its seems this is not used much in the UNIX world. On Windows, the authorization
field is used to distribute a PAC (Privilege Attribute Certificate) which includes group memberships from an Active Directory. Java GSSAPI cannot read this information. There is at least one LGPL Java project (Jaas Lounge). It seems on a non-MS platform, then the advice is to query the Directory independently after the Kerberos authentication. This might call for a LDAPGroupProvider whose role is to only query groups for an already authenticated user.
Source: http://www.kerberos.org/software/appskerberos.pdf