Code Monkey home page Code Monkey logo

gitlab-ce-ldap-sync's Introduction

LDAP users and groups sync script for Gitlab-CE and Gitlab-EE

This nifty little PHP-CLI tool will synchronise users and user groups from an LDAP server to Gitlab community edition instance(s). This also works on enterprise editions on the free tier, of which has identical functionality to the community edition.

Though this functionality is available out of the box with (non-free) Gitlab enterprise edition the pricing model is completely infeasible for teams of hobbyists working on non-revenue based projects but need to use a centralised authentication base.

As a bonus it can also do a light rake of LDAP users not currently in Gitlab, so those that haven't signed in for their first time can still have projects and permissions assigned to them. This may make the tool unsuitable git Gitlab-EE as this would certainly impact its licensing fees!

This tool has had basic community quality assurance.

Use in production environment at your own risk.

Though all of this tool's features are now implemented, very limited testing has happened, so don't expect a perfect experience. You should therefore only use this on test Gitlab CE/EE instances, or if you must use this on your production environment, at the very least take a backup of your Gitlab data before using this.

Features implemented:

  • Reading users from LDAP
  • Reading groups from LDAP
  • Synchronising users to Gitlab
  • Synchronising groups to Gitlab
  • Synchronising Gitlab group memberships based on LDAP group memberships

Not implemented:

  • Sub-group handling
  • SSH key importing (in progress): No longer required: Gitlab has a native configuration option "sync_ssh_keys" to specify a user attribute to synchronise keys from.

If in doubt use the dry run -d option to prevent writing to Gitlab first, combined with -vv to see exactly what would happen.

You have been warned!

Getting Started

These instructions will get you a copy of the project up and running on your local machine for development, testing, and live purposes.

Prerequisites

Requirements for running this tool from a management station:

  • Any system that can run PHP-CLI will do. (Even Windows.)
  • PHP version 8.1 or later: Available to most Linux distributions via apt-get or yum. You don't need anything web related, but you will need the command line interface.
  • PHP's LDAP functions: Usually installed with PHP as standard, but the LDAP module/functions may not be enabled by default.
  • Composer: Available to most Linux distributions via apt-get or yum, or manually download it as composer.phar alongside this tool.
  • LDAP instance: Used for Gitlab's authentication. It can (likely) be Microsoft Active Directory, OpenLDAP, 389-DS (including FreeIPA), and any other LDAP system, though most of my testing is with 389-DS (without FreeIPA).
  • Gitlab community edition or Gitlab community edition self-hosted: This must be configured to authenticate against an LDAP instance already.

Installing

Either checkout this project or download it in ZIP form and extract it somewhere safe. The configuration will later contain an LDAP password and Gitlab API secret keys, so do put some protection in place to ensure only you can access it.

After this you will need to install PHP components delivered via Composer. To do this open a terminal and change the working directory to this tool's location.

  • If you have Composer installed as a system-wide application (e.g. via apt-get or yum) use command composer install.
  • If you have Composer manually downloaded residing as composer.phar alongside this tool use command php composer.phar install.

Configuration

Make a duplicate copy of config.yml.dist called config.yml, then open config.yml in your favourite text editor. You may be able to work out how to configure this quite easily yourself, but still here's an explanation.

ldap

This section configures how to communicate with your LDAP instance.

debug (bool|null)

Enable this to show debugging information regarding LDAP connectivity. This is useful for detecting issues such as SSL certificate verification failures.

Default: false

winCompatibilityMode (bool|null)

In some Active Directory instances when "user_dn" is empty (for example when the whole AD is searched for users), LDAP queries fail with the message "ldap_search(): Search: Operations error.". This is further described in https://stackoverflow.com/questions/17742751/ldap-operations-error. The fix described there is applied when winCompatibilityMode is set to true. Since this has only been tested on one instance, it is recommended to leave this option at false and only activate it if you experience the error described above.

Default: false

server

This sub-section configures how to connect to your LDAP server.

host (string)

IP or hostname of the LDAP server. You should use "localhost" if the you're running this tool on the same machine as the LDAP server.

port (int\null)

TCP port used to access the LDAP server. Typically 389 for unencrypted connections or STARTTLS encrypted connections, but 636 for implicit SSL/TLS connections.

Leaving this null will use the default port based on the encryption setting.

version (int|null)

The version of the LDAP protocol to use. Typically 3 these days.

Leaving this null will assume version 3.

encryption (string|null)

The encryption protocol.

  • "none" for unencrypted connections, usually via TCP port 389. (Generally only safe to use with "localhost" or a very tightly controlled tunnel between this tool and the LDAP server.)
  • "tls" for explicit SSL/TLS connections, usually via TCP port 389. (Often called "STARTTLS".)
  • "ssl" for implicit SSL/TLS connections, usually via TCP port 636. (Often called "LDAPS".)

Leaving this null will assume an unencrypted connection.

If the server isn't "localhost" and STARTTLS or LDAPS is unavailable due to whatever certificate issues you have, highly consider using an SSH tunnel with port forwarding to make a secure link instead. (How to do that is beyond the scope of this tool.)

bindDn (string|null)

If your LDAP server does not allow anonymous access (which is a sensible restriction) specify a full distinguished name. (You cannot just specify the user name on its own.)

For example: "uid=Administrator,ou=People,dc=example,dc=com"

bindPw (string|null)

If your LDAP server does not allow anonymous access (which is a sensible restriction) specify the password to go with the bind distinguished name.

Because this has to be stored in the tool's configuration in plain text it would be advisable to create a separate user with limited read-only access to your directory. Do not use a bind DN/password with administrative/root permissions!

queries

This sub-section configures what to look for in your LDAP instance.

baseDn (string)

Specify the base distinguished name to work with. This will also be appended to all further DN settings.

Example to work with entire domain name: "dc=example,dc=com" Example to work with a specific organisational unit tree: "ou=Internal,dc=example,dc=com"

userDn (string|null)

Specify the distinguished name containing user objects to be searched for.

  • For Microsoft Active Directory this is typically "cn=Users".
  • For OpenLDAP and 389-DS this is typically "ou=People".

Leaving this null will search the entire base DN.

Default: null

userFilter (string|null)

Specify a search filter for finding user objects within the above DN.

  • For Microsoft Active Directory this is typically "(&(objectCategory=person)(objectClass=user))".
    • If you want to exclude disabled users use "(&(objectCategory=person)(objectClass=user)(!(userAccountControl:1.2.840.113556.1.4.803:=2)))".
  • For OpenLDAP and 389-DS this is typically "(objectClass=inetOrgPerson)".
    • If you want to exclude disabled users on OpenLDAP use "(&(objectClass=inetOrgPerson)(objectClass=posixAccount)(shadowExpire=99999))". -- This requires your user objects to use the "posixAccount" class.
    • If you want to exclude disabled users on 389-DS use "(&(objectClass=inetOrgPerson)(!(nsAccountLock=true)))".
    • For both also consider excluding guest accounts with "(!(uid=nobody))(!(gidNumber=65534))" mixed into the AND condition.

Default: "(objectClass=inetOrgPerson)"

userUniqueAttribute (string|null)

Specify the attribute used to uniquely identify a user by their user name. Their values must be a simple name of which the user would typically type to login to Gitlab or any other application interfacing with the same directory.

Default: "uid"

userMatchAttribute (string|null)

By default, it is assumed that the userUniqueAttribute is a user name that can be used to unambiguously determine group membership of individual persons as well as being used for login credentials. If this is the case with your LDAP structure, set userMatchAttribute to be empty.

If that is not the case, userMatchAttribute can be used to separate these two functions. Specify userMatchAttribute to the feature of your user that determines his membership in a group and userUniqueAttribute to the user name attribute used for Gitlab login credentials. For instance, in some Microsoft Active Directory versions, groups possess a "member" attribute that lists the "distinguishedName" attributes of each member of the group. The user name however is a different attribute of each user being attributed to that group. In this case, set userMatchAttribute to "distinguishedName" and userUniqueAttribute to your user name attribute.

This attribute only makes sense if groupMemberAttribute is "memberUid".

Default: Same as userNameAttribute

userNameAttribute (string|null)

Specify the attribute used for the user's full real name.

Default: "cn"

userEmailAttribute (string|null)

Specify the attribute used for the user's email address. (If there are multiple values only the first will be used.)

Default: "mail"

groupDn (string|null)

Specify the distinguished name containing group objects to be searched for.

  • For Microsoft Active Directory this is typically "cn=Users". (Yes, that really is "Users", not "Groups".)
  • For OpenLDAP this is typically "ou=Group".
  • For 389-DS this is typically "ou=Groups".

Leaving this null will search the entire base DN.

Default: null

groupFilter (string|null)

Specify a search filter for finding group objects within the above DN.

  • For Microsoft Active Directory this is typically "(objectClass=group)".
  • For OpenLDAP this is typically "(objectClass=posixGroup)"
  • For 389-DS this is typically "(objectClass=groupOfUniqueNames)".

Default: "(objectClass=groupOfUniqueNames)"

groupUniqueAttribute (string|null)

Specify the attribute used to uniquely identify a group by its name.

Default: "cn"

groupMemberAttribute (string|null)

Specify the attribute for group objects defining which users are a member of it. Depending on the class of group, possible values can be "memberUid", "member", or "uniqueMember".

  • In case "memberUid" is used, the values must be user names in their simple form matching the values you'd get with "usersUniqueAttribute", and not containing any structural information such as full distinguished names of users.
  • In case "member" or "uniqueMember" is used, the values must be valid distinguished names of users.

Default: "memberUid"

gitlab

This section configures how to communicate with your Gitlab-CE/EE instance.

options

userNamesToIgnore (array|null)

Specify a list of user names of which this tool should ignore. (Case-insensitive.)

This varies not only according to which directory software you're using, but also how your directory has been structured.

  • For Microsoft Active Directory this is could be "Administrator", "Guest", and any other user you don't expect to contain human users.
  • OpenLDAP and 389-DS do not ship with any users out of the box, though "root" and "nobody" are likely candidates to ignore.

This must be defined as an array even if you have only 1 user. Be sure to quote user names that have spaces. For example:

userNamesToIgnore:
    - "nobody"
    - "Administrator"
    - "Guest"

User name "root" will always be ignored because this is the built-in Gitlab root user. This tool will not attempt to create/delete/sync this user name.

Default: null

groupNamesToIgnore (array|null)

Specify a list of group names of which this tool should ignore. (Case-insensitive.)

This varies not only according to which directory software you're using, but also how your directory has been structured. You do not have to specify every group if you've left the "createEmptyGroups" setting (further down) switched off, as this will prevent groups containing no users to be ignored anyway.

  • For Microsoft Active Directory this is could be "Domain Computers", "Domain Controllers", "DnsAdmins", "DnsUpdateProxy", and any other group you don't expect to contain human users.
  • OpenLDAP does ship with any groups out of the box.
  • For 389-DS this could be the four out of the box groups: "Accounting Managers", "HR Managers", "PD Managers", and "QA Managers".

This must be defined as an array even if you have only 1 group. Be sure to quote group names that have spaces. For example:

groupNamesToIgnore:
    - "Managed Service Accounts"
    - "Marketing Staff"

Group names "Root" and "Users" will always be ignored because they are built-in Gitlab groups. This will will not attempt to create/delete/sync these group names.

Default: null

createEmptyGroups (bool|null)

Specify whether groups containing no LDAP users should still be created in Gitlab.

You should enable this if you want to specify permissions for groups in advance so they'll be ready when the first user is added to that group. If your directory has a lot of empty groups enabling this would only replicate the clutter to Gitlab, so should be used with care for large directories.

Default: false

deleteExtraGroups (bool|null)

Specify whether Gitlab groups not found in LDAP should be deleted.

You should only enable this if you don't like empty groups being left over in Gitlab after doing a purge in your directory. Consider if such groups still contain projects you need to keep. (This scenario remains untested!)

Only empty Gitlab groups will ever be deleted. If there are extra groups with members still in them they will not be deleted.

Default: false

newMemberAccessLevel (integer|null)

The access level to provide users when added to groups.

  • 10: Guest
  • 20: Reporter
  • 30: Developer
  • 40: Maintainer
  • 50: Owner

This will not interfere with existing group members, so you can adjust user permissions in Gitlab later on.

Default: 30

groupNamesOfAdministrators (array|null)

Specify a list of group names of which members should be granted administrator access.

This varies not only according to which directory software you're using, but also how your directory has been structured. Users that have directory administrator access may not necessarily have Gitlab administrator access too, so this one is up to you.

  • For Microsoft Active Directory this is could be "Domain Admins" and "Enterprise Admins".
  • OpenLDAP and 389-DS do not ship with such a group out of the box as they typically offer a "Directory Administrator" non-user object or similar for administrative purposes via bind DN.

This must be defined as an array even if you have only 1 group. Be sure to quote group names that have spaces. For example:

groupNamesOfAdministrators:
    - "Domain Admins"
    - "Enterprise Admins"

Default: null

groupNamesOfExternal (array|null)

Specify a list of group names of which members should be marked as external.

This varies not only according to which directory software you're using, but also how your directory has been structured.

  • For Microsoft Active Directory this is could be "Domain Guests".
  • OpenLDAP and 389-DS do not ship with such a group out of the box as they typically allow anonymous usage.

This must be defined as an array even if you have only 1 group. Be sure to quote group names that have spaces. For example:

groupNamesOfExternal:
    - "Domain Guests"
    - "Clients"

Default: null

instances (array)

Declare one or more Gitlab instances to sync with. Each array key represents the instance name, which can be used later on to only sync with a particular instance (out of multiple) when running this tool.

your-instance-name-here (array)

Make up an instance name. For example if you had multiple Gitlab installations on servers named "Athena" and "Demeter" it would be sensible to tag them as "athena" and "demeter" in your configuration. All sub-sections of this configuration will be repeated for each instance.

url (string)

Specify the full HTTP/HTTPS URL to this Gitlab instance, e.g. "https://athena.gitlab.example.com" or "https://demeter.gitlab.example.com". This is the same URL you use to really visit this Gitlab installation from your web browser.

token (string)

Specify an API token (usually a personal token or impersonation token) this tool can use to interface with the Gitlab instance's API. This token will need to have the "api" and "sudo" flags available.

ldapServerName (string)

Specify the LDAP server name used by this Gitlab instance. You can find this in the "ldap_servers" section of the "gitlab.rb" configuration file, which represents an array of data specifying how to interface with LDAP such as server host address, bind DN, encryption, base, etc.

You may need to put "ldap" before this value! This hasn't been tested across different installation types, but using the Omnibus package it appears if your "gitlab.rb" has the following...

gitlab_rails['ldap_servers'] = {
    'main' => {
        ...
    }
}

...you will likely need to specify it as "ldapmain" for this setting.

Running

Once you've configured this tool you can run it from a CLI using:

php bin/console ldap:sync -d

Depending on your system's PHP installation you may need to use php-cli instead of php. (This typically only occurs on WHM/cPanel based servers configured to host PHP via the fast process manager, PHP-FPM.)

The -d option is important for your first run. This enables "dry run" mode, meaning no changes will be persisted to your Gitlab instances. After running this tool you should evaluate the changes that will be made based on the output, then run it again without the -d option to persist the changes.

If you'd like to see more verbose output you can add up to 3 -v switches, for example:

php bin/console ldap:sync -v
php bin/console ldap:sync -vv
php bin/console ldap:sync -vvv

If you'd like to only sync with a single Gitlab instance you can specify the name of it as per your configuration as an argument, for example:

php bin/console ldap:sync athena
php bin/console ldap:sync demeter

Built With

  • PHP: Entirely PHP.
  • Symfony: Symfony for the console, polyfill, and YAML components.

Contributing

Please read CONTRIBUTING.md for details on our code of conduct, and the process for submitting pull requests to us.

PHP source code must comply with PHP-FIG PSRs 1, 4, and 12 as much as reasonably possible.

Potential features

  • Specifying an attribute on the LDAP user in which this script could write back a user ID for each Gitlab instance.
    • This would mean user name (UID) changes in LDAP could be detected and synchronised automatically without user duplication happening.
    • It would likely be a string attribute in the form of instanceName:userId, for example athena:3 and demeter:15.
    • It could either be a multi-value attribute to handle multiple Gitlab instances, or a single-value attribute split by a semi-colon, for example athena:3;demeter:15.
  • Likely the same as the above but for groups too. (Group renaming.)

Versioning

We use SemVer for versioning. For the versions available, see the tags on this repository.

Authors

See also the list of contributors who participated in this project.

License

Copyright 2018 Adam Reece

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License.

You may obtain a copy of the License at

https://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.

gitlab-ce-ldap-sync's People

Contributors

adambean avatar adamreece-webbox avatar beharbunjaku avatar bow-el avatar dependabot[bot] avatar macleykun avatar vigen-b avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

gitlab-ce-ldap-sync's Issues

Group #1 / member #1: No matching user name found for group member attribute "member"

When specifying a group DN, it fails to find members of the group. My guess is that the users are not included in the same OU as the GroupDN so it cant find them?

I've removed the groupdn query and it works, but I have a bunch of unwanted groups.

It also works if I specify the group dn to point to an OU with users, and groups that are assigned users.

My config for reference.
image

Dockerize

Would love to have this on docker hub. I wrote a little docker file that can probably be optimized, but works fine for me right now if you'd like to expand on it/add it to the project:

FROM php:7.4.9-cli-alpine3.12

COPY --from=mlocati/php-extension-installer /usr/bin/install-php-extensions /usr/bin/

RUN apk update
RUN apk add bash
RUN apk add curl

# Install PHP extensions
RUN install-php-extensions ldap


# INSTALL COMPOSER
RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/bin --filename=composer 

WORKDIR /app
COPY . .

RUN composer install

CMD ["php", "bin/console", "ldap:sync"]

Question RE: Usage with gitlab-ce in 2024

Sorry not an "issue" per se, but as an ignoramous... would like to know whether the gitlab-ce of 2024 supports standard LDAP auth and partially obsoletes the functionality offered here? We sync with AD.

Context is an older build with this incorporated by a departed individual and our requirement to rebuild the host. Hoping to shave some technical debt to make maintenance a little easier for any successors.

Cursory Google with the time afforded so far, has me at a pre-emptive conclusion of Gitlab supporting LDAP on the community edition as of 2024 at least.
Will inevitably have to do some more thorough investigation as go-time approaches.

Appreciate your patience and thanks in advance.

Email address is not syncing properly

When I change the email address of a user in LDAP then that change is not synced back into GitLab. Which is making it harder to find people.

So far in my tests it will sync once during creation of the account. But when it's changed after that the email address will stay the same.

When LDAP group is deleted script stops

So I have this scenario. I've had a LDAP group with users in them. ldap-sync will create these users and group.
Then I've deleted those users and the group from LDAP.
When ldap-sync runs it will mark those users as external and will try to remove every user from the group. This is not possible since Gitlab needs at least one user to be owner of the group.
The script however will stop when running into this. I get this error:

[notice] Deleting extra group members...
[info] Deleting user #2 "some-user-name" from group #32 "some-group-name" [some-group-name].
[error] Gitlab failure: 403 Forbidden

I understand that this is not possible but what I expect is that the script reports the error but continues with the rest.

Another question is: if I have deleteExtraGroups set to true. Will this also delete gitlab groups with the same names in groupNamesToIgnore?

Filter by attributes

Hi,

Is it possible to use filter with attributes instead objects ? For example userFilter: (&(attrib1=x)(attrib2=y))

In my cas it doesn't work.

Thanks

Please support mixing with non-LDAP groups and users

On EduGit.org, users and groups can come from different sources. One is our LDAP directory, others are omniauth identities and manually created namespaces (groups). It seems that this tool does not expect any users or groups to be there that do not come from the LDAP directory. I do not know whether it just ocmplains loudly, but would work otherwise and just ignore the other users and groups, or if it would cause other errors if I ran it without the dry run option.

It would be good if this mode of operation could be supported correctly. Itworks out in general, as GitLab handles duplicate usernames from different sources quite nicely (it adds a number to the namespace if a newly authenticating user would cause the creation of an already existing namespace). It would maybe help if this tool had an option to keep all the synced LDAP groups as subgroups of a defined parent group, to start with.

Gitlab LDAP sync script removes dashes from LDAP group names.

Hi

I am testing out the ldap sync tool and when creating LDAP groups in gitlab I noticed that all dashes are removed from groups names.

E.g. I have a group named: cn=example-name Then it will be created as example name in gitlab. Of course the URL path is example-name.

Is there a way to change this behaviour? I would propose a config.yml option to specify whether to remove dashes or some special characters from the group name.

Wrong warning

This is probably not even called a bug but I noticed a wrong warning.
And I thought let's file a bug report to make this software even better!

I have multiple GitLab servers but I don't want to create every group on every GitLab server. So what I did was create an unique GitLab user for every server (gitlab01, gitlab02, etc) and in the group filter search for groups where it's unique user is a member of.
That way I can easily say on the LDAP server what group should live on which server. This works like a charm.

But when I run ldap sync I get a warning that user gitlab01 isn't found. Just before it sees that that user is on the ignore list so I would expect it shouldn't give a warning but a notice.

[notice] Loading configuration.
[notice] Loaded configuration.
[notice] Validating configuration.
[notice] Validated configuration.
[notice] Retrieving directory users and groups.
[notice] Establishing LDAP connection.
[notice] LDAP connection established.
[notice] 8 directory user(s) found.
[info] User "gitlab" in ignore list.
[info] Found directory user "jsiegers" [uid=jsiegers,cn=users,cn=accounts,dc=postlab,dc=cloud].
[info] Found directory user "pmlombert" [uid=pmlombert,cn=users,cn=accounts,dc=postlab,dc=cloud].
[info] Found directory user "rdkoning" [uid=rdkoning,cn=users,cn=accounts,dc=postlab,dc=cloud].
[info] Found directory user "tvdmaat" [uid=tvdmaat,cn=users,cn=accounts,dc=postlab,dc=cloud].
[info] User "gitlab01" in ignore list.
[info] User "gitlab02" in ignore list.
[notice] 4 directory user(s) recognised.
[notice] 3 directory group(s) found.
[info] Found directory group "test-team-3".
[warning] Group #1 / member #1: User not found "gitlab01".
[info] Found directory group "test-team-3" member "jsiegers".
[info] Found directory group "test-team-3" member "rdkoning".
[notice] 2 directory group "test-team-3" member(s) recognised.

Not creating Groups with only 1 member

Again love your script. Found another little bug.
Sometimes it does this. I've placed a user in a group but the group isn't made in GitLab.

In the beginning of the script it says:

[info] Found directory group "hedge".
[warning] Group #1 / member #1: User not found "gitlab01".
[info] Found directory group "hedge" member "jsiegers".
[notice] 1 directory group "hedge" member(s) recognised.

While later on when it needs to create the directory it says:

[notice] Creating directory groups of which don't exist in Gitlab...
[warning] Not creating Gitlab group "hedge" [hedge]: No members in directory group, or config gitlab->options->createEmptyGroups is disabled.
[notice] 0 Gitlab group(s) created.

But as soon as I put another user inside of the group it will create it. It looks like there need to be at least 2 users before it creates it. I expect that a group of 1 should also be possible.

LDAP connection with TLS or SSL returns error.

php bin/console ldap:sync -d -vvv
LDAP users and groups sync script for Gitlab-CE

[warning] Dry run enabled: No changes will be persisted.
[notice] Loading configuration.
[notice] Loaded configuration.
[notice] Validating configuration.
[warning] Configuration: ldap->server->port missing. (It will be determined by the encryption setting.)
[warning] Configuration: ldap->server->bindDn missing. (Assuming anonymous access.)
[warning] Configuration: gitlab->options->groupNamesToIgnore missing. (Assuming none.)
[warning] Configuration: gitlab->options->groupNamesOfExternal missing. (Assuming none.)
[notice] Validated configuration.
[notice] Retrieving directory users and groups.
Getting users and groups from LDAP...
[notice] Establishing LDAP connection.
[debug] LDAP: Enabling debug mode
[debug] LDAP: Connecting
[debug] LDAP: Setting options
[debug] LDAP: Binding
TLS: can't connect: (unknown error code).
[error] LDAP failure: Can't contact LDAP server. (Code -1)

I tried to see if I have certs:

openssl s_client -connect redacted:389 -starttls ldap -showcerts
openssl s_client -connect redacted:636 -showcerts

And I have certificates. Super weird issue. Currently running on port 389 and no encryption works for now.

No matching user name found for group member attribute "uniquemember".

Hello Adam,

I have the following setup:

queries:
        baseDn:                         dc=awd-group,dc=tech

        userDn:                         ou=People
        userFilter:                     "(objectClass=inetOrgPerson)"
        userUniqueAttribute:            "cn"
        userMatchAttribute:             ~
        userNameAttribute:              "displayName"
        userEmailAttribute:             "mail"

        groupDn:                        "ou=Group"
        groupFilter:                    "(objectClass=groupOfUniqueNames)"
        groupUniqueAttribute:           "cn"
        groupMemberAttribute:           "uniqueMember"

And unfortunately I get this in the log:

[info] Found directory group "test-ldapgitlab".
[warning] Group #65 / member #1: Empty member attribute "uniquemember".
[warning] Group #65 / member #2: No matching user name found for group member attribute "uniquemember".
[warning] Group #65 / member #3: No matching user name found for group member attribute "uniquemember".
[notice] 0 directory group "test-ldapgitlab" member(s) recognised.

Where as you can see my LDAP has members.

image

incomplete ldap sync

Hi,
(first, thanks for your tools !!)
I have a ldap group of 1500 user and when the script ask my server for synchronizing users, it only found 500 of them.
[notice] LDAP connection established. [notice] 501 directory user(s) found.
Any tip to identify the root cause of a partial sync ?

Pre-check:

  • no firewall connection limits on my LDAP server
  • if I relaunch the script several time, it is always the same 500 ldap users
  • My LDAP work correctly for other usages.

Thanks a lot

Subgroups memberships are also set

In our scenario we sometimes want to add another member to a subgroup. That member is not part of the group and is only invited to join the subgroup.

What the script now does is setting the memberships of the subgroups as well.
So I know subgroups are not yet supported but I would expect that it would simply skip subgroups and leave them alone. This is not the case.

Also when encountering a subgroup you get a lot of lines of log

[warning] Group "A Test Folder" doesn't appear to exist at path "a-test-folder". (Is this a sub-group? Sub-groups are not supported yet.)
[warning] Group "A Test Folder" doesn't appear to exist at path "a-test-folder". (Is this a sub-group? Sub-groups are not supported yet.)
[warning] Group "A Test Folder" doesn't appear to exist at path "a-test-folder". (Is this a sub-group? Sub-groups are not supported yet.)
[warning] Group "A Test Folder" doesn't appear to exist at path "a-test-folder". (Is this a sub-group? Sub-groups are not supported yet.)
[warning] Group "A Test Folder" doesn't appear to exist at path "a-test-folder". (Is this a sub-group? Sub-groups are not supported yet.)
[warning] Group "A Test Folder" doesn't appear to exist at path "a-test-folder". (Is this a sub-group? Sub-groups are not supported yet.)
[warning] Group "A Test Folder" doesn't appear to exist at path "a-test-folder". (Is this a sub-group? Sub-groups are not supported yet.)
[warning] Group "A Test Folder" doesn't appear to exist at path "a-test-folder". (Is this a sub-group? Sub-groups are not supported yet.)
[warning] Group "A Test Folder" doesn't appear to exist at path "a-test-folder". (Is this a sub-group? Sub-groups are not supported yet.)
[warning] Group "A Test Folder" doesn't appear to exist at path "a-test-folder". (Is this a sub-group? Sub-groups are not supported yet.)
[warning] Group "A Test Folder" doesn't appear to exist at path "a-test-folder". (Is this a sub-group? Sub-groups are not supported yet.)
[warning] Group "A Test Folder" doesn't appear to exist at path "a-test-folder". (Is this a sub-group? Sub-groups are not supported yet.)
[warning] Group "A Test Folder" doesn't appear to exist at path "a-test-folder". (Is this a sub-group? Sub-groups are not supported yet.)
[warning] Group "A Test Folder" doesn't appear to exist at path "a-test-folder". (Is this a sub-group? Sub-groups are not supported yet.)
[warning] Group "A Test Folder" doesn't appear to exist at path "a-test-folder". (Is this a sub-group? Sub-groups are not supported yet.)
[warning] Group "A Test Folder" doesn't appear to exist at path "a-test-folder". (Is this a sub-group? Sub-groups are not supported yet.)
[warning] Group "A Test Folder" doesn't appear to exist at path "a-test-folder". (Is this a sub-group? Sub-groups are not supported yet.)
[warning] Group "A Test Folder" doesn't appear to exist at path "a-test-folder". (Is this a sub-group? Sub-groups are not supported yet.)
[warning] Group "A Test Folder" doesn't appear to exist at path "a-test-folder". (Is this a sub-group? Sub-groups are not supported yet.)
[warning] Group "A Test Folder" doesn't appear to exist at path "a-test-folder". (Is this a sub-group? Sub-groups are not supported yet.)
[warning] Group "A Test Folder" doesn't appear to exist at path "a-test-folder". (Is this a sub-group? Sub-groups are not supported yet.)
[warning] Group "A Test Folder" doesn't appear to exist at path "a-test-folder". (Is this a sub-group? Sub-groups are not supported yet.)
[warning] Group "A Test Folder" doesn't appear to exist at path "a-test-folder". (Is this a sub-group? Sub-groups are not supported yet.)
[warning] Group "A Test Folder" doesn't appear to exist at path "a-test-folder". (Is this a sub-group? Sub-groups are not supported yet.)
[warning] Group "A Test Folder" doesn't appear to exist at path "a-test-folder". (Is this a sub-group? Sub-groups are not supported yet.)
[warning] Group "A Test Folder" doesn't appear to exist at path "a-test-folder". (Is this a sub-group? Sub-groups are not supported yet.)
[warning] Group "A Test Folder" doesn't appear to exist at path "a-test-folder". (Is this a sub-group? Sub-groups are not supported yet.)
[warning] Group "A Test Folder" doesn't appear to exist at path "a-test-folder". (Is this a sub-group? Sub-groups are not supported yet.)
[warning] Group "A Test Folder" doesn't appear to exist at path "a-test-folder". (Is this a sub-group? Sub-groups are not supported yet.)
[warning] Group "A Test Folder" doesn't appear to exist at path "a-test-folder". (Is this a sub-group? Sub-groups are not supported yet.)
[warning] Group "A Test Folder" doesn't appear to exist at path "a-test-folder". (Is this a sub-group? Sub-groups are not supported yet.)
[warning] Group "A Test Folder" doesn't appear to exist at path "a-test-folder". (Is this a sub-group? Sub-groups are not supported yet.)
[warning] Group "A Test Folder" doesn't appear to exist at path "a-test-folder". (Is this a sub-group? Sub-groups are not supported yet.)
[warning] Group "A Test Folder" doesn't appear to exist at path "a-test-folder". (Is this a sub-group? Sub-groups are not supported yet.)
[warning] Group "A Test Folder" doesn't appear to exist at path "a-test-folder". (Is this a sub-group? Sub-groups are not supported yet.)
[warning] Group "A Test Folder" doesn't appear to exist at path "a-test-folder". (Is this a sub-group? Sub-groups are not supported yet.)
[warning] Group "A Test Folder" doesn't appear to exist at path "a-test-folder". (Is this a sub-group? Sub-groups are not supported yet.)
[warning] Group "A Test Folder" doesn't appear to exist at path "a-test-folder". (Is this a sub-group? Sub-groups are not supported yet.)
[warning] Group "A Test Folder" doesn't appear to exist at path "a-test-folder". (Is this a sub-group? Sub-groups are not supported yet.)
[warning] Group "A Test Folder" doesn't appear to exist at path "a-test-folder". (Is this a sub-group? Sub-groups are not supported yet.)
[warning] Group "A Test Folder" doesn't appear to exist at path "a-test-folder". (Is this a sub-group? Sub-groups are not supported yet.)
[warning] Group "A Test Folder" doesn't appear to exist at path "a-test-folder". (Is this a sub-group? Sub-groups are not supported yet.)
[warning] Group "A Test Folder" doesn't appear to exist at path "a-test-folder". (Is this a sub-group? Sub-groups are not supported yet.)
[warning] Group "A Test Folder" doesn't appear to exist at path "a-test-folder". (Is this a sub-group? Sub-groups are not supported yet.)
[warning] Group "A Test Folder" doesn't appear to exist at path "a-test-folder". (Is this a sub-group? Sub-groups are not supported yet.)
[warning] Group "A Test Folder" doesn't appear to exist at path "a-test-folder". (Is this a sub-group? Sub-groups are not supported yet.)
[warning] Group "A Test Folder" doesn't appear to exist at path "a-test-folder". (Is this a sub-group? Sub-groups are not supported yet.)
[warning] Group "A Test Folder" doesn't appear to exist at path "a-test-folder". (Is this a sub-group? Sub-groups are not supported yet.)
[warning] Group "A Test Folder" doesn't appear to exist at path "a-test-folder". (Is this a sub-group? Sub-groups are not supported yet.)
[warning] Group "A Test Folder" doesn't appear to exist at path "a-test-folder". (Is this a sub-group? Sub-groups are not supported yet.)
[warning] Group "A Test Folder" doesn't appear to exist at path "a-test-folder". (Is this a sub-group? Sub-groups are not supported yet.)
[notice] Synchronising 0 member(s) for group #103 "A Test Folder" [a-test-folder]...
[notice] Finding existing group members...
[info] Found Gitlab group member #70 "peterpan".
[notice] 1 Gitlab group "A Test Folder" [a-test-folder] member(s) found.
[notice] Adding missing group members...
[notice] 0 Gitlab group "A Test Folder" [a-test-folder] member(s) added.
[notice] Deleting extra group members...
[info] Deleting user #70 "peterpan" from group #103 "A Test Folder" [a-test-folder].
[notice] 1 Gitlab group "A Test Folder" [a-test-folder] member(s) deleted.

Request: Change script order or allow for robust handling of duplicate emails

Our Admin would sometimes need to open another account for our users in AD and transfer their email address to the new account. The old account is then deactivated or given another email.

The sync script cannot transfer this to GitLab, as it first attempts to create new users and then update existing users. The first step fails, as the email for the new account is already taken by the existing account in GitLab. An update of existing accounts before this step would solve the issue. Alternatively, the script could handle duplicate emails in a more robust fashion and skip new account creation for duplicate emails without aborting execution and failing. These could then be created upon the script's second run.

Non-critical error after sync

[error] Gitlab failure: "namespace.route" can't be blank, "notification_email" can't be blank, "notification_email" is invalid

Remove users from groups

Hi,

Thanks for this awesome tool!
I'm using the FreeIPA directory servers and got it working. The only thing I can't do is remove users from a group. I've created an LDAP group (team1) and placed users in them. During the sync the group is then created and the users are added as members. All fine.
When I now remove a user from the LDAP group and then do a sync I get the following error:

[root@fipa01 gitlab-ce-ldap-sync]# php bin/console ldap:sync -vv
LDAP users and groups sync script for Gitlab-CE

[notice] Loading configuration.
[notice] Loaded configuration.
[notice] Validating configuration.
[notice] Validated configuration.
[notice] Retrieving directory users and groups.
[notice] Establishing LDAP connection.
[notice] LDAP connection established.
[notice] 4 directory user(s) found.
[info] User "gitlab" in ignore list.
[info] Found directory user "jsiegers" [uid=jsiegers,cn=users,cn=accounts,dc=postlab,dc=cloud].
[info] Found directory user "hdjong" [uid=hdjong,cn=users,cn=accounts,dc=postlab,dc=cloud].
[notice] 2 directory user(s) recognised.
[notice] 9 directory group(s) found.
[info] Found directory group "team1".
[info] Found directory group "team1" member "jsiegers".
[warning] Group #1 / member #2: User not found "gitlab".
[notice] 1 directory group "team1" member(s) recognised.
[info] Group "externalusers" in ignore list.
[info] Group "gitlabadmins" in ignore list.
[info] Found directory group "jsiegers".
[warning] Group #4: Missing attribute "memberuid". (Could also mean this group has no members.)
[info] Found directory group "gitlab".
[warning] Group #5: Missing attribute "memberuid". (Could also mean this group has no members.)
[info] Group "postlab-cloud" in ignore list.
[info] Group "editors" in ignore list.
[info] Group "admins" in ignore list.
[notice] 3 directory group(s) recognised.
[notice] LDAP connection closed.
[notice] Retrieved directory users and groups.
[notice] Deploying users and groups to Gitlab instances.
[notice] Establishing Gitlab connection.
[notice] Finding all existing Gitlab users...
[info] Found Gitlab user #38 "jsiegers".
[info] Found Gitlab user #37 "hdjong".
[info] Found Gitlab user #4 "ghost".
[info] Gitlab built-in root user will be ignored.
[notice] 3 Gitlab user(s) found.
[notice] Creating directory users of which don't exist in Gitlab...
[notice] 0 Gitlab user(s) created.
[notice] Disabling Gitlab users of which don't exist in directory...
[info] User "ghost" in ignore list.
[notice] 0 Gitlab user(s) disabled.
[notice] Updating users of which were already in both Gitlab and the directory...
[info] User "ghost" in ignore list.
[info] Updating Gitlab user #37 "hdjong".
[info] Updating Gitlab user #38 "jsiegers".
[notice] 2 Gitlab user(s) updated.
[notice] Finding all existing Gitlab groups...
[info] Found Gitlab group #4 "team1" [team1].
[notice] 1 Gitlab group(s) found.
[notice] Creating directory groups of which don't exist in Gitlab...
[warning] Not creating Gitlab group "gitlab" [gitlab]: No members in directory group, or config gitlab->options->createEmptyGroups is disabled.
[warning] Not creating Gitlab group "jsiegers" [jsiegers]: No members in directory group, or config gitlab->options->createEmptyGroups is disabled.
[notice] 0 Gitlab group(s) created.
[notice] Deleting Gitlab groups of which don't exist in directory...
[notice] 0 Gitlab group(s) deleted.
[notice] Updating groups of which were already in both Gitlab and the directory...
[info] Updating Gitlab group #4 "team1" [team1].
[notice] 1 Gitlab group(s) updated.
[notice] Synchronising Gitlab group members with directory group members...
[notice] Synchronising 1 member(s) for group #4 "team1" [team1]...
[notice] Finding existing group members...
[info] Gitlab built-in root user will be ignored.
[info] Found Gitlab group member #38 "jsiegers".
**[error] Group member #3: User not found.**
[notice] 1 Gitlab group "team1" [team1] member(s) found.
[notice] Adding missing group members...
[notice] 0 Gitlab group "team1" [team1] member(s) added.
[notice] Deleting extra group members...
[notice] 0 Gitlab group "team1" [team1] member(s) deleted.
[notice] Gitlab connection closed.
[notice] Deployed users and groups to Gitlab instances.
[root@fipa01 gitlab-ce-ldap-sync]#

I'm expecting that user is removed from the gitlab group as well during the Deleting extra group members. But that doesn't seem the case.
Am I missing something or is a bug?

Cheers in advance!

Support handling usernames and DNs being changed in the directory

I had looked into an almost identical issue on an unrelated project, in that what should happen if a vital attribute (such as "cn" or "uid") or a record DN entirely should alter. I outline this slightly in the Potential features section of the readme:

  1. Specifying an attribute on the LDAP user in which this script could write back a user ID for each Gitlab instance.
    • This would mean user name (UID) changes in LDAP could be detected and synchronised automatically without user duplication happening.
    • It would likely be a string attribute in the form of instanceName:userId, for example athena:3 and demeter:15.
    • It could either be a multi-value attribute to handle multiple Gitlab instances, or a single-value attribute split by a semi-colon, for example athena:3;demeter:15.
  2. Likely the same as the above but for groups too. (Group renaming.)

This can be evaded with manual intervention by an administrator whereby their Gitlab account would have its LDAP DN link adjusted prior to the user's next sign-in. I accept his is far from ideal as instances scale, particularly if an entire OU were to have all its users moved, thus changing all their DNs.

I can think of two ways to handle this without manual intervention, and welcome your thoughts to the debate.

Option 1: Add attribute to directory to record Gitlab user IDs in the directory

This tool will need an attribute within the LDAP to mark user objects with an ID number of their user entity in each Gitlab instance they're a part of.

This would be a fully portable solution however it would require a directory administrator to amend their schema, which is a huge ask for larger directories. (Particularly in the case of Active Directory whereby once an attribute is added to a schema it's there as good as permanently.)

There is also an inherent security risk because suddenly this tool would need write access to user objects the directory, even if it were just a specific attribute, where as it currently only requires read-only access.

Option 2: Track against a unique ID attribute in the LDAP user

Another approach would be to read a unique identifier in the LDAP user object and record this within the Gitlab user, possibly in the "extern_uid" or "note" field. This attribute would need to be somewhat permanent to the user such as a Unix user ID, GUID/UUID, or similar. Although this changes for each directory backend it could easily be a configurable option to this tool. Such attributes could be:

  • "objectGUID" or "objectSid" for Active Directory, though using the SID wouldn't be useful if the user moved to a different domain in the forest.
  • "nsUniqueID" for 389-DS/FreeIPA, though not all circumstances permit external applications to read this.
  • "entryUUID" for OpenLDAP.
  • "uidNumber" for any directory implementing the "posixAccount" object class on their users.

A potential problem would be storing it somewhere safe within the Gitlab user account. For example the "note" property can be arbitrarily adjusted by any Gitlab administrator so care/tagging would need to be used to warn administrators not to tamper with it. (Though if for some really rare reason a user's UUID or similar did change an administrator could come here to update it.)

It does however have an advantage in that the directory schema won't need changing, and this tool would not need write access to the directory's users.

Possible to only sync specified LDAP groups?

Hello!
Thank you for writing this program, itโ€™s a real life saver!
I do have one feature request;question.
Is it possible to specify which groups only can be synced? The structure I work with basically means I have to exclude every single group as we have lots in the same OU folder.
Smth like:
AllowedGroups: XYZ-GitLab-*
Which will only sync all groups that start with XYZ-GitLab-

I do hope itโ€™s possible to add this functionality, I also worked out the Dockerfile more and will create a PR soon, once Iโ€™m happy with the whole setup.
I may want to include a update-ca-certificates so that SSL works between LDAP and GitLab.

Request: Sync sshPublicKey from LDAP into Gitlab

Hi,

First, thank you so much for taking the time to create this very useful solution to sync Gitlab against an LDAP server!

I had a feature request: Would you consider adding support to pull available sshPublicKeys from a user's LDAP entry and sync that into Gitlab?

I AM able to get OpenSSH to pull sshPublicKeys from OpenLDAP using AuthorizedKeyCommands (https://serverfault.com/questions/653792/ssh-key-authentication-using-ldap , https://askubuntu.com/questions/776700/ssh-ldap-authorizedkeyscommand ) but unfortunately GitLab doesn't honor this.

This is also a Gitlab-EE feature ("sync_ssh_keys").

Group # / member #1: No matching user name found for group attribute " member"

hi,
I have some problems when I run
problem 1:
php bin/console ldap:sync -vvv
the console show that
Group # / member #1: No matching user name found for group attribute " member"

but the number of groups is true, and the number of user in groups also right, but I can not see any user's information, like name or NO.

problem 2:
when I run
php bin/console ldap:sync -vvv
in the end show that
[ error] Gitlab failure: 403 Forbidden

but there has already connected gitlab and can get gitlab user.

Users from the "userNamesToIgnore" list removed from their groups

Hello and thanks for the helpful tool.

I'm not sure, but it seems to be a bug. When I add a user to the userNamesToIgnore list, he is removed from all groups. A little debugging shows that ignored users are not skipped in the deployGitlabUsersAndGroups method.

Particularly ignored users are not skipped in this snippet.

$this->logger?->notice("Deleting extra group members...");
foreach ($userGroupMembersSync["found"] as $gitlabUserId => $gitlabUserName) {
if (isset($membersOfThisGroup[$gitlabUserId]) && $membersOfThisGroup[$gitlabUserId] == $gitlabUserName) {
continue;
}
$this->logger?->info(sprintf("Deleting user #%d \"%s\" from group #%d \"%s\" [%s].", $gitlabUserId, $gitlabUserName, $gitlabGroupId, $gitlabGroupName, $gitlabGroupPath));
$gitlabGroupMember = null;
/** @var GitlabGroupArray|null $gitlabUser */
!$this->dryRun ? ($gitlabGroup = $gitlab->groups()->removeMember($gitlabGroupId, $gitlabUserId)) : $this->logger?->warning("Operation skipped due to dry run.");
$userGroupMembersSync["extra"][$gitlabUserId] = $gitlabUserName;
$this->gitlabApiCoolDown();
}

LDAP failure: No such object. (Code 32)

Hi all

I am getting the following error when running the script in debug mode:
ldap_build_search_req ATTRS: sAMAccountName distinguishedName cn mail
[error] LDAP failure: No such object. (Code 32)

What does this mean? I have tried multiple options (with userMatchAttribute and userMatchAttribute) and its always the same error.
Username and bind dn is correct as I get a different error when I put in a wrong dn or password.
Active Directory is used as LDAP Server.

Thanks for help.
Grimbi

ldap:
debug: true
winCompatibilityMode: false

server:
    host:                            "DC IP"
    port:                             389
    version:                        3
    encryption:                  "none"

    bindDn:                        "CN=ldap,OU=Admins,DC=domain,DC=local"
    bindPassword:            "password"

queries:
    baseDn:                       "DC=domain,DC=local"

    userDn:                         "OU=Users,DC=domain,DC=local"
    userFilter:                     "(&(objectCategory=person)(objectClass=user))"
    userUniqueAttribute:            "sAMAccountName"
    userMatchAttribute:             "distinguishedName"
    userNameAttribute:              "cn"
    userEmailAttribute:             "mail"

    groupDn:                        "OU=Groups,DC=domain,DC=local"
    groupFilter:                    "(objectClass=group)"
    groupUniqueAttribute:           "cn"
    groupMemberAttribute:           "memberUid"

Error when running: LDAP failure: no such object.

When running the tool, it fails with the following relevant output:

con_gitlab_ldapsync | [notice] 184 directory user(s) recognised.
con_gitlab_ldapsync | ldap_build_search_req ATTRS: cn member
con_gitlab_ldapsync | [error] LDAP failure: No such object. (Code 32)

I suspect that the tool is not finding any groups matching the filter, but I am not sure this is the case.
I've set the following settings in the config file:

queries:
    ...
    userUniqueAttribute:            "sAMAccountName"
    userNameAttribute:              "cn"
    userEmailAttribute:             "mail"
    ...
    groupFilter:                    "(objectClass=group)"
    groupUniqueAttribute:           "distinguishedName"
    groupMemberAttribute:           "member"

In our Windows AD, group membership is based on the 'member' attribute, which is a comma separated list of all the unique DistinguishedNames of the members (the full path in the AD). Would the setting be correct in that case? Does the tool support that configuration?
Thanks for any lead on what to adjust!

Add Static Group Mapping

Hi,

Great tool, thanks for the effort here.

A great additional feature would be the ability to map an LDAP group name to a different group name in Gitlab.
We (like many others) have a particular security group "naming scheme" on our AD server that doesn't work well as groups on Gitlab.

Being able to Statically set these in the config.yaml file would be a great addition

i.e

An LDAP group called "Grp-GitLabProject1" where it maps to gitlab group "Project1"

groupNameMapping:

  • Grp-GitLabProject1 : Project1

Reenabled users stay blocked in GitLab

I'm using FreeIPA where you can disable a user (so it still exists). When that happens the user disappears from LDAP itself. In this case I've disabled the user rdkoning.
I then get this error:

[info] Group "externals" members are external.
[warning] Group #3 / member #1: User not found "gitlab01".
[warning] Group #3 / member #2: User not found "gitlab02".
[notice] 0 directory group "externals" member(s) recognised.
[notice] Disabling Gitlab users of which don't exist in directory...
[warning] Disabling Gitlab user #4 "rdkoning".
[notice] 1 Gitlab user(s) disabled.
[notice] Updating users of which were already in both Gitlab and the directory...
[info] Updating Gitlab user #4 "rdkoning".
[error] Gitlab user "rdkoning" has no LDAP details available. This should not happen!

What has happened inside GitLab is that this user is now blocked and marked as external. Blocked I get but don't really understand why it's now marked as external. Perhaps this is something GitLab does?
Please note that the user still exists in FreeIPA and is inside FreeIPA still a member of it's groups.

When I now renable the user again and sync it then everything looks alright. The user is placed inside of every group again, etc. But inside GitLab the user is now no longer an external user but is still blocked!

I'm expecting that when I reenable a user that it's no longer external and no longer blocked.

Here is the output of the sync after I've reenabled the user:

[notice] Loading configuration.
[notice] Loaded configuration.
[notice] Validating configuration.
[notice] Validated configuration.
[notice] Retrieving directory users and groups.
[notice] Establishing LDAP connection.
[notice] LDAP connection established.
[notice] 8 directory user(s) found.
[info] Found directory user "jsiegers" [uid=jsiegers,cn=users,cn=accounts,dc=postlab,dc=cloud].
[info] Found directory user "pmlombert" [uid=pmlombert,cn=users,cn=accounts,dc=postlab,dc=cloud].
[info] Found directory user "rdkoning" [uid=rdkoning,cn=users,cn=accounts,dc=postlab,dc=cloud].
[info] Found directory user "tvdmaat" [uid=tvdmaat,cn=users,cn=accounts,dc=postlab,dc=cloud].
[info] User "gitlab01" in ignore list.
[info] User "gitlab02" in ignore list.
[info] User "gitlab-ldap-bind-user" in ignore list.
[notice] 4 directory user(s) recognised.
[notice] 6 directory group(s) found.
[info] Found directory group "gitlab-admins".
[info] Group "gitlab-admins" members are administrators.
[warning] Group #1 / member #1: User not found "gitlab01".
[notice] 0 directory group "gitlab-admins" member(s) recognised.
[info] Found directory group "hedge".
[info] Found directory group "hedge" member "pmlombert".
[info] Found directory group "hedge" member "rdkoning".
[info] Found directory group "hedge" member "tvdmaat".
[info] Found directory group "hedge" member "jsiegers".
[warning] Group #2 / member #5: User not found "gitlab01".
[notice] 4 directory group "hedge" member(s) recognised.
[info] Found directory group "externals".
[info] Group "externals" members are external.
[warning] Group #3 / member #1: User not found "gitlab01".
[warning] Group #3 / member #2: User not found "gitlab02".
[notice] 0 directory group "externals" member(s) recognised.
[info] Group "postlab-cloud" in ignore list.
[info] Found directory group "test-team-3".
[warning] Group #5 / member #1: User not found "gitlab01".
[info] Found directory group "test-team-3" member "jsiegers".
[info] Found directory group "test-team-3" member "rdkoning".
[notice] 2 directory group "test-team-3" member(s) recognised.
[notice] 4 directory group(s) recognised.
[notice] LDAP connection closed.
[notice] Retrieved directory users and groups.
[notice] Deploying users and groups to Gitlab instances.
[notice] Establishing Gitlab connection.
[notice] Finding all existing Gitlab users...
[info] Found Gitlab user #5 "tvdmaat".
[info] Found Gitlab user #4 "rdkoning".
[info] Found Gitlab user #3 "pmlombert".
[info] Found Gitlab user #2 "jsiegers".
[info] Gitlab built-in root user will be ignored.
[notice] 4 Gitlab user(s) found.
[notice] Creating directory users of which don't exist in Gitlab...
[notice] 0 Gitlab user(s) created.
[notice] Disabling Gitlab users of which don't exist in directory...
[notice] 0 Gitlab user(s) disabled.
[notice] Updating users of which were already in both Gitlab and the directory...
[info] Updating Gitlab user #2 "jsiegers".
[info] Updating Gitlab user #3 "pmlombert".
[info] Updating Gitlab user #4 "rdkoning".
[info] Updating Gitlab user #5 "tvdmaat".
[notice] 4 Gitlab user(s) updated.
[notice] Finding all existing Gitlab groups...
[info] Found Gitlab group #11 "hedge" [hedge].
[info] Found Gitlab group #9 "test team 3" [test-team-3].
[notice] 2 Gitlab group(s) found.
[notice] Creating directory groups of which don't exist in Gitlab...
[warning] Not creating Gitlab group "externals" [externals]: No members in directory group, or config gitlab->options->createEmptyGroups is disabled.
[warning] Not creating Gitlab group "gitlab admins" [gitlab-admins]: No members in directory group, or config gitlab->options->createEmptyGroups is disabled.
[notice] 0 Gitlab group(s) created.
[notice] Deleting Gitlab groups of which don't exist in directory...
[notice] 0 Gitlab group(s) deleted.
[notice] Updating groups of which were already in both Gitlab and the directory...
[info] Updating Gitlab group #11 "hedge" [hedge].
[info] Updating Gitlab group #9 "test team 3" [test-team-3].
[notice] 2 Gitlab group(s) updated.
[notice] Synchronising Gitlab group members with directory group members...
[notice] Synchronising 4 member(s) for group #11 "hedge" [hedge]...
[notice] Finding existing group members...
[info] Gitlab built-in root user will be ignored.
[info] Found Gitlab group member #2 "jsiegers".
[info] Found Gitlab group member #3 "pmlombert".
[info] Found Gitlab group member #5 "tvdmaat".
[notice] 3 Gitlab group "hedge" [hedge] member(s) found.
[notice] Adding missing group members...
[info] Adding user #4 "rdkoning" to group #11 "hedge" [hedge].
[notice] 1 Gitlab group "hedge" [hedge] member(s) added.
[notice] Deleting extra group members...
[notice] 0 Gitlab group "hedge" [hedge] member(s) deleted.
[notice] Synchronising 2 member(s) for group #9 "test team 3" [test-team-3]...
[notice] Finding existing group members...
[info] Gitlab built-in root user will be ignored.
[info] Found Gitlab group member #2 "jsiegers".
[notice] 1 Gitlab group "test team 3" [test-team-3] member(s) found.
[notice] Adding missing group members...
[info] Adding user #4 "rdkoning" to group #9 "test team 3" [test-team-3].
[notice] 1 Gitlab group "test team 3" [test-team-3] member(s) added.
[notice] Deleting extra group members...
[notice] 0 Gitlab group "test team 3" [test-team-3] member(s) deleted.
[notice] Gitlab connection closed.
[notice] Deployed users and groups to Gitlab instances.

Updating a user's email address fails due to "public_email" is not an email you own

If a user whom has already been synchronised changes their email address in the directory resynchronising them will fail with Gitlab throwing error message:

"public_email" is not an email you own

This occurs right here on line 1190 of "LdapSyncCommand.php":

!$this->dryRun ? ($gitlabUser = $gitlab->api("users")->update($gitlabUserId, [
    // "username"          => $gitlabUserName,
    // No point updating that. ^
    // If the UID changes so will that bit of the DN anyway, so this can't be detected with a custom attribute containing the Gitlab user ID written back to user's LDAP object.
    "reset_password"    => false,
    "name"              => $ldapUserDetails["fullName"],
    "extern_uid"        => $ldapUserDetails["dn"],
    "provider"          => $gitlabConfig["ldapServerName"],
    "public_email"      => $ldapUserDetails["email"],
    "admin"             => $ldapUserDetails["isAdmin"],
    "can_create_group"  => $ldapUserDetails["isAdmin"],
    "skip_confirmation" => true,
    "external"          => $ldapUserDetails["isExternal"],
])) : $this->logger->warning("Operation skipped due to dry run.");

$usersSync["update"][$gitlabUserId] = $gitlabUserName;

Specifically "public_email" => $ldapUserDetails["email"],.

The problem is that you can't set a user's public email address to an email address that isn't already registered to their account on your Gitlab instance. This could be corrected by adjusting the Gitlab user's primary email address by changing "public_email" to "email", though this comes with drawbacks:

  1. You cannot adjust "email" and "public_email" in one call to avoid this error. Either two update() calls are required, or a get() call must be performed first with a check to determine if a 2nd update() is necessary. Not doing this just means that the user's publicly visible email address would not be updated.
  2. If the new email address for this Gitlab user has already been used by another Gitlab user the update() will fail due to a unique constraint violation. Recursive action would be necessary to resolve conflicts in advance. (This problem can also apply to users newly synchronising into Gitlab.)

--

Just taking note of this issue so I don't forget it, and I'm open to hearing if anyone else has had this issue already and what you think would be best to resolve it.

Disable deleting extra group members

Everything synced up perfectly at first. Users and groups are created as expected.
After that, I went ahead and set up some new groups on GitLab myself and popped some users into them.
But, when I ran the LDAP sync script another time, it went and kicked the users out of the groups I created manually
Is it possible to stop this from happening? I only want to sync the groups that the script sets up.

Question: user names and dots

I noticed while trying a recent version that the script says that usernames with dots are not allowed and therefor are being replaced with a comma.

In our current setup we have a lot of users with a dot in the name all working fine.
The log says the Gitlab doesn't allow this. Is this since version 12? Or did I miss something here?

Special characters in password halts user creation

While creating various hundreds of users, I got HTTP 500 responses, so I had to run the tool multiple times. All the failing users had a random password with a strange special character. Removing special characters from the password generator fixes the issue.
A longer password could be used to improve security.

Crash when encountering subgroups

I've got a weird crash. I've got some groups that a user makes. One of them is called 'A Folder Renamed'.

This is what I get in the log:

LDAP users and groups sync script for Gitlab-CE

[notice] Loading configuration.
[notice] Loaded configuration.
[notice] Validating configuration.
[notice] Validated configuration.
[notice] Retrieving directory users and groups.
[notice] Establishing LDAP connection.
[notice] LDAP connection established.
[notice] 6 directory user(s) found.
[info] User "gitlab-ldap-bind-user" in ignore list.
[info] Found directory user "postlab-cloud" [uid=postlab-cloud,cn=users,cn=accounts,dc=fipa01,dc=postlab,dc=cloud].
[info] User "gl-ams-01" in ignore list.
[info] Found directory user "jsiegers" [uid=jsiegers,cn=users,cn=accounts,dc=fipa01,dc=postlab,dc=cloud].
[info] Found directory user "testuser" [uid=testuser,cn=users,cn=accounts,dc=fipa01,dc=postlab,dc=cloud].
[notice] 3 directory user(s) recognised.
[notice] 6 directory group(s) found.
[info] Found directory group "test-team".
[info] Group #1 / member #1: User "gl-ams-01" in ignore list.
[info] Found directory group "test-team" member "jsiegers".
[info] Found directory group "test-team" member "postlab-cloud".
[info] Found directory group "test-team" member "testuser".
[notice] 3 directory group "test-team" member(s) recognised.
[info] Found directory group "test-group".
[info] Found directory group "test-group" member "jsiegers".
[info] Group #2 / member #2: User "gl-ams-01" in ignore list.
[info] Found directory group "test-group" member "postlab-cloud".
[info] Found directory group "test-group" member "testuser".
[notice] 3 directory group "test-group" member(s) recognised.
[info] Group "postlab-cloud" in ignore list.
[info] Found directory group "gitlab-admins".
[info] Group "gitlab-admins" members are administrators.
[info] Found directory group "gitlab-admins" member "postlab-cloud".
[info] Group #4 / member #1: User "postlab-cloud" is an administrator.
[info] Group #4 / member #2: User "gl-ams-01" in ignore list.
[notice] 1 directory group "gitlab-admins" member(s) recognised.
[info] Found directory group "gitlab-externals".
[info] Group "gitlab-externals" members are external.
[info] Group #5 / member #1: User "gl-ams-01" in ignore list.
[notice] 0 directory group "gitlab-externals" member(s) recognised.
[notice] 4 directory group(s) recognised.
[notice] LDAP connection closed.
[notice] Retrieved directory users and groups.
[notice] Deploying users and groups to Gitlab instances.
[notice] Establishing Gitlab connection.
[notice] Finding all existing Gitlab users...
[info] Found Gitlab user #4 "testuser".
[info] Found Gitlab user #3 "jsiegers".
[info] Found Gitlab user #2 "postlab-cloud".
[info] Gitlab built-in root user will be ignored.
[notice] 3 Gitlab user(s) found.
[notice] Creating directory users of which don't exist in Gitlab...
[notice] 0 Gitlab user(s) created.
[notice] Disabling Gitlab users of which don't exist in directory...
[notice] 0 Gitlab user(s) disabled.
[notice] Updating users of which were already in both Gitlab and the directory...
[info] Updating Gitlab user #3 "jsiegers".
[info] Updating Gitlab user #2 "postlab-cloud".
[info] Updating Gitlab user #4 "testuser".
[notice] 3 Gitlab user(s) updated.
[notice] Finding all existing Gitlab groups...
[info] Found Gitlab group #8 "A Folder Renamed" [a-folder-renamed].
[info] Found Gitlab group #9 "A Sub Folder" [a-sub-folder].
[info] Found Gitlab group #3 "gitlab admins" [gitlab-admins].
[info] Found Gitlab group #11 "Series" [series].
[info] Found Gitlab group #5 "test group" [test-group].
[info] Found Gitlab group #7 "test team" [test-team].
[info] Found Gitlab group #10 "The Second Sub Folder" [the-second-sub-folder].
[notice] 7 Gitlab group(s) found.
[notice] Creating directory groups of which don't exist in Gitlab...
[warning] Not creating Gitlab group "gitlab externals" [gitlab-externals]: No members in directory group, or config gitlab->options->createEmptyGroups is disabled.
[notice] 0 Gitlab group(s) created.
[notice] Deleting Gitlab groups of which don't exist in directory...
[info] Not deleting Gitlab group #8 "A Folder Renamed" [a-folder-renamed]: Has members in directory group, or config gitlab->options->deleteExtraGroups is disabled.
[info] Not deleting Gitlab group #9 "A Sub Folder" [a-sub-folder]: Has members in directory group, or config gitlab->options->deleteExtraGroups is disabled.
[info] Not deleting Gitlab group #11 "Series" [series]: Has members in directory group, or config gitlab->options->deleteExtraGroups is disabled.
[info] Not deleting Gitlab group #10 "The Second Sub Folder" [the-second-sub-folder]: Has members in directory group, or config gitlab->options->deleteExtraGroups is disabled.
[notice] 0 Gitlab group(s) deleted.
[notice] Updating groups of which were already in both Gitlab and the directory...
[info] Updating Gitlab group #8 "A Folder Renamed" [a-folder-renamed].
[info] Gitlab group "A Folder Renamed" has no LDAP details available.
[info] Updating Gitlab group #9 "A Sub Folder" [a-sub-folder].
[info] Gitlab group "A Sub Folder" has no LDAP details available.
[info] Updating Gitlab group #11 "Series" [series].
[info] Gitlab group "Series" has no LDAP details available.
[info] Updating Gitlab group #10 "The Second Sub Folder" [the-second-sub-folder].
[info] Gitlab group "The Second Sub Folder" has no LDAP details available.
[info] Updating Gitlab group #3 "gitlab admins" [gitlab-admins].
[info] Updating Gitlab group #5 "test group" [test-group].
[info] Updating Gitlab group #7 "test team" [test-team].
[notice] 3 Gitlab group(s) updated.
[notice] Synchronising Gitlab group members with directory group members...
PHP Notice:  Undefined index: A Folder Renamed in /usr/local/scripts/gitlab-ce-ldap-sync/src/LdapSyncCommand.php on line 1362
PHP Fatal error:  Uncaught TypeError: Argument 2 passed to AdamReece\GitlabCeLdapSync\LdapSyncCommand::in_array_i() must be of the type array, null given, called in /usr/local/scripts/gitlab-ce-ldap-sync/src/LdapSyncCommand.php on line 1362 and defined in /usr/local/scripts/gitlab-ce-ldap-sync/src/LdapSyncCommand.php:1513
Stack trace:
#0 /usr/local/scripts/gitlab-ce-ldap-sync/src/LdapSyncCommand.php(1362): AdamReece\GitlabCeLdapSync\LdapSyncCommand->in_array_i('jsiegers', NULL)
#1 /usr/local/scripts/gitlab-ce-ldap-sync/src/LdapSyncCommand.php(144): AdamReece\GitlabCeLdapSync\LdapSyncCommand->deployGitlabUsersAndGroups(Array, 'gitlab', Array, Array, 3, Array, 4)
#2 /usr/local/scripts/gitlab-ce-ldap-sync/vendor/symfony/console/Command/Command.php(255): AdamReece\GitlabCeLdapSync\LdapSyncCommand->execute(Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
#3 /usr/local/scripts/gitlab-ce-ldap-sync/vendor/symfony/console/Application.php(946): Symfony\Component\Console\Command\C in /usr/local/scripts/gitlab-ce-ldap-sync/src/LdapSyncCommand.php on line 1513

Gitlab LDAP sync script tries to remove "bot" users from groups.

The Gitlab LDAP sync script tries to currently remove "bot users" that are created when adding an access_token from non-ldap groups. Maybe even from groups that are also present in LDAP.

https://gitlab.example.com/groups/<GROUPNAME>/-/settings/access_tokens

LDAP blocked users cannot be modified by the API

If a user in Gitlab is blocked because the LDAP back end is imposing that then this tool will fail with error:

[error] Gitlab failure: 403 Forbidden - LDAP blocked users cannot be modified by the API

This tool should skip these users with a warning.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.