Category Archives: Uncategorized

Migration and User Private Groups

When adding an IPA user they are typically created with a User-Private Group (UPG). This is a group of the same name, with the same GID. It is treated specially in that it cannot have members and does not typically appears in group searches using the IPA API (unless the private option is included).

Migrating from another LDAP source, including another IPA server, does not create UPGs. There are a number of reasons for this:

  1. It can be expensive to be sure that no groups reference any given user
  2. What to do if one group cannot be made into a UPG.  There is no interactive mode so it is either skip it, add it as a non-UPG or drop the group members and add it as a UPG.

We took the easy way out and don’t convert any. There is an RFE to be able to do this during migration.

This came up recently on the freeipa-users list and I thought about what it would take to convert a group back into a UPG.

My first solution was a rather compex set of ldapmodify operations.

One to update the group:

$ kinit admin
$ ldapmodify -Y GSSAPI
dn: cn=test,cn=groups,cn=accounts,dc=example,dc=com
changetype: modify
add: objectclass
objectClass: mepManagedEntry
-
add: mepManagedBy
mepManagedBy: uid=test,cn=users,cn=accounts,dc=example,dc=com
- 
delete: objectclass
objectClass: ipausergroup
-
delete: objectclass
objectClass: groupofnames
-
delete: objectclass
objectClass: nestedgroup

^D

And one to update the user:

$ ldapmodify -Y GSSAPI
dn: uid=test,cn=users,cn=accounts,dc=example,dc=com
changetype: modify
add: objectclass
objectClass: mepOriginEntry
-
add: mepManagedEntry
mepManagedEntry: cn=test,cn=groups,cn=accounts,dc=example,dc=com

^D

This seemed cumbersome especially if there are a lot of groups to convert. It also doesn’t consider the case where a group has a member so is nonconvertible. An ObjectclassViolation LDAP error would be thrown in that case.

So I poked at the group-detach command and came up with this. If you drop this file, attach.py, into /usr/lib/python-*/site-packages/ipaserver/plugins and restart Apache you’ll have the group-attach command:

import six

from ipalib import Str
from ipalib.plugable import Registry
from .baseldap import (
    pkey_to_value,
    LDAPQuery
)
from ipalib import _, ngettext
from ipalib import errors
from ipalib import output

register = Registry()


@register()
class group_attach(LDAPQuery):
    __doc__ = _('Attach a managed group to a user.')

    takes_parms = (
        Str('user',
            doc=_('User to attach group to'),
            flags=['no_create', 'no_update', 'no_search'],
        )
    )

    has_output = output.standard_value
    msg_summary = _('Attached group "%(value)s" to user "%(value)s"')

    def execute(self, *keys, **options):
        """
        This requires updating both the user and the group. We first need to
        verify that both the user and group can be updated, then we go
        about our work. We don't want a situation where only the user or
        group can be modified and we're left in a bad state.
        """
        ldap = self.obj.backend

        group_dn = self.obj.get_dn(*keys, **options)
        user_dn = self.api.Object['user'].get_dn(*keys)

        try:
            user_attrs = ldap.get_entry(user_dn)
        except errors.NotFound:
            raise self.obj.handle_not_found(*keys)
        is_managed = self.obj.has_objectclass(
            user_attrs['objectclass'], 'mepmanagedentry'
        )
        if (not ldap.can_write(user_dn, "objectclass") or
                not ldap.can_write(user_dn, "mepManagedEntry")
                and is_managed):
            raise errors.ACIError(
                info=_('not allowed to modify user entries')
            )

        group_attrs = ldap.get_entry(group_dn)
        is_managed = self.obj.has_objectclass(
            group_attrs['objectclass'], 'mepmanagedby'
        )
        if (not ldap.can_write(group_dn, "objectclass") or
                not ldap.can_write(group_dn, "mepManagedBy")
                and is_managed):
            raise errors.ACIError(
                info=_('not allowed to modify group entries')
            )

        objectclasses = user_attrs['objectclass']
        if 'meporiginentry' in [x.lower() for x in objectclasses]:
            raise errors.ACIError(
                info=_('The user is already attached to a group')
            )

        group_attrs = ldap.get_entry(group_dn)
        objectclasses = group_attrs['objectclass']
        if 'mepmanagedentry' in [x.lower() for x in objectclasses]:
            raise errors.ACIError(
                info=_('The group is already managed')
            )
        if group_attrs.get('member'):
            raise errors.ACIError(
                info=_('The group has members')
            )

        for objectclass in ('ipausergroup', 'groupofnames', 'nestedgroup'):
            try:
                i = objectclasses.index(objectclass)
            except ValueError:
                # this should never happen
                pass
            del objectclasses[i]

        objectclasses.append('mepManagedEntry')

        group_attrs['mepManagedBy'] = user_dn
        group_attrs['objectclass'] = objectclasses
        ldap.update_entry(group_attrs)

        try:
            user_attrs['objectclass'].append('mepOriginEntry')
            user_attrs['mepManagedEntry'] = group_dn
            ldap.update_entry(user_attrs)
        except ValueError:
            # Somehow the user isn't managed, let it pass for now. We'll
            # let the group throw "Not managed".
            pass

        return dict(
            result=True,
            value=pkey_to_value(keys[0], options),
        )

This leaves some things to be desired, notably the exceptions are ACIError rather than something perhaps more relevant.

$ ipa group-detach test
--------------------------------------
Detached group "test" from user "test"
--------------------------------------

$ ipa group-attach test
------------------------------------
Attached group "test" to user "test"
------------------------------------

There be dragons. I have barely tested this, just enough to scratch the itch of my curiosity.

Advertisements

Peer certificate cannot be authenticated with given CA certificates

I’ve seen this twice now so I’m documenting the fix so I don’t have to go hunting again.

Basically it starts as a typical “oh crap my certs expired” question on #freeipa or freeipa-users. Sadly the usual things don’t seem to help (go back in time).

The last time this happened there was the added twist that the renewal master was gone so we had to first reconfigure a replica to do the renewal (you do have more than one CA right? RIGHT?)

Anyway, he would persistently get the Peer certificate cannot be authenticated message. We tried:

  • Confirming that ipaCert was the correct value in the IPA RA entry in LDAP
  • The CA is up and running: curl --cacert /etc/ipa/ca.crt -v https://`hostname`:8443/ca/ee/ca/getCertChain
  • Ensuring that certutil -L -d /etc/pki/pki-tomcat/alias-n 'caSigningCert cert-pki-ca' -a and cat /etc/ipa/ca.crt are the same cert

Then I remembered a fellow Red Hatter had reported a similar issue and discovered that the fix was to reset the NSS trust flags in the Apache NSS database (which certmonger uses).

# SSL_DIR=/etc/httpd/alias/ curl -v -o /dev/null --cacert /etc/ipa/ca.crt https://`hostname`:8443/ca/agent/ca/profileReview

You should get client certificate not found. If you don’t then try this:

# certutil -M -d /etc/httpd/alias -n 'EXAMPLE.COM IPA CA' -t ,,
# certutil -M -d /etc/httpd/alias -n 'EXAMPLE.COM IPA CA' -t CT,C,C

See https://lists.fedorahosted.org/archives/list/freeipa-users@lists.fedorahosted.org/thread/XSMWWPJU2VRUIGE6SRAHYAJF7BYBCNOE/