# Splat: Scalable Periodic LDAP Attribute Transmogrifier

The content of this guide is the original work of Three Rings Design, Inc, Landon Fuller, and Nick Barkas. All rights reserved.

The XML and CSS used to generate this guide is based on the work of Will Barton and Michael Maibaum, as contributed to the DarwinPorts Project under the 3 clause BSD license. Their copyright remains.

2005 - 2008 Landon Fuller, Nick Barkas, and Three Rings Design, Inc.
2002 - 2004 The OpenDarwin Project

## About Splat

Splat was written by Landon Fuller landonf@threerings.net and Will Barton wbb4@opendarwin.org for the purpose of distributing SSH keys from LDAP in a way that did not require modifying the SSH daemon.

In the process, we designed a generic daemon capable of pulling nearly any information from LDAP and using it in any way you see fit. Synchronize your LDAP directory with a relational database, update an organizational chart, or build a x509 certificate revocation list. I encourage you to submit your local modules for inclusion with the next Splat release.

## Installing and Using Splat

### Installation

#### Prerequisites

Splat depends on the following components:

- Python 2.4

- Twisted Networking Framework

- Python LDAP

- ZConfig

Also, to use the OpenNMS plugin, the following additional components must be installed:

- pysqlite2

- Either cElementTree or Python 2.5, which includes this module.

#### Installing with the Python Distutils

Splat uses the standard Python distutils. To install, simply run `setup.py`:

    ./setup.py install

The Splat library will be installed in the Python site-packages directory. The `splatd` daemon will be installed in the Python-specified bin directory. An example configuration file, `splat.conf`, is supplied with the source distribution.

The Splat daemon will be installed in the bin directory as specific by Python. On certain operating systems, this may not be what you expect. For example, Mac OS X might install `splatd` in `/Library/Python/2.3/bin`. Feel free to move the binary after the installation is complete.

### Configuring Splat

#### splat.conf

Splat is configured by a configuration file usually named `splat.conf`. See the example config below (this config file is also included with the Splat source), and the splat.conf man page.

Example splat.conf

### Running Splat

#### splatd

`splatd` detaches from the terminal and executes your service modules according to the intervals specified in the configuration file. While the main distribution doesn't include any startup scripts, one is installed by the FreeBSD port, Ubuntu package, and DarwinPorts portfile for Splat. Writing one for other operating systems should be relatively straight-forward. Feel free to submit your scripts for inclusion. For daemon usage information, run `splatd` with no arguments, or see the splatd man page.

## Included Modules

### Introduction

Splat was originally written for the purpose of distributing SSH keys from LDAP in a way that did not require modifying the SSH daemon.

In the process, we designed a generic daemon capable of pulling nearly any information from LDAP and using it in any way you see fit. Synchronize your LDAP directory with a relational database, update an organizational chart, or build a x509 certificate revocation list. I encourage you to submit your local modules for inclusion with the next Splat release.

### Home Directories

The Home Directory service module will query LDAP for users, create a home directory if it does not already exist, copy in template home directory, and optionally run a user supplied post-create script.

#### LDAP Schema

The Home Directory service module uses the standard RFC 2307 (or 2307bis) schema.

#### Home Directory Service Options

The Home Directory service module supports sanity checking on the uid, gid, and home directory, home directory templates, and optional post-create script.

##### Home Directory Service Options

home

  Only create home directories that are subdirectories of the specified directory.

minuid

  Do not create home directories for uids lower than minuid.

mingid

  Do not create home directories for gids lower than mingid.

skeldir

  Path to a template or skeletal home directory. If this option is used, files named with a prefix of dot. such as `dot.filename` will be copied to `.filename` in the created home directory. Files without this dot. prefix will have the same name in the new home directory.

postcreate

  Execute a post-create command after the user's home directory has been created. The command is passed the new user's numeric uid, numeric gid, and the full path to the new home directory.

### OpenSSH Public Keys

The SSH service module will extract SSH keys from LDAP, optionally create a home directory if it does not already exist, apply any key command limitations specified by the administrator, and atomically overwrite the user's `authorized_keys` file.

#### LDAP Schema

To store the SSH keys in LDAP, we created an sshAccount schema in our Three Rings Enterprise OID space. The schema is included with the Splat distribution as `schema/ooossh.schema`. It depends on another included schema: `schema/ooo.schema` . The SSH schema defines a new auxiliary object class, sshAccount and an optional attribute, sshPublicKey. Feel free to use this schema with your own installation.

    attributetype ( oooSSHAttributes:1 NAME 'sshPublicKey'
            DESC 'OpenSSH Public Key'
            EQUALITY octetStringMatch
            SYNTAX 1.3.6.1.4.1.1466.115.121.1.40 )

    objectclass ( oooSSHClasses:1 NAME 'sshAccount'
            DESC 'OpenSSH Account'
            SUP top AUXILIARY
            MAY ( sshPublicKey ) )

#### SSH Service Options

The SSH service module supports `authorized_keys` command limitations and sanity checking on the uid, gid, and home directory.

##### SSH Service Options

makehome

  Create home directories for users. Set to `true` to enable home directory creation, but be aware that other plugins (such as the `homeDirectory` plugin) may also be configured to create the same home directory, with different options.

home

  Only write keys to subdirectories of the specified directory.

minuid

  Do not write keys for uids lower than minuid.

mingid

  Do not write keys for gids lower than mingid.

command

  Limit user to the specified command.

### User Mail Forward Files

The User Mail Forward service module was written by Kevin Van Vechten kevin@opendarwin.org of the OpenDarwin project. The module will extract the mailForwardingAddress attribute from LDAP, optionally create a home directory if it does not already exist, and atomically overwrite the user's `.forward` file.

#### LDAP Schema

The mailForwardingAddress attribute can be found in several schema definitions.

- As an optional attribute of the qmailUser object class as defined in the Qmail LDAP schema.

- As an optional attribute of the nsMessagingServerUser class in the Netscape LDAP schema.

#### User Mail Forwarding Service Options

The User Mail Forwarding service module supports sanity checking on the uid, gid, and home directory.

##### User Mail Forwarding Service Options

makehome

  Create home directories for users. Set to `true` to enable home directory creation, but be aware that other plugins (such as the `homeDirectory` plugin) may also be configured to create the same home directory, with different options.

home

  Only write forward files to subdirectories of the specified directory.

minuid

  Do not write forward files for uids lower than minuid.

mingid

  Do not write forward files for gids lower than mingid.

### Purge User Home Directories

The Home Directory Purging service module can archive, delete, and later delete archives of the home directories of users set to be purgeable in LDAP.

#### LDAP Schema

We created a purgeableAccount schema in the Three Rings Enterprise OID space to keep track of the date and time at which an LDAP user's home directory should be archived and deleted. The needed schema can be found in `schema/ooo.schema` and `schema/ooopa.schema` in the Splat distribution. These define a new object class purgeableAccount and a new optional attribute pendingPurge. The Home Directory Purging module utilizes the pendingPurge attribute to determine account purgability.

    attributetype ( oooPAAttributes:1 NAME 'pendingPurge'
            DESC 'Timestamp of pending deletion date'
            EQUALITY generalizedTimeMatch
            SYNTAX 1.3.6.1.4.1.1466.115.121.1.24 SINGLE-VALUE )

    objectclass ( oooPAClasses:1 NAME 'purgeableAccount'
            DESC 'Purgeable Account'
            SUP top AUXILIARY
            MAY ( pendingPurge ) )

#### Home Directory Purging Service Options

The Home Directory Purging module supports the following options. The `home`, `minuid` , and `mingid` options are not required to be set, but are useful for sanity checking.

home

  Only home directories that are subdirectories of the specified directory may be purged and/or archived.

minuid

  Do not purge/archive home directories for uids lower than minuid.

mingid

  Do not purge/archive home directories for gids lower than mingid.

archivehomedir

  Set to `true` to archive a user's home directory before purging it. Defaults to true.

purgehomedir

  Set to `true` to purge a user's home directory after the `pendingPurge` time in the user's LDAP entry. Defaults to true. N.B. if this option and `archivehomedir` are both set to `false`, the plugin will do nothing.

purgehomearchive

  Set to `true` to delete the archive created of a user's home directory. This action will take place `purgearchivewait` days after the archive was last modified (which will generally be when the archive was created). Defaults to true.

archivedest

  Directory that homedir archives will be created and left in. Defaults to `/home`.

purgearchivewait

  Number of days since its last modified time to wait before deleting a homedir archive. Defaults to 14.

### OpenNMS User Synchronization

The OpenNMS User Plugin was written to support synchronization of user contact information with the OpenNMS network management system. The plugin will synchronize users with OpenNMS 1.3's `users.xml` and `groups.xml`.

This plugin should be used in combination with OpenNMS's Acegi LDAP authentication support.

#### OpenNMS User Synchronization Service Options

The OpenNMS User Synchronization service module supports synchronizing contact information with arbitrary LDAP attributes.

##### OpenNMS Service Options

usersFile

  Location of the OpenNMS `users.xml` file.

groupsFile

  Location of the OpenNMS `groups.xml` file.

opennmsGroup

  Specifies the OpenNMS group in which to place the user. Future versions of this plugin are expected to support mapping of arbitrary LDAP groups to OpenNMS groups.

userNameAttribute

  LDAP attribute containing the user's login name (eg: uid).

fullNameAttribute

  LDAP attribute containing the user's full name (eg: cn).

commentsAttribute

  LDAP attribute containing OpenNMS user comments (eg: title).

emailAttribute

  LDAP attribute containing the user's primary e-mail address (eg: mail).

xmppAddressAttribute

  LDAP attribute containing the user's XMPP (Jabber) address.

numericPagerAttribute

  LDAP attribute containing the user's numeric pager number.

numericPagerServiceAttribute

  LDAP attribute containing the user's numeric pager service name.

textPager

  LDAP attribute containing the user's text pager number.

textPagerService

  LDAP attribute containing the user's text pager service name.

## Writing New Modules

### Introduction

Splat Helper Modules are implemented as a Python module containing a single subclass of `splat.plugin.Helper`. The subclass must implement three methods: attributes, parseOptions, and work.

An instance of the helper class is instantiated for each `Service Helper Configuration` in the `splat.conf` configuration file. The parseOptions method is passed a dictionary of key value pairs, as derived from any helper-specific `Option` configuration directives. Individual `Group` directives may supply group-specific options.

The parseOptions method is expected to return an opaque context for a given set of options. Later invocations of the work method will be passed a reference to this opaque context, an LDAP Entry instance, and a boolean value indicating whether the LDAP entry has been modified since the last time the helper has run successfully. The attributes method should just return a tuple of the LDAP attributes needed by a plugin.

### Helper Class Implementation

Splat Helper Modules are implemented as a Python module containing a single subclass of `splat.plugin.Helper`. The subclass must implement three methods: parseOptions, work, and attributes.

For a working example, refer to the SSH public key helper located in `splat/helpers/sshPublicKeys.py`.

Skeleton of Splat Helper Module

    class MyHelper(splat.plugin.Helper):
        @classmethod
        def attributes(self):
            ...
        @classmethod
        def parseOptions(self, options):
            ...
        def work(self, context, ldapEntry, modified):
            ...

#### Error Handling and Logging

If an error occurs, your module must raise an exception of type splat.plugin.SplatPluginError, with an error message provided as the second argument. This error will be presented to the user by the splat daemon.

    raise splat.plugin.SplatPluginError, "No such user"

Logging is provided by the standard logging module. To find the Splat logger, use `splat.LOG_NAME`:

    logger = logging.getLogger(splat.LOG_NAME)

#### Parsing Configuration Options

Helper-specific configuration options may be specified in the `splat.conf` configuration file. These options are passed to the helper's parseOptions method as a dictionary of key/value strings. These options are not validated by the configuration parser -- it is your responsibility to validate these strings and convert them to the appropriate data type.

Helper Module parseOptions() Method

    def parseOptions(self, options):
        context = {}

        for key in options.keys():
            if (key == 'minuid'):
                context['minuid'] = int(options[key])
                continue
            raise splat.plugin.SplatPluginError, "Invalid options '%s' specified." % key

        return context

#### Handling LDAP Entries

The splat daemon will perform an LDAP search on the helper's behalf, filter based on group restrictions, and pass individual LDAP Entry instances to the work method. The following method arguments are provided:

context

  The opaque configuration context corresponding with the supplied entry.

entry

  An LDAP Entry instance..

modified

  If true, the supplied LDAP entry has been modified since the last successful helper iteration. This can be used to avoid doing unnecessary work in the helper, such as overwriting identical ssh keys. `modified` will also be set to true if the `modifyTimestamp` attribute was not found in an LDAP entry. This helps ensure that helpers will still be able to function when this attribute is not available for whatever reason.

The Entry class provides two instance variables: `dn` and `attributes`. The `dn` variable provides the corresponding LDAP object's full `DN`, while `attributes` provides a dictionary of LDAP attributes and corresponding lists of values. The desired LDAP object attributes must be provided by the attributes() method of your splat.plugin.Helper subclass; these LDAP attributes will used in the LDAP search and will be returned if available.

In addition to any requested attributes, the `modifyTimestamp` operational attribute will always be returned. This can be used by your helper to determine if any work should be done; For example, the SSH Public Key helper will `stat()` the user's key file and compare the two modification dates to determine whether the key should be updated.

The helper's attributes() method defines the LDAP object attributes that you are interested in -- an LDAP Entry will still be returned if it does not contain all of your requested attributes, and/or the `modifyTimestamp` attribute. It is your responsibility to ensure that any attributes required by your helper are supplied.

Helper Module attributes() and work() Methods

    class MyHelper(splat.plugin.Helper):
        def attributes(self):
            return ('uid', 'sshPublicKey')
        def work(self, context, ldapEntry, modified):
            # If the entry has not been modified since our last run, skip it.
            if (not modified):
                return

            if (not ldapEntry.attributes.has_key('sshPublicKey'))
                return

            key = attributes.get('sshPublicKey')[0]

## Splat Man Pages

Nick Barkas

    snb@threerings.net

2006-05-14
splatd 8
splatd Scalable Periodic LDAP Attribute Transmogrifier
`splatd` `-h` `-d` `-f config` `-p pidfile`

#### Description

Splat is a daemon designed to help keep information in an LDAP directory in sync with information outside of an LDAP directory. This information can be any set of attributes on any object in the LDAP directory.

#### Options

`-h`

  Show summary of options.

`-d`

  Debug mode. Will not fork and run in the background.

`-f`

  Splat configuration file.

`-p`

  File to write daemon pid to.

#### See Also

splat.conf 5

.

Nick Barkas

    snb@threerings.net

Landon Fuller

    landonf@threerings.net

2006-05-14
splat.conf 5
splat.conf Scalable Periodic LDAP Attribute Transmogrifier configuration file

#### Introduction

The Splat configuration file is composed of three different section types:

- LDAP Configuration

- Service Helper Configuration

- Logging Configuration

The configuration uses an Apache-style syntax:

    <LDAP>
        # The LDAP Server configuration.
        # URI of the server(s)
        URI         ldaps://ldap1.example.com
        # The default search base for the server
        BaseDN      dc=example,dc=com
    </LDAP>

#### LDAP Configuration

The `LDAP` section defines connection parameters for your LDAP server, and may appear once within a Splat configuration file. If you make use of SSL/TLS, you will need to ensure that the appropriate settings are enabled in your system `ldap.conf` (e.g. `TLS_CACERT` or `TLS_CACERTDIR`, if necessary).

##### LDAP Configuration Options

URI

  URI of the LDAP server(s).

BaseDN

  Default Search Base DN.

BindDN

  LDAP Bind DN. If omitted, Slap will use an anonymous bind.

Password

  LDAP Bind Password.

#### Logging Configuration

The `Logging` section configures logging for the Splat daemon. It is composed of any number of `syslog` or `logfile` subsections and a global `Level` setting.

Level

  Log Verbosity. One of: debug, info, warning, error, critical

##### Syslog Configuration Options

Facility

  Syslog Facility.

Address

  Address of syslog server, or pathname to syslog socket. This varies between hosts. FreeBSD's syslog socket is located at `/var/run/log`, while Linux systems often use `/dev/log`.

##### Logfile Configuration Options

Path

  Path to log file, or reference to Python file object such as STDOUT.

#### Service Helper Configuration

The `Service` section configures Splat's service helper modules. It is composed service-specific settings, including any number of `Option`, and `Group` subsections. Group and Service names (such as UserSSH or Administrators) must be unique within their scope, but are not interpreted by Splat in any other way.

`Option` subsections may be specified in both the Service section and the Group subsection. Options are used to set and unset helper-specific settings. Module options are documented in the module-specific documentation. Example:

    <Option mingid>
      # Do not write keys for users with a GID less than mingid
      Value 1000
    </Option>

    # Unset the Command option
    <Option command/>

##### Service Configuration Options

Helper

  Python Helper Module. Example: splat.helpers.sshPublicKeys

Frequency

  Frequency at which helper is invoked. Units may be specified in hours (h), minutes (m), or seconds (s).

SearchBase

  LDAP search base. If left unspecified, defaults to the BaseDN specified in the LDAP configuration section.

SearchFilter

  LDAP search filter. All records that match this filter (and, optionally, are members of a permitted group) will be passed to the service helper module.

RequireGroup (yes/no)

  Require that returned entries match one of the specified Groups.

##### Group Configuration Options

SearchBase

  LDAP search base. If left unspecified, defaults to the BaseDN specified in the LDAP configuration section.

SearchFilter

  LDAP search filter. All records that match this filter will determine this groups membership. The filter should return groupOfUniqueNames or groupOfNames objects. I suggest using RFC2307bis to combine posixGroup and groupOfUniqueName.

MemberAttribute

  LDAP attribute used to store member DNs. Defaults to uniqueMember.

#### Examples

An example configuration file `splat.conf` is included with the Splat source.

#### See Also

splatd 8 ldap.conf 5

.