Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SAML2 group mapping #11273

Open
wants to merge 2 commits into
base: dev
Choose a base branch
from
Open

Conversation

fopina
Copy link

@fopina fopina commented Nov 16, 2024

Description

While SAML2 authentication works well as it is, it's not very useful for authorization.

This PR introduces the ability to map SAML2 IdP groups (or some other list attribute) to Dojo groups.
And its configuration is similar to Azure AD group matching:

  • Take all groups from IdP that match a configurable regexp
  • Take all groups from the user on Dojo that match the same regexp
  • Take all existing groups in Dojo that match the same regexp

Testing

Auth0 was used to test the setup as it support SAML2

  • Create new application, Regular Web Application
  • Under application addons, enable SAML2 web-app
  • Use settings
{
 "mappings": {
   "user_id":     "urn:oid:0.9.2342.19200300.100.1.1",
   "email":       "urn:oid:1.2.840.113549.1.9.1.1",
   "given_name":  "urn:oid:2.5.4.3",
   "family_name": "urn:oid:2.5.4.4",
   "groups":      "http://schemas.xmlsoap.org/claims/Group"
 },
 "passthroughClaimsWithNoMapping": false
}
  • Add http://localhost:8080/saml/acs/ as callback url and click Enable

As there are no groups in Auth0 and roles are not shared via SAML assertions, we can push them (roles) into a custom assertion (based on this and this links

  • Go to Actions choose Trigger
  • Choose postlogin trigger
/**
* Handler that will be called during the execution of a PostLogin flow.
*
* @param {Event} event - Details about the user and the context in which they are logging in.
* @param {PostLoginAPI} api - Interface whose methods can be used to change the behavior of the login.
*/
exports.onExecutePostLogin = async (event, api) => {
  var roles = event.authorization.roles;
	api.samlResponse.setAttribute('urn:oid:2.5.4.33', roles);
};
  • Back to Dojo, set the following extra configuration (via env):
DD_SAML2_ENABLED: 'true'
DD_SAML2_METADATA_AUTO_CONF_URL: <METADATA URL FROM AUTH0 SAML2 ADDON>
DD_SITE_URL: http://localhost:8080
DD_SOCIAL_LOGIN_AUTO_REDIRECT: "true"
DD_SOCIAL_AUTH_SHOW_LOGIN_FORM: "false"
DD_SAML2_CREATE_USER: "true"
DD_SAML2_ATTRIBUTES_MAP: email=email,uid=username,cn=first_name,sn=last_name
DD_SAML2_GROUPS_FILTER='.*'
DD_SAML2_GROUPS_ATTRIBUTE=roleOccupant
  • Now create some roles and add them to the test user in Auth0.
  • Create Dojo groups with the exame same name
  • Login using that test user and the user should be part of the mapped Dojo groups
    • You can verify in logs that those groups were deteted

@github-actions github-actions bot added docker settings_changes Needs changes to settings.py based on changes in settings.dist.py included in this PR apiv2 docs unittests integration_tests ui parser helm labels Nov 16, 2024
Copy link

dryrunsecurity bot commented Nov 16, 2024

DryRun Security Summary

The GitHub Pull Request summarizes several key changes to the DefectDojo application, including SAML2 configuration, deduplication and hashcode generation, additional authentication methods, security-related settings, and Celery, logging, and file upload settings, all aimed at enhancing the security and usability of the application.

Expand for full summary

Summary:

The code changes in this GitHub Pull Request cover several areas of the DefectDojo application, with a focus on enhancing the security and usability of the application.

The key changes include:

  1. SAML2 Configuration: The changes introduce the ability to use a custom SAML authentication backend, enable SAML2 force authentication, and map SAML2 groups to DefectDojo groups. This improves the integration with enterprise-grade authentication and authorization systems.

  2. Deduplication and Hashcode Generation: The changes allow for configuring the deduplication algorithm and the fields used to compute the hashcode per parser. This helps maintain the integrity of the vulnerability data stored in DefectDojo.

  3. Additional Authentication Methods: The changes add support for REMOTE_USER authentication and API token authentication, providing more flexibility in the application's authentication mechanisms.

  4. Security-related Settings: The changes introduce various security-related settings, such as CSRF and session cookie configurations, secure headers, and rate limiting for the login page. These enhancements improve the overall security posture of the application.

  5. Celery, Logging, and File Upload Settings: The changes introduce settings related to Celery, logging, and file uploads, which are important for the reliable and secure operation of the DefectDojo application.

Overall, these changes demonstrate a thoughtful approach to application security, with a focus on improving the flexibility, integrity, and security of the DefectDojo application. As an application security engineer, I would recommend thoroughly reviewing the changes to ensure that they do not introduce any unintended security vulnerabilities, and that the new security-related settings are properly configured and maintained.

Files Changed:

  1. dojo/settings/.settings.dist.py.sha256sum: This file contains the SHA-256 hash value for the .settings.dist.py file, which is used to verify the integrity of the associated settings file. The change updates the hash value to reflect the changes made to the .settings.dist.py file.

  2. dojo/backends.py: The changes in this file enhance the Saml2Backend class to automatically add or remove a user from DefectDojo groups based on the user's SAML2 group membership. This improves the synchronization of user group access with their SAML2 group assignments.

  3. dojo/settings/settings.dist.py: The changes in this file cover a wide range of configuration updates, including SAML2 authentication, deduplication and hashcode generation, additional authentication methods, security-related settings, Celery, logging, and file upload settings. These changes improve the security, usability, and reliability of the DefectDojo application.

Code Analysis

We ran 9 analyzers against 3 files and 0 analyzers had findings. 9 analyzers had no findings.

Riskiness

🟢 Risk threshold not exceeded.

View PR in the DryRun Dashboard.

@fopina fopina changed the base branch from master to dev November 16, 2024 01:22
Copy link
Contributor

@kiblik kiblik left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like this change. I was considering similar functionality in the past, and I'm happy that somebody has done it now.

A couple of comments:

  • Thanks for the nice explanation in the description of PR, can you put it in the documentation as well? https://documentation.defectdojo.com/integrations/social-authentication/
  • If you have an implementation that can filter-out the right list of groups, why not create them? I'm not saying it has to be default, but opt-in.
  • Other social provider's groups are markered (Remote, Azure). Can you add ”SAML” as a social provider?
    social_provider = models.CharField(max_length=10, choices=SOCIAL_CHOICES, blank=True, null=True, help_text=_("Group imported from a social provider."), verbose_name=_("Social Authentication Provider"))

Thank you

dojo/backends.py Outdated Show resolved Hide resolved
return user

# list of all existing "SAML2-mapped" groups
all_saml_groups = {group.name: group for group in Dojo_Group.objects.all() if self.group_re.match(group.name)}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
all_saml_groups = {group.name: group for group in Dojo_Group.objects.all() if self.group_re.match(group.name)}
all_saml_groups = {group.name: group for group in Dojo_Group.objects.filter(social_provider=Dojo_Group.SAML) if self.group_re.match(group.name)}

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess this makes sense thinking of the AzureAD pipeline yet it "forces" groups to have been created by this (instead of added manually to match IdP groups), right?

@@ -177,6 +177,11 @@
"Lastname": "last_name",
}),
DD_SAML2_ALLOW_UNKNOWN_ATTRIBUTE=(bool, False),
# similar to DD_SOCIAL_AUTH_AZUREAD_TENANT_OAUTH2_GROUPS_FILTER, regular expression for which SAML2 groups to map to Dojo groups (with same name)
# Groups need to already exist in Dojo. And if value is not set, no group processing is done
DD_SAML2_GROUPS_FILTER=(str, ""),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you call it _READERS as it adds a Reader role? In the future

Suggested change
DD_SAML2_GROUPS_FILTER=(str, ""),
DD_SAML2_GROUPS_FILTER_READERS=(str, ""),

Or even more future-proof (if new roles would be created): dict with mapping to roles

Suggested change
DD_SAML2_GROUPS_FILTER=(str, ""),
DD_SAML2_GROUPS_FILTER=(dict, {"readers": ""}),

More info, about how dict works with environ

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

At first sight, I agree this would make sense, but does it really? When would anyone not be just reader in a group where the membership is expected to be managed by IdP?

Noone should be able to add/remove people from the group in Dojo, they should do it in IdP, otherwise that person will eventually be removed from the group (upon login)

Maybe I'm missing some use case in my mind, please let me know if so. Otherwise this would just increase complexity of the configuration without any added benefit

@fopina
Copy link
Author

fopina commented Nov 16, 2024

Thank you @kiblik

Quick feedback on number 2: my initial implementation was to create the groups on demand (not optional) but there's the save signal on groups that uses user for that request and fails (with NoneType exception as it is unexpected) because there's no user (at that time yet as not done authenticating).

I decided to remove creation as that also allows more granular control on the groups.

If I make it optional too, it would allow both things, but what's the right fix on the owner? Should I modify the signal too to allow a group without owner?

@kiblik
Copy link
Contributor

kiblik commented Nov 16, 2024

If I make it optional too, it would allow both things, but what's the right fix on the owner? Should I modify the signal too to allow a group without owner?

Is it the owner of a group? I'm not able to find this attribute. Can you send the link to which signal was failing, please?

@fopina
Copy link
Author

fopina commented Nov 16, 2024

I believe it was this one https://github.com/fopina/django-DefectDojo/blob/268583954fb74635fc708c49d8826e1534cd8f5d/dojo/group/utils.py#L37
Not 100% sure as I’ve been using this in my fork for a few months now

Easy to fix in several ways, not sure which one is preferred

@kiblik
Copy link
Contributor

kiblik commented Nov 16, 2024

Easy to fix in several ways, not sure which one is preferred

It is hard to say about preference but impersonate came to my mind first.

@fopina
Copy link
Author

fopina commented Nov 16, 2024

I meant less technically but more business oriented: which user should own the group? Should anyone?

This was the reason I didn’t try to solve it.
And I have the impression it’s the same for AzureAD, groups are not created (not confirmed though as I’m mobile now)

@fopina
Copy link
Author

fopina commented Nov 16, 2024

@kiblik my bad, AzureAD pipeline does create the groups but one of the required settings skips that owner user in the signal.

I guess I can simply do the same then, I'll update the PR next week 👍

Copy link
Contributor

This pull request has conflicts, please resolve those before we can evaluate the pull request.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
conflicts-detected settings_changes Needs changes to settings.py based on changes in settings.dist.py included in this PR
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants