Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

Numbered Headings
start-numbering-ath2

Features

This section describes in detail the different functionalities that this module implements, indicating the configuration possibilities that are present. To see how these functionalities are enabled the configuration possibilities, refer to the Configuration section.

Generation of the Moonshot Targeted IDs RADIUS attribtes

Moonshot defines a set of RADIUS attributes that are intended to provide pseudonymity. This module is able to generate and distributed them as required.

Dynamic generation of SAML assertions

This is the most important feature of the module. Upon a user has been authenticated by FreeRADIUS, this module takes the attributes from the configured Attribute Sources and generate a valid SAML assertion to be delivered to the Relying Party.

The module provides two different implementations (so called Assertion builders) for doing this: leveraging the module to use the PySAML library to generate a standard and valid assertion, or using a Jinja2 template to create a custom SAML assertion using a set of variables provided by the module. Let's see them with a little more detail.

PySAML Assertion Builder

The ABFAB IDP implementation can make use of the PySAML library to generate valid and well-formed SAML assertions, using the information obtained from the attribute sources. This is the easiest and more straightforward way of generating a SAML assertion and probably the best fit for most of the users.

Jinja2 Assertion Builder

Jinja2 is a modern and designer-friendly templating language for Python, modelled after Django’s templates. It is fast, widely used and secure with the optional sandboxed template execution environment.

Users can provide a Jinja2 template that makes use of the following variables to generate the SAML assertions of their IDP:

  • username: name of the end user
  • rp_name: name of the RP as the 4-tuple (GSS-Acceptor-Service-Name, GSS-Acceptor-Host-Name, GSS-Acceptor-Realm-Name, Trust-Router-COI)
  • targeted_ids: user's targeted IDs as a 3-tuple (Moonshot-Host-TargetedId, Moonshot-Realm-TargetedId, Moonshot-TR-COI-TargetedId)
  • issuer: IDP SAML issuer name
  • attributes: list of attributes as 3-tuple (name, format, list of values)
  • reqid: SAML request ID (or None if not provided)
  • now: current date time in "%Y-%m-%dT%H:%M:%SZ" format
  • expiration: assertion expiration date time in "%Y-%m-%dT%H:%M:%SZ" format
  • random: random string of 256 digits for miscellaneous use

An example Jinja2 template would look like the following:

Code Block
languagexml
linenumberstrue
<saml:Assertion xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion"
                IssueInstant="{{now}}"
                ID="{{random[:16]}}"
                Version="2.0">
    <saml:Issuer>{{issuer}}</saml:Issuer>
    <saml:Subject>
        <saml:NameID>{{targeted_ids.get('Moonshot-TR-COI-TargetedId')}}</saml:NameID>
    </saml:Subject>
    <saml:Conditions NotBefore="{{now}}" NotOnOrAfter="{{expiration}}"/>
    <saml:AttributeStatement>
    {% for name, nameformat, values in attributes %}
        <saml:Attribute NameFormat="{{nameformat}}" Name="{{name}}">
            {% for value in values %}
                <saml:AttributeValue>{{value}}</saml:AttributeValue>
            {% endfor %}
        </saml:Attribute>
    {% endfor %}
    </saml:AttributeStatement>
</saml:Assertion>

Support for a number of different attribute sources

The assertion builders are fed with a number of attributes about the authenticated user. This module provides support for obtaining such attributes form a number of different sources (so called Attribute sources). In particular, it supports LDAP servers, SQLite databases and static definitions. Each IDP might have zero, one or more attribute source of each type. The values of the attributes from different sources that share the name are combined. Each attribute source can define an Attribute Relase Policy (so called Attribute Filter) which of the obtained attributes will be included in the provided SAML assertion.

LDAP Attribute Source

The ABFAB IDP module can obtain user attributes from LDAP servers. This support has been tested with openLDAP and Active Directory, although any other standard LDAP server should work as well. For an LDAP source it must be defined a mapping between the FreeRADIUS user name and the LDAP attribute that will be used as a key value to identify the user (e.g. mail address).

SQLite Attribute Source

It is also possible to create an attribute database where attributes are stored. This attribute source requires a table called attributes to exist, with the following schema:

Code Block
languagesql
linenumberstrue
CREATE TABLE attributes (
    `username`TEXT NOT NULL,
    `name`TEXT NOT NULL,
    `name_format`TEXT NOT NULL,
    `value` TEXT NOT NULL
    PRIMARY KEY(username,attribute_name));

Static Attribute Source

The ABFAB IDP module also allows an IDP to declare static attribute that will be included in every SAML assertion, regardless who the user is.

Support for attribute release policies

Sometimes an IDP do not want to include all the user attributes obtained from the Attribute Sources in the SAML assertion. The most typical reason for doing this is privacy protection, although other reasons might also be possible (e.g. optimization).

This module provides two different implementations (so called Attribute Filters) to define such policies.

JSON Attribute Filter

This filter allows the definition of complex filters based on a JSON file that contains a set of filtering rules. Each rule declares a RP name, a whitelist and a blacklist.

If the textual representation of the RP name matches the regular expression defined in the RP name, the rule is applied. Otherwise, the next rule is examined.

If a rule is applied, all the attributes matching any of the regular expressions listed in the blacklist will not be included in the SAML assertion, with the exception to those that match any of the regular expressions listed in the whitelist.

An example policy file be:

Code Block
languagejs
linenumberstrue
[
    {
        "rp_name": ".*@test.org",
        "blacklist": ["urn:.*"],
        "whitelist": [".*entitlement.*"]
    },
    {
        "rp_name": ".*@um.*",
        "blacklist": [".*"],
        "whitelist": ["displayName", "primaryGroupID", "co", "cn", "countryCode",
            "userPrincipalName", "logonCount", "distinguishedName", "streetAddress", "whenCreated", "uSNCreated", "postalCode", "mail", "objectGUID"]
    }
]

Let's imagine we have a user called testuser, which has the following attributes:

Code Block
[urn:fedearted:testattr, urn:oid:1.2.3:attr1, urn:4.5.6:entitlement, mail]

If the attributes are being provided to a RP named "testrp@test.org", it will get only

Code Block
[urn:4.5.6:entitlement, mail]

whereas if the RP is named "testrp@um.es", it would get

Code Block
[mail]

as everything else is being blacklisted.

Simple Attribute Filter

This filter provides a much simpler alternative, where the IDP only declares a list of allowed attribute names, with no requirement of having a separated JSON file. This unique rule is applied to any RP. As a matter of example, the results would be identical to having a JSON filter with the following rule:

Code Block
languagejs
linenumberstrue
[
    {
        "rp_name": ".*",
        "blacklist": [".*"],
        "whitelist": ["values of the whitelist here"]
    }
]

Support for several logging backends

The module emits a number of log messages with the purpose of providing information of what is going on on the IDP. There are three different logging backends (so called Loggers) that can be used to record these messages. They can be used simultaneously if desired.

FreeRADIUS Logger

This is the most basic Logger. It generates FreeRADIUS log messages that are included alog the rest of FreeRADIUS logging messages and recorded according the FreeRADIUS configuration (e.g. console, file, syslog...).

File Logger

This Logger writes the logging messages into a specified file in the filesystem.

Syslog logger

This Logger sends the log messages to the syslog daemon using the /dev/syslog

Installation

You can download the code of the ABFAB IdP module from our Bitbucket git repository by:

Code Block
languagebash
linenumberstrue
git clone https://bitbucket.org/umujisc/abfab_idp_module.git

This module installation requires you to have a python 2 development environment with setuptools and pip installed, in order to satisfy all the dependencies. Following you can find specific instructions for a set of Linux distributions. Other distributions might work following similar instructions.

The installation process installs the "abfab_idp" and "abfab_idp_legacy" files into the FreeRADIUS mods-available folder. The first one is intended to be used with FreeRADIUS > 3.0.10, whereas the second one is intended for older versions of FreeRADIUS.

It also installs an abfab_idp.conf and policies.json example files under /usr/share/abfabidp/examples.

Debian 7

Code Block
languagebash
linenumberstrue
apt-get install python-pip build-essential freeradius python-dev python-ldap python-jinja2 libffi-dev syslog-ng
python setup.py install 

Debian 8

Code Block
languagebash
linenumberstrue
apt-get install python-pip build-essential freeradius python-pysaml2 python-ldap python-jinja2 syslog-ng
python setup.py install

Centos 6 and 7

Code Block
languagebash
linenumberstrue
yum install epel-release
yum install python-pip freeradius python-devel python-ldap python-jinja2 syslog-ng libffi-devel openssl-devel gcc
python setup.py instal

Configuration

Enabling the module in the FreeRADIUS configuration structure

The package installs the "abfab_idp" and "abfab_idp_legacy" files into the FreeRADIUS mods-available folder. The first one is intended to be used with FreeRADIUS >= 3.0.10, whereas the second one is intended for older versions of FreeRADIUS. You must enable one of them by symlinking the chosen one from the mods-enabled folder.

Once the module has been enabled, you must add it into the post-auth section of the inner_tunnel site (under the sites-enabled folder). The IDP module needs to have access to the value of the GSS-Acceptor-* RADIUS attributes, hence you must make sure that they are being passed from the "default" site to the "inner_tunnel" one. The easiest way of doing it is by just setting the "copy_request_to_tunnel=yes" in the ttls and/or peap section of the "eap" module (in the mods-enabled/eap file). Similarly, the IDP module generates SAML-Message attributes that need to be included in the outcome of the "default" site. The easiest way of enabling this is by just setting the "use_tunneled_reply=yes" option in the ttls and/or peap sections.

Info

The use_tunneled_reply option should no longer be used after v3.0.7. Use the outer.session-state list/object instead.

 

Configuring the IDP behaviour

The behaviour of the IDP module is defined in the abfab_idp file (using the "unlang" file format). However, FreeRADIUS versions below 3.0.10 lack of Python's module configuration capabilities. For this reason, the abfab_idp_legacy module uses a JSON representation of the same configuration and located in the /etc/abfab_idp.conf file instead.

The following stanza depicts a complete configuration file, including comments with explanations for almost every possible option.

Code Block
languagetext
linenumberstrue
collapsetrue
config {
    # Value of the RADIUS realm for this IDP. This is used to check whether a
    # user belongs to this IDP or not.
    # MANDATORY.
    # realm = "testrealm.org"
    # Pseudo random string that is used as salt for the generation of targeted IDs.
    # OPTIONAL. Defaults to "changeme"
    # salt = "random string that you want to be your salt"
    # List of TargetedId that will be sent (comma separated)
    # OPTIONAL. Defaults to ""
    # send_targeted_ids = "Moonshot-Host-TargetedId, Moonshot-TR-COI-TargetedId"
    # Maximum length of the SAML-Protocol and SAML-Assertion RADIUS attributes.
    # OPTIONAL. Defaults to 220
    # max_rad_attr_len = 120
    # Loggers configuration
    loggers {
        # The subsection name is the name you want to give to the logger
        # mylog {
            # Type of the logger (file, freeradius, syslog).
            # MANDATORY
            # type = "radius"
            # Minimal log event level to be recorded.
            #   Ordering is: debug < info < warning < error < critical
            # OPTIONAL. Defaults to "debug" (log everything)
            # level = "debug"
            # Path to the log file ("file" logger)
            # MANDATORY
            # file_name = "/var/log/abfab_idp.log"
        # }
    }
    # Assertion Builder configuration
    assertion_builder {
        # Type of the assertion builder engine (pysaml, jinja2).
        # MANDATORY
        # type = "pysaml"
        # Value of the Issuer element to be used in the generated assertions.
        # MANDATORY
        # issuer = "http://testrealm.org/"
        # Lifetime in minutes of the generated Assertions.
        # OPTIONAL. Defaults to 60
        # lifetime = 600
        # Indicates if the targeted_id value should be used as Subject's NameID value ("pysaml" builder)
        # OPTIONAL. Defaults to ""
        # pysaml_targeted_id_as_nameid = "Moonshot-Realm-TargetedId"
        # Location of the Jinja2 template used by the jinja2 builder ("jinja2" builder)
        # MANDATORY
        # jinja2_template = "assertion_template.jinja2"
    }
    # Configuration specific to the gathering of user attributes
    attribute_sources {
        # The section name is the name you want to give to this source
        # myattrsource {
            # Type of the attribute source (sqlite, ldap, static)
            # MANDATORY
            # type = "ldap"
            # Attribute filter to be applied to the obtained attributes of this source.
            # OPTIONAL. If omitted, no filter is applied
            # filter {
                # The type of attribute filter (simple, json)
                # MANDATORY
                # type = "json"
                # Comma separated list of allowed attribute names ("simple" filter)
                # MANDATORY
                # simple_whitelist = "mobile,age"
                # Location of the attribute filter policies file ("json" filter")
                # MANDATORY
                # json_policy_file = "policies.json"
            # }
            # Location of the SQLite attribute database ("sqlite" source)
            # MANDATORY
            # sqlite_db = "attributes.db"
            # LDAP server URI to connect to ("ldap" source)
            # MANDATORY.
            # ldap_uri = "ldap://ldap.exaple.org:389"
            # LDAP Login DN ("ldap" source)
            # OPTIONAL. Only needed with authentication is required
            # ldap_login = "cn=admin,dc=whatever,dc=whatever"
            # LDAP Password ("ldap" source)
            # OPTIONAL. Only needed when authentication is required
            # ldap_password = "whatever"
            # LDAP Base DN for the queries ("ldap" source)
            # MANDATORY
            # ldap_basedn = "dc=users,dc=whatever,dc=whatever"
            # Mapping to determine how RADIUS username (%u) is used to retrieve the LDAP information ("ldap" source)
            # MANDATORY
            # ldap_mapping = "(mail=%u)"
            # Maximum time to wait for a response in seconds ("ldap" source)
            # OPTIONAL. Defaults to 2
            # ldap_timeout = 1
            # Attribute name ("static" source)
            # MANDATORY
            # static_name = "1.3.6.1.4.1.5923.1.1.1.3"
            # Attribute name format ("static" source)
            # OPTIONAL. Defaults to "urn:oasis:names:tc:SAML:2.0:attrname-format:uri"
            # static_name_format = "mycustomformat"
            # Attribute value ("static" source)
            # MANDATORY
            # static_value = "o=Hogwarts, dc=hsww, dc=wiz"
        # }
    }

Configuration examples

This subsection provides different real configuration examples using both configuration languages (unlang and JSON).

Example 1

This IDP is using the freeradius log system for delivering the log events, generating the assertions by means of the internal SAML library, and obtaining the attributes from a LDAP directory and a static source.

Unlang format

Code Block
languagetext
linenumberstrue
collapsetrue
config {
    realm = "testrealm.org"
    salt = "this is a random 788 string"
    send_targeted_ids = "Moonshot-Host-TargetedId"
    loggers {
        radiuslog {
            type = "freeradius"
            level = "debug"
        }
    }
    assertion_builder {
        type = "pysaml"
        issuer = "http://testrealm.org/"
    }
    attribute_sources {
        ldapsrc {
            type = "ldap"
            ldap_uri = "ldap://ldap.server.org"
            ldap_login = "cn=admin,dc=server,dc=org"
            ldap_password = "secret"
            ldap_basedn = "dc=server,dc=org"
            ldap_mapping = "(uid=%u)"
            filter {
                type = "simple"
                simple_whitelist = "mobile,age"
            }
        }
        staticsrc {
            type = "static"
            static_name = "Organization"
            static_value = "My organization"
        }
    }
}

JSON format

Code Block
languagejs
linenumberstrue
collapsetrue
{
    "realm": "testrealm.org",
    "salt": "this is a random 788 string",
    "send_targeted_ids": "Moonshot-Host-TargetedId",
    "loggers": {
        "radiuslog": {
            "type": "freeradius",
            "level": "debug"
        }
    },
    "assertion_builder": {
        "type": "pysaml",
        "issuer": "http://testrealm.org/"
    },
    "attribute_sources": {
        "ldapsrc": {
            "type": "ldap",
            "ldap_uri": "ldap://ldap.server.org",
            "ldap_login": "cn=admin,dc=server,dc=org",
            "ldap_password": "secret",
            "ldap_basedn": "dc=server,dc=org",
            "ldap_mapping": "(uid=%u)",
            "filter": {
                "type": "simple",
                "simple_whitelist": "mobile,age"
            }
        },
        "staticsrc": {
            "type": "static",
            "static_name": "Organization",
            "static_value": "My organization"
        }
    }
}

Example 2

This IDP is using the two log destinations. On the one hand all the events are being sent to the freeradius log system, whereas the file /var/log/radiusidp/idp.log will record all the events above the "info" level. The SAML assertions are being generated using a Jinja2 template (see section above). Finally, it is gathering attributes from two different LDAP servers. For the first one, a JSON attribute release policy is being used (see above), while for the second all the attributes are being provided to the RP.

Unlang format:

Code Block
languagetext
collapsetrue
config {
    realm = "testrealm.org"
    salt = "this is a random 788 string"
    send_targeted_ids = "Moonshot-Host-TargetedId"
    loggers {
        radiuslog {
            type = "freeradius"
            level = "debug"
        }
        file {
            type = "file"
            file_name = "/var/log/radiusidp/idp.log"
            level = "info"
        }
    }
    assertion_builder {
        type = "jinja2"
        issuer = "http://testrealm.org/"
        jinja2_template = "/etc/abfab_idp_template.jinja2"
    }
    attribute_sources {
        ldapsrc {
            type = "ldap"
            ldap_uri = "ldap://ldap.server.org"
            ldap_login = "cn=admin,dc=server,dc=org"
            ldap_password = "secret"
            ldap_basedn = "dc=server,dc=org"
            ldap_mapping = "(uid=%u)"
            filter {
                type = "json"
                json_policy_file = "/etc/abfab_idp_attr_policies.json"
            }
        }
        localldap {
            type = "ldap"
            ldap_uri = "ldap://localhost"
            ldap_login = "cn=admin,dc=local,dc=org"
            ldap_mapping = "(mail=%u)"
        }
    }
}

JSON format

Code Block
languagejs
linenumberstrue
collapsetrue
{
    "realm": "testrealm.org",
    "salt": "this is a random 788 string",
    "send_targeted_ids": "Moonshot-Host-TargetedId",
    "loggers": {
        "radiuslog": {
            "type": "freeradius",
            "level": "debug"
        },
        "file": {
            "type": "file",
            "file_name": "/var/log/radiusidp/idp.log",
            "level": "info"
        }
    },
    "assertion_builder": {
        "type": "jinja2",
        "issuer": "http://testrealm.org/",
        "jinja2_template": "/etc/abfab_idp_template.jinja2"
    },
    "attribute_sources": {
        "ldapsrc": {
            "type": "ldap",
            "ldap_uri": "ldap://ldap.server.org",
            "ldap_login": "cn=admin,dc=server,dc=org",
            "ldap_password": "secret",
            "ldap_basedn": "dc=server,dc=org",
            "ldap_mapping": "(uid=%u)",
            "filter": {
                "type": "json",
                "json_policy_file": "/etc/abfab_idp_attr_policies.json"
            }
        },
        "localldap": {
            "type": "ldap",
            "ldap_uri": "ldap://localhost",
            "ldap_login": "cn=admin,dc=local,dc=org",
            "ldap_mapping": "(mail=%u)"
        }
    }
}