SAML 2.0 authentication

Canopy can act as a SAML 2.0 Service Provider (SP) and authenticate against any SAML 2.0 compatible Identity provider (IdP) as a SSO solution with just-in-time user creation. Additionally it can populate user fields and role assignments directly from SAML attributes.

SAML authentication related settings are configured in /etc/canopy/canopy.ini, /etc/canopy/saml/config.py and a few other related files as specified in /etc/canopy/saml/config.py. Any change to them requires Canopy to be restarted.

Important Information

  • Login can be SP initiated or IdP initiated.

  • Canopy’s SAML URLs:

  • The IdP’s metadata should be stored under /etc/canopy/saml/metadata.xml

  • NameID identifier must be sent by IdP. This should be the user’s email address and be typed as a Persistent NameID.

  • Canopy requires that the email field is mapped to a SAML attribute that contains the users email address and the name field is mapped to the user’s name/surname attribute.

  • SAML request/response signing/transport certificate/key (X509) must be configured in /etc/canopy/saml/config.py.

  • If a SAML attribute map is required then /etc/canopy/saml/attributemaps/basic.py should be updated accordingly. Specifically the attribute name format should match what the IdP is emitting.

  • Example config files are located under /opt/checksec/canopy/sample_configs/saml/

Configuration

Example config files may be used as a starting point via the following steps:

mkdir /etc/canopy/saml
cp /opt/checksec/canopy/sample_configs/saml/config.py /etc/canopy/saml/
cp -a /opt/checksec/canopy/sample_configs/saml/attributemaps /etc/canopy/saml/

The following config options (with defaults) are valid inside /etc/canopy/canopy.ini:

SAML_ENABLE=true
SAML_ATTRIBUTE_MAPPING=email=email  # This is the minimum required. Format is: user_field1=saml_attribute1,user_field2=saml_attribute2
SAML_SSO_NAME=Single Sign On  # Name of SSO service to display on login page
LOG_LEVEL_SAML=DEBUG  # *** Remove this line after SAML configuration is complete

Additionally a SAML specific config file is also required at /etc/canopy/saml/config.py

Many of the fields will need updating, specifically URLs and file locations.

Attribute Mapping

If allow_unknown_attributes is not set to True in config.py under the SAML_CONFIG dictionary then all attributes used must be configured under required_attributes or optional_attributes and in the attribute maps (/etc/canopy/saml/samlattributemaps/basic.py).

At the very least the email and name fields MUST be mapped to SAML attributes.

Role assignment

There are two ways user roles can be assigned via SAML attributes:
  • Multi valued attribute that contains the complete role list (New in 3.2.1)

  • Attribute per role

Both approaches form part of the SAML_ATTRIBUTE_MAPPING setting and can be used in combination (must be in the above order).

Multi valued attribute (Canopy 3.2.1+)

User roles can be sent via a multi valued SAML2 attribute, the user’s roles will be reset to this list. If the complete list of user roles cannot be sent via a single multi valued attribute then per role attributes will be required, possibly in addition to this.

The multi valued roles attribute needs to be mapped to the set_roles field.

SAML_ATTRIBUTE_MAPPING=email=email,name=name,set_roles=saml_roles_attribute
Role name translation (Canopy 3.2.1+)

Since the role names may not always match what Canopy expects, one can translate role names being sent via a multi valued SAML attribute using the SSO_USER_ROLE_MAPPING setting in /etc/canopy/canopy.ini

The mapping is a comma delimited list of SAML Role Name = Canopy Role Name

SSO_USER_ROLE_MAPPING=SAMLRoleName=canopy_role_name1, SAMLRoleName2=canopy_role_name2

This setting will apply role translation to all SSO methods.

Supported Roles
All builtin and custom roles are supported, the builtin list of roles is:
  • admin

  • technical-managers

  • senior-analysts

  • analysts

  • schedulers

  • sales-managers

  • account-managers

  • kb-admins

  • pr-reviewers

  • qa-reviewers

For custom roles their name in lower case will be used.

Attribute per role

Assigning roles based on SAML attributes is optional and not all flags need to be mapped. Unmapped flags can be managed via the Canopy admin interface. The attribute value should be any string value to signify membership, except for the following strings, which denote the removal of the role: ‘’ (Empty string), ‘no’, ‘false’ and ‘0’.

If the attribute is missing then no adjustment will occur.

Role fields currently supported:
  • is_admin

  • is_technical_managers

  • is_senior_analysts

  • is_analysts

  • is_schedulers

  • is_sales_managers

  • is_account_managers

  • is_custom_pr_reviewers

  • is_custom_qa_reviewers

SAML_ATTRIBUTE_MAPPING=email=email, name=name, is_admin=saml_admin_attribute

if per attribute role mappings are in use with multi valued attribute role mapping then the role flags should be after the set_roles mapping.

SAML_ATTRIBUTE_MAPPING=email=email, name=name, set_roles=saml_roles_attribute, is_admin=saml_admin_attribute

Note

Per role attributes do not support custom roles.

Microsoft ADFS

ADFS requires some additional configuration:

  • A NameID claim with the correct name format is required. Most IdPs send these but ADFS doesn’t by default. See https://blogs.msdn.microsoft.com/card/2010/02/17/name-identifiers-in-saml-assertions/ If this is not sent then Canopy will error with AttributeError: ‘NoneType’ object has no attribute ‘name_qualifier’ in its logs.

  • When set to send a group membership claims, ADFS will not submit the claim when the user is NOT in the given group. Canopy will be unable to remove a role from a user. To correct this one requires an additional custom claim rule to submit the lack of membership to a group:

    NOT EXISTS([Type == "GROUPCLAIMTYPE"]) => issue(Type = "GROUPCLAIMTYPE", Value = "");
    

    Note

    This is however not required when using the multi valued attribute approach as it resets the roles list.

Multi valued attribute for Canopy role claims

An efficient approach to provisioning user roles from ADFS groups is to use a consistent naming convention for the ADFS groups that contain the Canopy role names, combined with claim issuance transformation rules to emit the Canopy roles.

For example, the if the naming convention for Canopy roles as ADFS groups were PREFIX-Canopy-ENVIRONMENT-CANOPYROLE

then Canopy roles can be mapped using ADFS group names such as:
  • Application-Canopy-PROD-admin

  • Application-Canopy-PROD-analysts

  • Application-Canopy-PROD-senior_analysts

  • Application-Canopy-PROD-customrole1

The custom claim issuance transformation rules required to convert these group names into Canopy roles are as follows:

c:[Type == "http://schemas.microsoft.com/ws/2008/06/identity/claims/windowsaccountname", Issuer == "AD AUTHORITY"]
=> add(store = "Active Directory", types = ("canopyroles"), query = ";tokenGroups;{0}", param = c.Value);

This adds a claim named canopyroles with ALL of the user’s ADFS groups

c:[Type == "canopyroles", Value =~ "^Application-Canopy"]
=> issue(Type = "canopyroles", Value = RegExReplace(c.Value, "^.*-PROD-", ""));

then the 2nd rule filters the groups to the relevant ones, strips everything that isn’t part of the Canopy Role and issues the claim.

For the above example the appropriate setting in /etc/canopy/canopy.ini would be similar to (note the set_roles=canopyroles part):

SAML_ATTRIBUTE_MAPPING=name=uid,email=email,set_roles=canopyroles