Remote-to-Local User Mapping using a Database
Various options exist about how to map an incoming federated identifier to a local account on a specific service. This page discusses how a deployer can map from a pseudonymous identifier or SAML assertion issued by a Moonshot IdP to a local account using a database mapping table.
Contents
1. Introduction
Deployers of Moonshot-enabled services may wish external users to authenticate to their service but control the user account that each particular user has access to. For example, mapping specific external users to specific local Linux accounts when logging in through SSH, or specific external users to specific local mailboxes when logging in to Exchange, etc.
Additionally, it often is the case that a user may have multiple identities he/she would like to associate with their local account. For example, a scientist may wish to use their institutional identity (as issued by their home institution), as well as perhaps a Google or Facebook account, where allowed.
It is also the case that not all associations are trusted equally. In some cases, the institutional identity may have a higher level of assurance than a social platform like Google or Facebook, and the organisation may wish to restrict logging into more sensitive systems to identities with higher levels of assurance. For example, the scientist is allowed to use their Google or Facebook account to sign into reference libraries, user accounts, project and grant submission systems, but may only use their institutional identity to log into data libraries containing sensitive research data.
2. Where does the mapping happen?
In this scenario, the mapping from external user to internal happens on the Moonshot RP Proxy. A single Moonshot RP Proxy can handle mappings for multiple Moonshot-enabled services, as the database will record which service the mapping is associated with. However, this means that the administrator of the Moonshot RP Proxy (or rather, the administrator of the database) will need to either manage all of the mappings for all of the services that make use of their Moonshot RP Proxy, or give all administrators of those systems access to the database. An alternative would be for each system that wishes to make use of mappings run their own Moonshot RP proxy.
3. Configure the RP Proxy
The first thing needed is to configure your Moonshot RP Proxy (or Moonshot IdP if you are using that as both an IdP and an RP Proxy) to make use of the database of mappings that we will set up in the next step.
3.1. Enable the FreeRADIUS SQL module
To access the database from your Moonshot RP proxy (FreeRADIUS), you must configure FreeRADIUS to enable database support. FreeRADIUS by default supports SQLite, MySQL/MariaDB, PostgreSQL, and any database that supports Unix ODBC connections. However, depending on your database dialect, you may have to install the FreeRADIUS module for it to enable this support.
Example - adding support for MySQL to FreeRADIUS
To add support for MySQL in FreeRADIUS on RHEL, issue the following command:
$ yum install freeradius-mysql
After installation, configure the SQL module:
- Edit
/etc/raddb/mods-available/sql
In the
sql
section, adjust thedriver
value to your database dialect as per the comments above it:driver = "rlm_sql_mysql"
- Depending on your database dialect, you may wish to adjust some of the options that are specific for your database dialect.
Adjust the
dialect
line to match your database dialect as per the comments above it:dialect = "mysql"
Remove the comments from the
server
,port
,login
andpassword
lines and adjust them to suit:server = "database.mydomain.com" port = 3306 login = "radius" password = "radpass"
You must ensure that the login user and password are created in the database before you start the FreeRADIUS server. See your database documentation on how to create users in your database, or use the supplied FreeRADIUS setup scripts for your database dialect to create a default database.
- Adjust the
radius_db
line to suit your database dialect. Take note of the additional options documented that may be applicable to your database dialect. - If you do not use RADIUS accounting, check or reply items, and/or don't store your users in a database, comment out any tables that are not applicable to you.
Save the file and create a soft link to enable the SQL module:
$ cd /etc/raddb/mods-enabled $ ln -s ../mods-available/sql
If your Moonshot RP Proxy is also your Moonshot IdP, and you make use of LDAP for authentication, you will have to comment out the call to sql in FreeRADIUS' default and inner-tunnel sites-enabled configurations.
4. Shared vs Different local identifiers
There are typically two deployment models for user mapping to multiple moonshot-enabled services managed by a single Moonshot RP Proxy:
- Where all backends are separate and a single user might have different local user identifiers per system; or
- Where all backends are related and a single user would have the same local user identifier across all of the backend systems.
Instructions differ depending on your particular deployment model, so follow the correct set of following instructions for you:
4.1. Option 1 - Different local user identifier per system
4.1.1. Configure the Database
The first thing that is needed is a database to store these mappings. In your favourite database server software, create a table with four columns:
gss_acceptor | targeted_id | local_uid | auth_type |
---|---|---|---|
varchar(256) | varchar(256) | varchar(32) | smallint |
Example - Creating the mapping table in MySQL
In MySQL, you can achieve this with the following command:
mysql> CREATE TABLE account_id_map ( gss_acceptor varchar(256) NOT NULL, targeted_id varchar(256) NOT NULL, local_uid varchar(32) NOT NULL default 'moonshot', auth_type smallint NOT NULL default 0, PRIMARY KEY (gss_acceptor,targeted_id) );
The first column will contain the gss_acceptor name for the service, made up of the GSS-Acceptor-Service-Name and GSS-Acceptor-Host-Name of the service (e.g., host/ssh-server.example.com).
The second column will store the incoming targeted identifier. The incoming value will generally be qualified, i.e. it will be a value with the incoming realm appended to it (i.e., value@REALM). The width is limited to the maximum length of the SAML eduPersonTargetedID
attribute. Alternatively, you may wish to use the maximum length of any RADIUS attribute (253 octets).
The third column is the local account name. The width is limited to the maximum length of useradd(8) on Unix. On Windows, underlying usernames are limited to 20 characters for compatibility purposes (see Microsoft TechNet: Active Directory Maximum Limits - Scalability for more information on this).
The fourth column is for information (and possibly integration) purposes and contains an identifier that identifies the service used. Suggested values are zero, indicating a local-to-local mapping, one indicating a Moonshot mapping, and any other value thereafter for clearly defined values as per your organisation's choice. This field could also indicate the level of assurance.
The primary key is made up of the tuple of gss_acceptor and targeted_id, meaning that for a particular service and a particular external user, only one mapping can exist.
4.1.2. Using the Moonshot-Host-TargetedId attribute in a database lookup
A typical Moonshot deployment uses three pseudonymous targeted identifiers as issued by a Moonshot IdP - the Moonshot-Host-TargetedId
, Moonshot-Realm-TargetedId,
and Moonshot-TR-COI-TargetedId.
These are carried as ordinary RADIUS attributes - see Issue Pseudonymous Identifiers for further information on these identifiers and how an IdP is configured to issue them.
To use these identifiers as the incoming attribute to be used as the remote identifier for the user, do the following:
4.1.2.1. Create a mapping policy configuration
In FreeRADIUS'
/etc/raddb/policy.d
directory, create a file named "moonshot
".In this file place the following content:
moonshot_saml.post-auth { # if the request contains the GSS-Acceptor-* attributes and is not a trust router request if ( (&request:GSS-Acceptor-Service-Name) && (&request:GSS-Acceptor-Host-Name) && ("%{tolower:%{request:GSS-Acceptor-Service-Name}}" != 'trustidentity') ) { if (&reply:Moonshot-Host-TargetedId) { update reply { # do a query against the database to see if an account mapping exists for this Moonshot-Host-TargetedId, otherwise return the Targeted-Id Tmp-String-1 := "%{%{sql:SELECT account_id_map.local_uid FROM `account_id_map` WHERE account_id_map.gss_acceptor = '%{GSS-Acceptor-Service-Name}/%{GSS-Acceptor-Host-Name}' AND account_id_map.targeted_id = '%{reply:Moonshot-Host-TargetedId}'}:-%{reply:Moonshot-Host-TargetedId}}" } } # if TargetedId # erase incoming SAML Assertion update reply { SAML-AAA-Assertion !* ANY } # create a new SAML assertion, use the contents of control:Tmp-String-1 as the name (if it exists), otherwise use 'moonshot' update reply { SAML-AAA-Assertion = '<saml:Assertion xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" IssueInstant="2011-03-19T08:30:00Z" ID="foo" Version="2.0">' SAML-AAA-Assertion += '<saml:Issuer>urn:mace:incommon:osu.edu</saml:Issuer>' SAML-AAA-Assertion += '<saml:AttributeStatement>' SAML-AAA-Assertion += '<saml:Attribute NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:uri" Name="urn:oid:1.3.6.1.4.1.5923.1.1.1.7">' SAML-AAA-Assertion += "<saml:AttributeValue>%{%{reply:Tmp-String-1}:-moonshot}</saml:AttributeValue>" SAML-AAA-Assertion += '</saml:Attribute></saml:AttributeStatement>' SAML-AAA-Assertion += '</saml:Assertion>' User-Name := "%{%{reply:Tmp-String-1}:-moonshot}" } } # if gss-acceptor }
The logic of the code above is simple but very constrained:
- You receive a reply from an ID Provider (including your own) in the Moonshot system.
- If it is a trust router request, don't do anything.
- If that reply contains the
Moonshot-Host-TargetedId
, execute a SQL query in your database for theMoonshot-Host-TargetedId
.- If there is a local account mapping for the
Moonshot-Host-TargetedId
, set theTmp-String-1
attribute to the local account for it. - If there is no local account mapping for
Moonshot-Host-TargetedId
, set theTmp-String-1
attribute to theMoonshot-Host-TargetedId
- If there is a local account mapping for the
- If the reply does not contain the
Moonshot-Host-TargetedId
, do nothing andTmp-String-1
will not be set to anything. - Next, delete any SAML assertion contained in the
SAML-AAA-Assertion
attributes in the reply. - Finally, create a new SAML Assertion, but this time use the value of
Tmp-String-1
, if it exists. If it does not, use 'moonshot'.
4.2. Option 2 - Same local user identifier across all systems
4.2.1. Configure the Database
The first thing that is needed is a database to store these mappings. In your favourite database server software, create a table with three columns:
targeted_id | local_uid | auth_type |
---|---|---|
varchar(256) | varchar(32) | smallint |
Example - Creating the mapping table in MySQL
In MySQL, you can achieve this with the following command:
mysql> CREATE TABLE account_id_map ( targeted_id varchar(256) NOT NULL, local_uid varchar(32) NOT NULL default 'moonshot', auth_type smallint NOT NULL default 0, PRIMARY KEY (gss_acceptor,targeted_id) );
The first column will store the incoming targeted identifier. The incoming value will generally be qualified, i.e., it will be a value with the incoming realm appended to it (for example, value@REALM). The width is limited to the maximum length of the SAML eduPersonTargetedID
attribute. Alternatively, you may wish to use the maximum length of any RADIUS attribute (253 octets).
The second column is the local account name. The width is limited to the maximum length of useradd(8) on Unix. On Windows, underlying usernames are limited to 20 characters for compatibility purposes (see Microsoft TechNet: Active Directory Maximum Limits - Scalability for more information on this).
The third column is for information (and possibly integration) purposes and contains an identifier that identifies the service used. Suggested values are zero, indicating a local-to-local mapping, one indicating a Moonshot mapping, and any other value thereafter for clearly defined values as per your organisation's choice. This field could also indicate the level of assurance.
The primary key is the targeted_id, meaning that for a particular external user, only one mapping can exist.
4.2.2. Using the Moonshot-Host-TargetedId attribute in a database lookup
A typical Moonshot deployment uses three pseudonymous targeted identifiers as issued by a Moonshot IdP - the Moonshot-Host-TargetedId
, Moonshot-Realm-TargetedId,
and Moonshot-TR-COI-TargetedId.
These are carried as ordinary RADIUS attributes - see Issue Pseudonymous Identifiers for further information on these identifiers and how an IdP is configured to issue them.
To use these identifiers as the incoming attribute to be used as the remote identifier for the user, do the following:
4.2.2.1. Create a mapping policy configuration
In FreeRADIUS'
/etc/raddb/policy.d
directory, create a file named "moonshot
".In this file place the following content:
moonshot_saml.post-auth { # if the request contains the GSS-Acceptor-* attributes and is not a trust router request if ( (&request:GSS-Acceptor-Service-Name) && (&request:GSS-Acceptor-Host-Name) && ("%{tolower:%{request:GSS-Acceptor-Service-Name}}" != 'trustidentity') ) { if (&reply:Moonshot-Host-TargetedId) { update reply { # do a query against the database to see if an account mapping exists for this Moonshot-Host-TargetedId, otherwise return the Targeted-Id Tmp-String-1 := "%{%{sql:SELECT account_id_map.local_uid FROM `account_id_map` WHERE account_id_map.targeted_id = '%{reply:Moonshot-Host-TargetedId}'}:-%{reply:Moonshot-Host-TargetedId}}" } } # if TargetedId # erase incoming SAML Assertion update reply { SAML-AAA-Assertion !* ANY } # create a new SAML assertion, use the contents of control:Tmp-String-1 as the name (if it exists), otherwise use 'moonshot' update reply { SAML-AAA-Assertion = '<saml:Assertion xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" IssueInstant="2011-03-19T08:30:00Z" ID="foo" Version="2.0">' SAML-AAA-Assertion += '<saml:Issuer>urn:mace:incommon:osu.edu</saml:Issuer>' SAML-AAA-Assertion += '<saml:AttributeStatement>' SAML-AAA-Assertion += '<saml:Attribute NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:uri" Name="urn:oid:1.3.6.1.4.1.5923.1.1.1.7">' SAML-AAA-Assertion += "<saml:AttributeValue>%{%{reply:Tmp-String-1}:-moonshot}</saml:AttributeValue>" SAML-AAA-Assertion += '</saml:Attribute></saml:AttributeStatement>' SAML-AAA-Assertion += '</saml:Assertion>' User-Name := "%{%{reply:Tmp-String-1}:-moonshot}" } } # if gss-acceptor }
The logic of the code above is simple but very constrained:
- You receive a reply from an ID Provider (including your own) in the Moonshot system.
- If it is a trust router request, don't do anything.
- If that reply contains the
Moonshot-Host-TargetedId
, execute a SQL query in your database for theMoonshot-Host-TargetedId
.- If there is a local account mapping for the
Moonshot-Host-TargetedId
, set theTmp-String-1
attribute to the local account for it. - If there is no local account mapping for
Moonshot-Host-TargetedId
, set theTmp-String-1
attribute to theMoonshot-Host-TargetedId
- If there is a local account mapping for the
- If the reply does not contain the
Moonshot-Host-TargetedId
, do nothing andTmp-String-1
will not be set to anything. - Next, delete any SAML assertion contained in the
SAML-AAA-Assertion
attributes in the reply. - Finally, create a new SAML Assertion, but this time use the value of
Tmp-String-1
, if it exists. If it does not, use 'moonshot'.
5. Enabling the mapping policy
To use the policy, it has to be enabled by using it.
In
/etc/freeradius/sites-enabled/abfab-tr-idp
, find thepost-auth
section. At the top, insert onto its own line the following:moonshot_saml
You can adjust where you want to call the policy that inserts the Moonshot policy, as long as it is called in the
post-auth
section ofabfab-tr-idp
.If you use non-TLS connections for Moonshot, you may wish to repeat Step 2 in/etc/freeradius/sites-enabled/default
.Restart FreeRADIUS.
6. Testing the mapping
Create a manual mapping entry in your database with a test user from Janet (please contact us for it).
- Run FreeRADIUS in debug mode.
- Execute an authentication request with the Janet test user. The debug output will include the Moonshot-Host-TargetedId that can be copied.
Insert the TargetedId together with the chosen username on your local systems with SQL:
Option 1 - Different user identifiers per systemmysql> INSERT INTO account_id_map (gss_acceptor, targeted_id, local_uid, auth_type) VALUES ('[value of your GSS-Acceptor-Service-Name]/[value of your GSS-Acceptor-Host-Name],'[value of Janet Moonshot-Host-TargetedId]', '[value of local username to be used]', 1);
Option 2 - Same user identifier across all systemsmysql> INSERT INTO account_id_map (targeted_id, local_uid, auth_type) VALUES ([value of Janet Moonshot-Host-TargetedId]', '[value of local username to be used]', 1);
- Execute another authentication request with the Janet user. You should now see the local username in the SAML-AAA-Assertion.
6.1. Populating the mapping database
To populate the mapping database in a production environment, you will need to create a system that will require the user to log in twice, once with their local user (which should give you access to the local username), and once with Moonshot to gain access to one (or more) of the Moonshot TargetedIds. Then you will be able to create an entry in the database that contains the mapping.