This utility is intended to enable synchronization between GitHub and various LDAP and SAML providers. This is particularly useful for large organizations with many teams that either use GitHub Enterprise Cloud, do not use LDAP for authentication, or use a SAML provider other than what is natively supported. It supports both GitHub.com, GitHub Enterprise Server (GHES) and GitHub, but it will need to live in a location that can access your LDAP servers.
This utility provides the following functionality:
Feature | Supported | Description |
---|---|---|
Sync Users | Yes | Add or remove users from Teams in GitHub to keep in sync with Active Directory groups |
Dynamic Config | Yes | Utilize a settings file to derive Active Directory and GitHub settings |
LDAP SSL | Yes | SSL or TLS connections. |
Failure notifications | Yes | Presently supports opening a GitHub issue when sync failed. The repo is configurable. |
Sync on new team | Yes | Synchronize users when a new team is created |
Sync on team edit | No | This event is not processed currently |
Custom team/group maps | Yes | The team slug and group name will be matched automatically, unless you define a custom mapping with syncmap.yml |
Force custom map | Yes | Sync only team defined in syncmap.yml |
Dry run / Test mode | Yes | Run and print the differences, but make no changes |
Nested teams/groups | No | Synchronize groups within groups. Presently, if a group is a member of another group, it is skipped |
settings
page on the organization that you want to own the GitHub App, and navigate to the GitHub Apps
section.
https://<MY_GITHUB_HOSTNAME>/organizations/<MY_ORG_NAME>/settings/apps
http://ip.of.machine:3000
)http://ip.of.machine:3000
)WEBHOOK_SECRET
events
and permissions
listed below will be required. For more information on how to create a GitHub App, please visit https://developer.github.com/apps/building-github-apps/creating-a-github-appCreate GitHub App
button at the bottom of the page to continueAPP ID
on your newly-created GitHub App. You will need to set this as an environment variable when configuring the app.PRIVATE_KEY_PATH
Install App
All Repositories
or the desired repositories you wish to watchCategory | Attribute | Permission |
---|---|---|
Repository permissions | Issues |
Read & write |
Repository permissions | Metadata |
Read-only |
Organization permissions | Members |
Read & write |
User permissions | Email addresses |
Read-only |
Event | Required? | Description |
---|---|---|
Team |
Optional | Trigger when a new team is created , deleted , edited , renamed , etc. |
Authentication methods
This app requires the following Azure permissions:
GroupMember.Read.All
User.Read.All
If you have ADMIN_FINE_GRAINED_AUTHZ
enabled, you only need the following permission for the user realm:
view-users
To get started, ensure that you are using Python 3.9 (or update your Pipfile
to the version you're running, 3.4+). The following additional libraries are required:
Install the required libraries.
pipenv install
Once you have all of the requirements installed, be sure to edit the .env
to match your environment.
.env
for GitHub App settings## GitHub App settings
WEBHOOK_SECRET=development
APP_ID=12345
PRIVATE_KEY_PATH=.ssh/team-sync.pem
GHE_HOST=github.example.com
.env
for choosing your backend## AzureAD = AAD
## AD/LDAP = LDAP
## Okta = OKTA
## OneLogin = ONELOGIN
USER_DIRECTORY=LDAP
## Sync users on username or email attribute
USER_SYNC_ATTRIBUTE=username
.env
for Active DirectoryLDAP_SERVER_HOST=dc1.example.com
LDAP_SERVER_PORT=389
LDAP_BASE_DN="DC=example,DC=com"
LDAP_USER_BASE_DN="CN=Users,DC=example,DC=example"
LDAP_GROUP_BASE_DN="OU=Groups,DC=example,DC=example"
LDAP_USER_FILTER="(objectClass=person)"
LDAP_USER_ATTRIBUTE=sAMAccountName
LDAP_USER_MAIL_ATTRIBUTE=mail
LDAP_GROUP_FILTER="(&(objectClass=group)(cn={group_name}))"
LDAP_GROUP_MEMBER_ATTRIBUTE=member
LDAP_BIND_USER="bind-user@example.com"
LDAP_BIND_PASSWORD="p4$$w0rd"
LDAP_SEARCH_PAGE_SIZE=1000
.env
for OpenLDAPLDAP_SERVER_HOST=dc1.example.com
LDAP_SERVER_PORT=389
LDAP_BASE_DN="dc=example,dc=com"
LDAP_USER_BASE_DN="ou=People,dc=example,dc=com"
LDAP_GROUP_BASE_DN="ou=Groups,dc=example,dc=com"
LDAP_USER_FILTER="(&(objectClass=person)({ldap_user_attribute}={username}))"
LDAP_USER_ATTRIBUTE=uid
LDAP_USER_MAIL_ATTRIBUTE=mail
LDAP_GROUP_FILTER="(&(objectClass=posixGroup)(cn={group_name}))"
LDAP_GROUP_MEMBER_ATTRIBUTE=memberUid
LDAP_BIND_USER="cn=admin,dc=example,dc=com"
LDAP_BIND_PASSWORD="p4$$w0rd"
LDAP_SEARCH_PAGE_SIZE=1000
.env
for AzureADAZURE_TENANT_ID="<tenant_id>"
AZURE_CLIENT_ID="<client_id>"
AZURE_CLIENT_SECRET="<client_secret>"
AZURE_APP_SCOPE="default"
AZURE_API_ENDPOINT="https://graph.microsoft.com/v1.0"
# can also be an extensionAttribute
AZURE_USERNAME_ATTRIBUTE=userPrincipalName
AZURE_USER_IS_UPN=true
# use transitive members of a group instead of direct members
AZURE_USE_TRANSITIVE_GROUP_MEMBERS=false
.env
for OktaOKTA_ORG_URL=https://example.okta.com
OKTA_USERNAME_ATTRIBUTE=github_username
# token login
OKTA_ACCESS_TOKEN=asdfghkjliptojkjsj00294759
# OAuth login
OKTA_AUTH_METHOD=oauth
OKTA_CLIENT_ID=abcdefghijkl
OKTA_SCOPES='okta.users.read okta.groups.read'
OKTA_PRIVATE_KEY='{"kty": "RSA", ...}'
.env
for KeycloakKEYCLOAK_USERNAME=api-account
KEYCLOAK_PASSWORD=ExamplePassword
KEYCLOAK_REALM=ExampleCorp
KEYCLOAK_ADMIN_REALM=master
KEYCLOAK_USE_GITHUB_IDP=true
.env
for OneLoginONELOGIN_CLIENT_ID='asdafsflkjlk13q33433445wee'
ONELOGIN_CLIENT_SECRET='ca3a86f982fjjkjjkfkhls'
REGION=US
.env
settings for additional settings## Additional settings
CHANGE_THRESHOLD=25
OPEN_ISSUE_ON_FAILURE=true
REPO_FOR_ISSUES=github-demo/demo-repo
ISSUE_ASSIGNEE=githubber
SYNC_SCHEDULE=0 * * * *
TEST_MODE=false
SYNCMAP_ONLY=false
EMU_SHORTCODE=volcano
### Automatically add users missing from the organization
ADD_MEMBER=false
## Automatically remove users from the organization that are not part of a team
REMOVE_ORG_MEMBERS_WITHOUT_TEAM=false
.env
setting for flask app####################
## Flask Settings ##
####################
## Default: app, comment out to run once as a script
FLASK_APP=app
## Default: production
FLASK_ENV=development
## Default: 5000
FLASK_RUN_PORT=5000
## Default: 127.0.0.1
FLASK_RUN_HOST=0.0.0.0
syncmap.yml
custom mapping file---
mapping:
- github: demo-team
directory: ldap super users
org: my github org
- github: demo-admin-2
directory: some other group
The custom map uses slugs that are lowercase. If you don't specify organization name, it will synchronize all teams with same name in any organization.
This example runs the app in a standard Flask environment.
pipenv run flask run --host=0.0.0.0 --port=5000
Or you can run the app with Python directly.
pipenv run python app.py
⚠️ This is free and open-source software that is supported by the open-source community, and is not included as part of GitHub's official platform support.
This project draws much from: