Change LDAP directory structure to accommodate multiple domains #127

Closed
opened 2020-01-30 16:05:52 +00:00 by raucao · 17 comments
Owner
  • New hierarchy design
  • Adapt MediaWiki config for new hierarchy
* [x] New hierarchy design * [x] Adapt MediaWiki config for new hierarchy
raucao added a new dependency 2020-01-30 16:06:20 +00:00
greg was assigned by raucao 2020-01-30 16:06:32 +00:00
Owner

Proposed hierarchy:

dc=kosmos,dc=org
    cn=users
        ou=kosmos.org
            uid=alice
            uid=bob
        ou=customer.pro 
            uid=claire

For comparison here is our current hierarchy that does not support domains:

dc=kosmos,dc=org
    ou=users
        uid=alice
        uid=bob

Here's what the ldif files for the proposal would look like:

# container for the organizationUnits (domains)
dn: cn=users,dc=kosmos,dc=org
objectClass: top
objectClass: organizationalRole
cn: users

# kosmos.org
dn: ou=kosmos.org,cn=users,dc=kosmos,dc=org
objectClass: top
objectClass: organizationalUnit
ou: kosmos.org

# customer.pro pro organization
dn: ou=customer.pro,cn=users,dc=kosmos,dc=org
objectClass: top
objectClass: organizationalUnit
ou: customer.pro

And this is what a user would look like:

# alice@kosmos.org
dn: cn=alice,ou=kosmos.org,cn=users,dc=kosmos,dc=org
objectClass: top
objectClass: account
objectClass: person
objectClass: extensibleObject
cn: alice
sn: alice
uid: alice
samaccountname: alice
mail: alice@example.com
wiki: enabled
xmpp: enabled
userPassword: {SSHA512}[SNIP]=

# claire@kosmos.org
dn: cn=claire,ou=customer.pro,cn=users,dc=kosmos,dc=org
objectClass: top
objectClass: account
objectClass: person
objectClass: extensibleObject
cn: alice
sn: alice
uid: alice
samaccountname: alice
mail: alice@customer.pro
wiki: enabled
xmpp: enabled
userPassword: {SSHA512}[SNIP]=

Does that look correct to you?

Proposed hierarchy: ``` dc=kosmos,dc=org cn=users ou=kosmos.org uid=alice uid=bob ou=customer.pro uid=claire ``` For comparison here is our current hierarchy that does not support domains: ``` dc=kosmos,dc=org ou=users uid=alice uid=bob ``` Here's what the ldif files for the proposal would look like: ```ldif # container for the organizationUnits (domains) dn: cn=users,dc=kosmos,dc=org objectClass: top objectClass: organizationalRole cn: users # kosmos.org dn: ou=kosmos.org,cn=users,dc=kosmos,dc=org objectClass: top objectClass: organizationalUnit ou: kosmos.org # customer.pro pro organization dn: ou=customer.pro,cn=users,dc=kosmos,dc=org objectClass: top objectClass: organizationalUnit ou: customer.pro ``` And this is what a user would look like: ```ldif # alice@kosmos.org dn: cn=alice,ou=kosmos.org,cn=users,dc=kosmos,dc=org objectClass: top objectClass: account objectClass: person objectClass: extensibleObject cn: alice sn: alice uid: alice samaccountname: alice mail: alice@example.com wiki: enabled xmpp: enabled userPassword: {SSHA512}[SNIP]= # claire@kosmos.org dn: cn=claire,ou=customer.pro,cn=users,dc=kosmos,dc=org objectClass: top objectClass: account objectClass: person objectClass: extensibleObject cn: alice sn: alice uid: alice samaccountname: alice mail: alice@customer.pro wiki: enabled xmpp: enabled userPassword: {SSHA512}[SNIP]= ``` Does that look correct to you?
Author
Owner

Looks good. But shouldn't wiki and xmpp rather be user groups?

This is also missing the groups considerations (or whatever other solution may make sense) for Gitea orgs for example.

Please think outside the box there, because it's complete greenfield and we haven't decided on any pricing/donation structures. For example either a minimum donation amount in general (no matter for which service) could allow a user to create one or more orgs. Or, akkounts-api could create orgs without users being allowed to create their own.

Also, one small thing: Gitea (and likely other services too) allows uppercase letters in usernames.

Looks good. But shouldn't `wiki` and `xmpp` rather be user groups? This is also missing the groups considerations (or whatever other solution may make sense) for Gitea orgs for example. Please think outside the box there, because it's complete greenfield and we haven't decided on any pricing/donation structures. For example either a minimum donation amount in general (no matter for which service) could allow a user to create one or more orgs. Or, akkounts-api could create orgs without users being allowed to create their own. Also, one small thing: Gitea (and likely other services too) allows uppercase letters in usernames.
Author
Owner

One more thing:

"mail" should probably be split into "smtp" and "imap". And we need an actual "email" property for the user's own (fallback) email.

Edit: Gitea suggest "mail" as well, so this is just an idea.

One more thing: "mail" should probably be split into "smtp" and "imap". And we need an actual "email" property for the user's own (fallback) email. Edit: Gitea suggest "mail" as well, so this is just an idea.
Author
Owner

Gitea also supports an attribute for SSH public keys, and a bunch of other things:

https://gitea.kosmos.org/admin/auths/new

In fact, LDAP support in Gitea looks pretty darn good!

Gitea also supports an attribute for SSH public keys, and a bunch of other things: https://gitea.kosmos.org/admin/auths/new In fact, LDAP support in Gitea looks pretty darn good!
Owner

One more thing:

“mail” should probably be split into “smtp” and “imap”. And we need an actual “email” property for the user’s own (fallback) email.

Edit: Gitea suggest “mail” as well, so this is just an idea.

mail is part of the standard attributes on a User/Account for email. Gitea supports does look very good and flexible! Gitea allows to set the mail attribute you want, but most LDAP integration doesn't give you that flexibility. Most of the time you can only set the User Base and a User Filter

I'm looking into groups and roles to find out ways that would work with organizations, as well as services

> One more thing: > > “mail” should probably be split into “smtp” and “imap”. And we need an actual “email” property for the user’s own (fallback) email. > > Edit: Gitea suggest “mail” as well, so this is just an idea. `mail` is part of the standard attributes on a User/Account for email. Gitea supports does look very good and flexible! Gitea allows to set the mail attribute you want, but most LDAP integration doesn't give you that flexibility. Most of the time you can only set the User Base and a User Filter I'm looking into groups and roles to find out ways that would work with organizations, as well as services
Owner

So far I think for the use case of enabling different services, that roles, in particular filtered roles would be a good fit.

Right now I cannot think of a good use of groups for us. This would be useful for giving out people UNIX accounts and adding them automatically to a UNIX group, for example. memberOf gives you a similar experience to filtered roles, except it only works on static groups, not on dynamic groups created with groupOfURLs (that can be used similarly to filtered roles). So at first glance I thought dynamic groups would be great, until I figured out then it's not convenient to know what dynamic group a user belongs to.

Filtered roles

Here is an example of a filtered role:

# role definition
dn: cn=wiki,ou=kosmos.org,cn=users,dc=kosmos,dc=org
objectclass: top
objectclass: LDAPsubentry
objectclass: nsRoleDefinition
objectclass: nsComplexRoleDefinition
objectclass: nsFilteredRoleDefinition
cn: wiki
nsRoleFilter: wiki=enabled
Description: filtered role for wiki

In this example I am using a simple filter, but it can be more complex. When filtering from just one attribute it is as easy to filter directly using the attributes.

Then you can filter users that have the role applied:

$ ldapsearch -x -D 'cn=Directory Manager' -b "ou=kosmos.org,cn=users,dc=kosmos,dc=org"  "(&(objectclass=person)(nsRole=cn=wiki,ou=kosmos.org,cn=users,dc=kosmos,dc=org))" \* nsRole
# alfred, kosmos.org, users, kosmos.org
dn: cn=alfred,ou=kosmos.org,cn=users,dc=kosmos,dc=org
objectClass: top
objectClass: account
objectClass: person
objectClass: extensibleObject
cn: alfred
sn: alfred
uid: alfred
samaccountname: alfred
mail: alfred@example.com
wiki: enabled
xmpp: enabled
admin: false
userPassword:: [snip]
nsRole: cn=wiki,ou=kosmos.org,cn=users,dc=kosmos,dc=org

Static group

Here is an example of a static group:

dn: cn=wiki,ou=kosmos.org,cn=roles,dc=kosmos,dc=org
objectClass: top
objectClass: groupOfUniqueNames
cn: wiki
uniqueMember: cn=alfred,ou=kosmos.org,cn=users,dc=kosmos,dc=org

Static groups need a plugin to be abled, that consists of enabling it and setting the attributes on the group and the account:

dn: cn=MemberOf Plugin,cn=plugins,cn=config
changetype: modify
replace: nsslapd-pluginEnabled
nsslapd-pluginEnabled: on

dn: cn=MemberOf Plugin,cn=plugins,cn=config
changetype: modify
replace: memberofgroupattr
memberofgroupattr: uniqueMember

dn: cn=MemberOf Plugin,cn=plugins,cn=config
changetype: modify
replace: memberofattr
memberofattr: memberOf

The 389 server then needs to be restarted, and it's easier to only create the users after this, otherwise the memberOf values need to be regenerated. I haven't found a way to successfully regenerate these on the version of 389 that ships with Ubuntu 18.06

Then you can list users in the group, and see if a user is a member of a group:

$ ldapsearch -x -D 'cn=Directory Manager' -b "cn=wiki,ou=kosmos.org,cn=roles,dc=kosmos,dc=org"

# wiki, kosmos.org, roles, kosmos.org
dn: cn=wiki,ou=kosmos.org,cn=roles,dc=kosmos,dc=org
objectClass: top
objectClass: groupOfUniqueNames
cn: wiki
uniqueMember: cn=alfred,ou=kosmos.org,cn=users,dc=kosmos,dc=org

$ ldapsearch -x -D 'cn=Directory Manager' -b "ou=kosmos.org,cn=users,dc=kosmos,dc=org" "(&(objectclass=person))"
# alfred, kosmos.org, users, kosmos.org
dn: cn=alfred,ou=kosmos.org,cn=users,dc=kosmos,dc=org
objectClass: top
objectClass: account
objectClass: person
objectClass: extensibleObject
cn: alfred
sn: alfred
uid: alfred
samaccountname: alfred
mail: alfred@example.com
wiki: enabled
xmpp: enabled
admin: false
memberOf: cn=wiki,ou=kosmos.org,cn=roles,dc=kosmos,dc=org
userPassword:: [snip]

Do you have questions about filtered groups? Do you think it is a good fit for us>? Of course using attributes means they need to be locked down, so users cannot edit any attribute on their own account except for their email and password. This should be done as part of #128

Gitea & organizations

When it comes to Gitea, the LDAP support is for authentication, including adding admin privileges to users, but it looks like we'll have to deal with organizations ourselves. On an LDAP user/account there is a o attribute, for example o=kosmos. I think adding users to organizations could be done as a Gitea plugin or using the API

So far I think for the use case of enabling different services, that roles, in particular [filtered roles](https://access.redhat.com/documentation/en-us/red_hat_directory_server/11/html/administration_guide/advanced_entry_management-using_roles#creating_a_filtered_role) would be a good fit. Right now I cannot think of a good use of [groups](https://access.redhat.com/documentation/en-us/red_hat_directory_server/11/html/administration_guide/advanced_entry_management#Advanced_Entry_Management-Using_Groups) for us. This would be useful for giving out people UNIX accounts and adding them automatically to a UNIX group, for example. `memberOf` gives you a similar experience to filtered roles, except it only works on static groups, not on dynamic groups created with `groupOfURLs` (that can be used similarly to filtered roles). So at first glance I thought dynamic groups would be great, until I figured out then it's not convenient to know what dynamic group a user belongs to. ## Filtered roles Here is an example of a filtered role: ```ldif # role definition dn: cn=wiki,ou=kosmos.org,cn=users,dc=kosmos,dc=org objectclass: top objectclass: LDAPsubentry objectclass: nsRoleDefinition objectclass: nsComplexRoleDefinition objectclass: nsFilteredRoleDefinition cn: wiki nsRoleFilter: wiki=enabled Description: filtered role for wiki ``` In this example I am using a simple filter, but it can be more complex. When filtering from just one attribute it is as easy to filter directly using the attributes. Then you can filter users that have the role applied: ``` $ ldapsearch -x -D 'cn=Directory Manager' -b "ou=kosmos.org,cn=users,dc=kosmos,dc=org" "(&(objectclass=person)(nsRole=cn=wiki,ou=kosmos.org,cn=users,dc=kosmos,dc=org))" \* nsRole # alfred, kosmos.org, users, kosmos.org dn: cn=alfred,ou=kosmos.org,cn=users,dc=kosmos,dc=org objectClass: top objectClass: account objectClass: person objectClass: extensibleObject cn: alfred sn: alfred uid: alfred samaccountname: alfred mail: alfred@example.com wiki: enabled xmpp: enabled admin: false userPassword:: [snip] nsRole: cn=wiki,ou=kosmos.org,cn=users,dc=kosmos,dc=org ``` ## Static group Here is an example of a static group: ``` dn: cn=wiki,ou=kosmos.org,cn=roles,dc=kosmos,dc=org objectClass: top objectClass: groupOfUniqueNames cn: wiki uniqueMember: cn=alfred,ou=kosmos.org,cn=users,dc=kosmos,dc=org ``` Static groups need a plugin to be abled, that consists of enabling it and setting the attributes on the group and the account: ```ldif dn: cn=MemberOf Plugin,cn=plugins,cn=config changetype: modify replace: nsslapd-pluginEnabled nsslapd-pluginEnabled: on dn: cn=MemberOf Plugin,cn=plugins,cn=config changetype: modify replace: memberofgroupattr memberofgroupattr: uniqueMember dn: cn=MemberOf Plugin,cn=plugins,cn=config changetype: modify replace: memberofattr memberofattr: memberOf ``` The 389 server then needs to be restarted, and it's easier to only create the users after this, otherwise the `memberOf` values need to be regenerated. I haven't found a way to successfully regenerate these on the version of 389 that ships with Ubuntu 18.06 Then you can list users in the group, and see if a user is a member of a group: ``` $ ldapsearch -x -D 'cn=Directory Manager' -b "cn=wiki,ou=kosmos.org,cn=roles,dc=kosmos,dc=org" # wiki, kosmos.org, roles, kosmos.org dn: cn=wiki,ou=kosmos.org,cn=roles,dc=kosmos,dc=org objectClass: top objectClass: groupOfUniqueNames cn: wiki uniqueMember: cn=alfred,ou=kosmos.org,cn=users,dc=kosmos,dc=org $ ldapsearch -x -D 'cn=Directory Manager' -b "ou=kosmos.org,cn=users,dc=kosmos,dc=org" "(&(objectclass=person))" # alfred, kosmos.org, users, kosmos.org dn: cn=alfred,ou=kosmos.org,cn=users,dc=kosmos,dc=org objectClass: top objectClass: account objectClass: person objectClass: extensibleObject cn: alfred sn: alfred uid: alfred samaccountname: alfred mail: alfred@example.com wiki: enabled xmpp: enabled admin: false memberOf: cn=wiki,ou=kosmos.org,cn=roles,dc=kosmos,dc=org userPassword:: [snip] ``` Do you have questions about filtered groups? Do you think it is a good fit for us>? Of course using attributes means they need to be locked down, so users cannot edit any attribute on their own account except for their email and password. This should be done as part of #128 ## Gitea & organizations When it comes to Gitea, the [LDAP support](https://docs.gitea.io/en-us/authentication/) is for authentication, including adding admin privileges to users, but it looks like we'll have to deal with organizations ourselves. On an LDAP user/account there is a `o` attribute, for example `o=kosmos`. I think adding users to organizations could be done as a Gitea plugin or using the API
Author
Owner

When it comes to Gitea, the LDAP support is for authentication, including adding admin privileges to users, but it looks like we’ll have to deal with organizations ourselves

It seems like there's misunderstanding about what we need from akkounts in regards to orgs. The important thing is not adding users to organizations, but to allow users to create organizations. As you say, this most likely needs to be done via the Gitea API from akkounts-api, but the LDAP directory needs to have a flag for this somewhere.

> When it comes to Gitea, the LDAP support is for authentication, including adding admin privileges to users, but it looks like we’ll have to deal with organizations ourselves It seems like there's misunderstanding about what we need from akkounts in regards to orgs. The important thing is not adding users to organizations, but to allow users to *create* organizations. As you say, this most likely needs to be done via the Gitea API from `akkounts-api`, but the LDAP directory needs to have a flag for this somewhere.
Owner

OK, now I understand. In Gitea there is a global setting for "Allow Creation of Organizations by Default", that is off by default and off in our deployment. Admins can always create organizations, and there's also a flag on regular users for their ability to create organizations if needed

For Pro users we could create the Gitea organization, then add the users to the Gitea org. In LDAP the users being under the ou=customer.pro,cn=users,dc=kosmos,dc=org suffix would be enough. So I think all we need is to add a description to store the org's name, because so far we only had the domain. So a Pro org in LDAP would look like this (added the description attribute):

# customer.pro pro organization
dn: ou=customer.pro,cn=users,dc=kosmos,dc=org
objectClass: top
objectClass: organizationalUnit
description: Pro Customer
ou: customer.pro

I also found this project in a Gitea issue: https://github.com/tws-inc/gitea-group-sync

OK, now I understand. In Gitea there is a global setting for "Allow Creation of Organizations by Default", that is off by default and off in our deployment. Admins can always create organizations, and there's also a flag on regular users for their ability to create organizations if needed For Pro users we could create the Gitea organization, then add the users to the Gitea org. In LDAP the users being under the `ou=customer.pro,cn=users,dc=kosmos,dc=org` suffix would be enough. So I think all we need is to add a `description` to store the org's name, because so far we only had the domain. So a Pro org in LDAP would look like this (added the `description` attribute): ```ldif # customer.pro pro organization dn: ou=customer.pro,cn=users,dc=kosmos,dc=org objectClass: top objectClass: organizationalUnit description: Pro Customer ou: customer.pro ``` I also found this project in a [Gitea issue](https://github.com/go-gitea/gitea/issues/7194): https://github.com/tws-inc/gitea-group-sync
Author
Owner

It's very difficult to discuss these topics when the terms used are either inaccurate, or they exist but for something different.

  1. There are no "pro customers" for Gitea orgs in our planning right now. We only used this term for Kosmos Chat for organizations.
  2. Keeping anything about the Gitea org in LDAP would only be needed for the donations, not for any user management. This would all be done in Gitea, and no other user account would have to have org info in LDAP. In fact, we could skip LDAP here and just query the Gitea database directly even. In any case, Gitea is not something that is part of a private/company/custom-domain Kosmos Chat plan.
  3. For Kosmos Chat Pro, we would indeed have to add users to an org, but not as an org property. Those are simply more full accounts, which people with permission can create under their domain (as opposed to those users registering themselves).
It's very difficult to discuss these topics when the terms used are either inaccurate, or they exist but for something different. 1. There are no "pro customers" for Gitea orgs in our planning right now. We only used this term for Kosmos Chat for organizations. 2. Keeping anything about the Gitea org in LDAP would only be needed for the *donations*, not for any user management. This would all be done in Gitea, and no other user account would have to have org info in LDAP. In fact, we could skip LDAP here and just query the Gitea database directly even. In any case, Gitea is not something that is part of a private/company/custom-domain Kosmos Chat plan. 3. For Kosmos Chat Pro, we would indeed have to add users to an org, but not as an org property. Those are simply more full accounts, which people with permission can create under their domain (as opposed to those users registering themselves).
Owner

Thanks for clarifying it, this is all clearer to me now

Thanks for clarifying it, this is all clearer to me now
Author
Owner

By the way, filtered roles seem like a good solution for enabling/disabling services. 👍

One of the main questions I still have is if we somehow keep donation status in LDAP, or if we need yet another database/storage for that.

By the way, filtered roles seem like a good solution for enabling/disabling services. :+1: One of the main questions I still have is if we somehow keep donation status in LDAP, or if we need yet another database/storage for that.
Owner

I agree, here's a filtered role example (for the 5apps XMPP config): #123 (comment)

Now that I have figured out the LDAP ACIs, I think donation status can be stored in LDAP. The filtered role could use an extra donation status field to determine if an account is enabled or not

I agree, here's a filtered role example (for the 5apps XMPP config): https://gitea.kosmos.org/kosmos/chef/issues/123#issuecomment-1554 Now that I have figured out the LDAP ACIs, I think donation status can be stored in LDAP. The filtered role could use an extra donation status field to determine if an account is enabled or not
Author
Owner

It's not so much about if the account is enabled, but when to send a message to donate again.

It's not so much about if the account is enabled, but when to send a message to donate again.
Owner

Moved from the ejabberd issue:

I ran into an issue with MediaWiki in my VM when using the new LDAP schema. I was preparing the config for MediaWiki, and I cannot make the LDAP authorization work using the filtered role. It looks like we have to use an attribute directly to perform the auth check in the MediaWiki config. I will take another look at this tomorrow

Moved from the ejabberd issue: I ran into an issue with MediaWiki in my VM when using the new LDAP schema. I was preparing the config for MediaWiki, and I cannot make the LDAP authorization work using the filtered role. It looks like we have to use an attribute directly to perform the auth check in the MediaWiki config. I will take another look at this tomorrow
Author
Owner

Why do we have to filter anything? We agreed that every user should have a wiki account, no matter what (and that we may want to do the same with other accounts, like e.g. personal Gitea accounts).

Why do we have to filter anything? We agreed that every user should have a wiki account, no matter what (and that we may want to do the same with other accounts, like e.g. personal Gitea accounts).
Owner

Why do we have to filter anything? We agreed that every user should have a wiki account, no matter what (and that we may want to do the same with other accounts, like e.g. personal Gitea accounts).

Yes, in the end removing the LDAPAuthorization Mediawiki extension works in this case, no need for filtering. I'm pushing a PR with the config changes

> Why do we have to filter anything? We agreed that every user should have a wiki account, no matter what (and that we may want to do the same with other accounts, like e.g. personal Gitea accounts). Yes, in the end removing the LDAPAuthorization Mediawiki extension works in this case, no need for filtering. I'm pushing a PR with the config changes
Owner

Closing this one since we've migrated ejabberd

Docs are on the wiki page: https://wiki.kosmos.org/Infrastructure:LDAP

Closing this one since we've migrated ejabberd Docs are on the wiki page: https://wiki.kosmos.org/Infrastructure:LDAP
greg closed this issue 2020-02-20 13:37:31 +00:00
Sign in to join this conversation.
No Milestone
No project
No Assignees
2 Participants
Notifications
Due Date
The due date is invalid or out of range. Please use the format 'yyyy-mm-dd'.

No due date set.

Reference: kosmos/chef#127
No description provided.