{{FreeIPA plugin to allow ID Overrides as members of IPA groups|version=|ticket=TODO|author=abokovoy}}

== Overview ==

=== A proof-of-concept plugin to support Active Directory users as members of IdM groups ===

This plugin for FreeIPA allows to add Active Directory users as members of
FreeIPA groups.  As result, Active Directory users can manage FreeIPA resources
if these groups are part of appropriate roles. For example, adding an Active
Directory user as a member of 'admins' group would make it equivalent to
built-in FreeIPA 'admin' user.

The plugin works by allowing user ID overrides from the `Default Trust View` in FreeIPA
to be members of IdM groups. User ID overrides in the `Default Trust View` can only be
created for Active Directory users. When Active Directory user authenticates
with GSSAPI against the FreeIPA LDAP server, its Kerberos principal is
automatically mapped to the user's ID override in the `Default Trust View`.
LDAP server's access control plugin uses membership information of the
corresponding LDAP entry to decide how access can be allowed.

Note that we consider this approach as an interim solution. A proper solution
is to make sure actual group membership of a user from its Kerberos ticket is
used to consider membership in the LDAP groups.  This approach requires a
number of changes, in 389-ds LDAP server and in Cyrus-SASL library, to allow
dynamic discovery of the group information as supplied by a KDC in MS-PAC
structure (part of a Kerberos ticket). These changes are particularly invasive
but allow to re-use so-called `external groups` and external group members
already defined in FreeIPA.

== Use Cases ==


# As an Administrator in AD I want to also be able to fully administer FreeIPA as if I am an FreeIPA admin so that I do not have to have two different accounts and passwords.
# As an AD user I want to be able to use self service features of FreeIPA Web UI for example to upload my SSH keys or change other related to me data that is managed in FreeIPA on my behalf.
# As an AD user or Admin I want to be able to access FreeIPA Web UI with SSO if I have a valid kerberos ticket
# As an AD user or Admin I want to be able to access FreeIPA Web UI and be prompted for user name and password
# As an AD user who is assigned appropriate privileges in FreeIPA, I'd like to be able enroll FreeIPA hosts.
# As an AD user who is assigned appropriate privileges in FreeIPA, I'd like to be able to promote FreeIPA hosts to replicas.
# As an AD user who is assigned appropriate privileges in FreeIPA, I'd like to be able to issue certificates for FreeIPA resources.

== Design==

=== FreeIPA integration ===

FreeIPA allows to associate an ID override with an Active Directory user. This
user ID override stores information about user-specific POSIX attributes: POSIX
IDs for explicitly defined ID range case, home directory, user's shell, SSH
public keys, and associated user certificates. ID overrides can be defined in a
number of ID views, with `Default Trust View` always applied by SSSD whenever
information about this AD user is requested in the FreeIPA realm.

Existence of the user ID override in the `Default Trust View` also allows this
user to bind to FreeIPA LDAP server when using GSSAPI authentication. This is a
feature that FreeIPA Web UI utilizes to provide a web interfaces for a
self-service of Active Directory users.

The plugin `idoverride-memberof` relies on the following functionality of FreeIPA:

# Ability to extend set of objects the could be members of IPA groups
# Ability to propagate membership information from groups to its members performed by `memberof` plugin in 389-ds
# Ability to return information of an ID override object as part of the `whoami` IPA command
# Ability to request and receive membership information of an ID override object
# Ability to change default Web UI view based on the membership information of an ID override object

=== Plugin operation ===

Upon FreeIPA management framework startup `idoverride-admemberof` plugin
performs following actions:

# Allow objects of `idoverrideuser` type to be members of IPA groups
# Register a pre-callback with `group_add_member` command to add `nsMemberOf` object class back to idoverrideuser objects in case they miss it, to allow memberof plugin to propagate group membership
# Allow idoverrideuser objects to have `nsMemberOf` object class

Finally, the most complex part: in a case FreeIPA management framework has a
broken `baseidoverride.get_dn()` implementation, replace it with a correct
version See https://pagure.io/freeipa/issue/7255 for details. The code in the
plugin relies on C Python code objects interface which should work for both
Python 2.7 and Python 3.

==== Web UI plugin ====

Web UI component of `idoverride-admemberof` plugin extends Web UI logic to
decide whether default Web UI view should have a full menu or should be
confined to a self-service interface. Standard logic in FreeIPA Web UI is
to combine two facts:

# for IPA users membership in `admins` group is used to indicate full menu should be shown
# for AD users the fact that ID override object is presented by IPA `whoami` command is used to confine to a self-service interface

The `idoverride-admemberof` Web UI plugin adds support for analysing `memberof`
property of an ID override object. Since this information is returned in a
different format than standard Web UI code expects, a separate plugin is
needed.

The plugin calls standard Web UI self-service detection code and in case we are
dealing with an Active Directory user further looks in to `memberOf`
information. If AD user is member of an admins group (any of a DNs provided as
part of `memberOf` attribute starts with `cn=admins,cn=groups,` string), it is
considered to be allowed to use a full Web UI view.

=== Access Control ===

389-ds LDAP server implements access control with the help of `acl` plugin.
This plugin relies on a membership information of the LDAP object of the
identity currenly bound to the LDAP server connection.

The `acl` plugin retrieves list of groups the bound LDAP object belongs to and
then evaluates each access based on the membership information and access
control lists (ACLs). Most if not all FreeIPA ACLs rely on the group membership
through the role/privilege/permission concepts.

If an active LDAP bound object belongs to certain groups and these groups
mentioned in the ACLs, this LDAP bound object will be allowed to perform those
LDAP operations which allowed through the ACLs.

=== Compatibility to older FreeIPA versions ====

`idoverride-admemberof` plugin requires FreeIPA 4.5 or later. An integration on
the framework level actually requires FreeIPA 4.4.1 to allow external plugins.
However, integration with Web UI relies on an Active Directory self-service Web
UI feature which is part of FreeIPA 4.5. In particular, FreeIPA 4.5 provides
`whoami` command that is used to find out whether logged-in user is represented
by an ID override.

=== Upgrade and migration ===

`idoverride-admemberof` plugin is designed to be resistant over future FreeIPA
changes. When a proper solution to Active Directory users' management of
FreeIPA is implemented, all what would be needed is to remove direct group
membership of an ID override. A future FreeIPA solution will make sure to
de-duplicate multiple membership information coming through static `memberof`
and dynamic SID/ipaExternalMember group membership discovery.

The plugin does not require any schema updates because `nsMemberOf` is part of
a core 389-ds LDAP schema.

== Usage of a plugin ==

In order to allow Active Directory user to manage FreeIPA, following steps should be done:

# An ID override for the user should be created in the `Default Trust View`
# An ID override for the user should be added to an IPA group
# This group should be part of a role/privilege that allows modification/management of any IPA object

For Web UI the logic is a bit different from the one used for IPA users. If AD
user is a member of any role/privilege/permission, it is provided with a full
interface. If such user is not a member of any role/privilege/permission, only
self-service page would be available.

Unlike Web UI, IPA CLI relies on the access controls applied by the FreeIPA
LDAP server and does not hide any commands. Thus full administration
capabilities are available through the IPA CLI.

=== Sample usage ===

1. As admin, add ID override for a user (ab@ad.ipa.cool):

	[root@nyx ~]# ipa idoverrideuser-add 'default trust view' ab@ad.ipa.cool
	---------------------------------------
	Added User ID override "ab@ad.ipa.cool"
	---------------------------------------
	 Anchor to override: ab@ad.ipa.cool

	[root@nyx ~]# ipa idoverrideuser-show 'default trust view' ab@ad.ipa.cool --all --raw
	 dn: ipaanchoruuid=:SID:S-1-5-21-1356309095-650748730-1613512775-1127,cn=Default Trust View,cn=views,cn=accounts,dc=xs,dc=ipa,dc=cool
	 ipaanchoruuid: :SID:S-1-5-21-1356309095-650748730-1613512775-1127
	 ipaoriginaluid: ab@ad.ipa.cool
	 objectClass: ipaOverrideAnchor
	 objectClass: top
	 objectClass: ipaUserOverride
	 objectClass: ipasshuser
	 objectClass: ipaSshGroupOfPubKeys

Note objectclasses and a lack of 'memberOf'.

2. Add ab@ad.ipa.cool to 'admins' group:

	[root@nyx ~]# ipa group-add-member admins --idoverrideusers=ab@ad.ipa.cool
	 Group name: admins
	 Description: Account administrators group
	 GID: 1536000000
	 Member users: admin, simo
	 Member groups: ad-admins-external
	-------------------------
	Number of members added 1
	-------------------------

We don't display these ID override members yet but you can see "Number
of members added 1" – this is a successful operation.

3. Indeed, ID override now has 'nsMemberOf' object class and memberOf
attribute propagated:

	[root@nyx ~]# ipa idoverrideuser-show 'default trust view' ab@ad.ipa.cool --all --raw
	 dn: ipaanchoruuid=:SID:S-1-5-21-1356309095-650748730-1613512775-1127,cn=Default Trust View,cn=views,cn=accounts,dc=xs,dc=ipa,dc=cool
	 ipaanchoruuid: :SID:S-1-5-21-1356309095-650748730-1613512775-1127
	 ipaoriginaluid: ab@ad.ipa.cool
	 memberOf: cn=admins,cn=groups,cn=accounts,dc=xs,dc=ipa,dc=cool
	 memberOf: cn=Replication Administrators,cn=privileges,cn=pbac,dc=xs,dc=ipa,dc=cool
	 memberOf: cn=Add Replication Agreements,cn=permissions,cn=pbac,dc=xs,dc=ipa,dc=cool
	 memberOf: cn=Modify Replication Agreements,cn=permissions,cn=pbac,dc=xs,dc=ipa,dc=cool
	 memberOf: cn=Read Replication Agreements,cn=permissions,cn=pbac,dc=xs,dc=ipa,dc=cool
	 memberOf: cn=Remove Replication Agreements,cn=permissions,cn=pbac,dc=xs,dc=ipa,dc=cool
	 memberOf: cn=Modify DNA Range,cn=permissions,cn=pbac,dc=xs,dc=ipa,dc=cool
	 memberOf: cn=Read PassSync Managers Configuration,cn=permissions,cn=pbac,dc=xs,dc=ipa,dc=cool
	 memberOf: cn=Modify PassSync Managers Configuration,cn=permissions,cn=pbac,dc=xs,dc=ipa,dc=cool
	 memberOf: cn=Read LDBM Database Configuration,cn=permissions,cn=pbac,dc=xs,dc=ipa,dc=cool
	 memberOf: cn=Add Configuration Sub-Entries,cn=permissions,cn=pbac,dc=xs,dc=ipa,dc=cool
	 memberOf: cn=Read DNA Range,cn=permissions,cn=pbac,dc=xs,dc=ipa,dc=cool
	 memberOf: cn=Host Enrollment,cn=privileges,cn=pbac,dc=xs,dc=ipa,dc=cool
	 memberOf: cn=System: Add krbPrincipalName to a Host,cn=permissions,cn=pbac,dc=xs,dc=ipa,dc=cool
	 memberOf: cn=System: Enroll a Host,cn=permissions,cn=pbac,dc=xs,dc=ipa,dc=cool
	 memberOf: cn=System: Manage Host Certificates,cn=permissions,cn=pbac,dc=xs,dc=ipa,dc=cool
	 memberOf: cn=System: Manage Host Enrollment Password,cn=permissions,cn=pbac,dc=xs,dc=ipa,dc=cool
	 memberOf: cn=System: Manage Host Keytab,cn=permissions,cn=pbac,dc=xs,dc=ipa,dc=cool
	 memberOf: cn=System: Manage Host Principals,cn=permissions,cn=pbac,dc=xs,dc=ipa,dc=cool
	 objectClass: ipaOverrideAnchor
	 objectClass: top
	 objectClass: ipaUserOverride
	 objectClass: ipasshuser
	 objectClass: ipaSshGroupOfPubKeys
	 objectClass: nsMemberOf

4. Can we use it? Let's see:

	[root@nyx ~]# kdestroy -A
	[root@nyx ~]# kinit ab@AD.IPA.COOL
	Password for ab@AD.IPA.COOL: [root@nyx ~]# ipa group-show some-new-group
	ipa: ERROR: some-new-group: group not found
	[root@nyx ~]# ipa group-add some-new-group
	----------------------------
	Added group "some-new-group"
	----------------------------
	 Group name: some-new-group
	 GID: 1536000052
	[root@nyx ~]# ldapsearch -H `cat /etc/ipa/default.conf |grep ldap_uri|cut -d' ' -f3` -LLL  -o ldif-wrap=no \
				 -b dc=xs,dc=ipa,dc=cool cn=some-new-group '*' creatorsname createtimestamp
	SASL/EXTERNAL authentication started
	SASL username: gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth
	SASL SSF: 0
	dn: cn=some-new-group,cn=groups,cn=accounts,dc=xs,dc=ipa,dc=cool
	cn: some-new-group
	objectClass: top
	objectClass: groupofnames
	objectClass: nestedgroup
	objectClass: ipausergroup
	objectClass: ipaobject
	objectClass: posixgroup
	objectClass: ipantgroupattrs
	ipaUniqueID: ffc0e82e-c5e2-11e7-9677-001a4a62eb77
	gidNumber: 1536000052
	ipaNTSecurityIdentifier: S-1-5-21-570121326-3336757064-1157332047-1052
	creatorsname: ipaanchoruuid=:sid:s-1-5-21-1356309095-650748730-1613512775-1127,cn=default trust view,cn=views,cn=accounts,dc=xs,dc=ipa,dc=cool
	createtimestamp: 20171110064720Z

	[root@nyx ~]# ipa group-remove-member admins --idoverrideusers=ab@ad.ipa.cool
	 Group name: admins
	 Description: Account administrators group
	 GID: 1536000000
	 Member users: admin, simo
	 Member groups: ad-admins-external
	---------------------------
	Number of members removed 1
	---------------------------
	[root@nyx ~]# ipa group-add some-new-group-1
	ipa: ERROR: Insufficient access: Insufficient 'add' privilege to add the entry 'cn=some-new-group-1,cn=groups,cn=accounts,dc=xs,dc=ipa,dc=cool'.

	[root@nyx ~]# ldapsearch -H `cat /etc/ipa/default.conf |grep ldap_uri|cut -d' ' -f3` -LLL  -o ldif-wrap=no \
				 -b dc=xs,dc=ipa,dc=cool ipaoriginaluid=ab@ad.ipa.cool
	SASL/EXTERNAL authentication started
	SASL username: gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth
	SASL SSF: 0
	dn: ipaanchoruuid=:SID:S-1-5-21-1356309095-650748730-1613512775-1127,cn=Default Trust View,cn=views,cn=accounts,dc=xs,dc=ipa,dc=cool
	ipaAnchorUUID:: OlNJRDpTLTEtNS0yMS0xMzU2MzA5MDk1LTY1MDc0ODczMC0xNjEzNTEyNzc1LTExMjc=
	objectClass: ipaOverrideAnchor
	objectClass: top
	objectClass: ipaUserOverride
	objectClass: ipasshuser
	objectClass: ipaSshGroupOfPubKeys
	objectClass: nsMemberOf
	ipaOriginalUid: ab@ad.ipa.cool

	[root@nyx ~]# klist Ticket cache: KEYRING:persistent:0:krb_ccache_QfT6NDp
	Default principal: ab@AD.IPA.COOL

	Valid starting     Expires            Service principal
	11/10/17 07:47:13  11/10/17 17:46:51  HTTP/nyx.xs.ipa.cool@XS.IPA.COOL
	    renew until 11/11/17 07:46:47
	11/10/17 07:47:12  11/10/17 17:46:51  krbtgt/XS.IPA.COOL@AD.IPA.COOL
	    renew until 11/11/17 07:46:47
	11/10/17 07:46:51  11/10/17 17:46:51  krbtgt/AD.IPA.COOL@AD.IPA.COOL
	    renew until 11/11/17 07:46:47

When user is removed from the admins group, it immediately becomes powerless.
The 'nsMemberOf' objectclass still there but memberOf plugin removed all
the membership.

