Merge pull request #23358 from praphull-purohit/ldap-tls

Add TLS options to support GSuite org data ingestion via LDAP
This commit is contained in:
Ben Lambert
2024-03-19 13:35:58 +01:00
committed by GitHub
9 changed files with 84 additions and 11 deletions
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/plugin-catalog-backend-module-ldap': patch
---
Add TLS support to ingest GSuite LDAP data
+37
View File
@@ -110,6 +110,18 @@ This is the URL of the targeted server, typically on the form
`ldaps://ds.example.net` for SSL enabled servers or `ldap://ds.example.net`
without SSL.
#### target.tls.keys
`keys` in TLS options specifies location of a file, that contains private keys
to establish connection with your LDAP server, in PEM format. See an example
for Google Secure LDAP Service below.
#### target.tls.certs
`certs` in TLS options specifies location of a file, that contains certificate
chains to establish connection with your LDAP server, in PEM format. See an
example for Google Secure LDAP Service below.
### bind
The bind block specifies how the plugin should bind (essentially, to
@@ -374,3 +386,28 @@ catalog:
rules:
- allow: [User, Group]
```
### Example configurations
#### Google Secure LDAP Service
To sync Google Workspace/Cloud Identity organization data to users and groups in backstage,
you must [configure Secure LDAP Service](https://support.google.com/a/answer/9048516) first.
Once Secure LDAP Service is configured, you can enable TLS options in LDAP configuration,
as mentioned below. `keys` and `certs` specify the location of files that are generated
while configuring Secure LDAP Service above.
```yaml
ldap:
providers:
- target: ldaps://ldap.google.com:636
tls:
rejectUnauthorized: false
keys: '/var/secrets/tls/gldap.key'
certs: '/var/secrets/tls/gldap.crt'
users:
# users configuration comes here
groups:
# groups configuration comes here
```
@@ -197,6 +197,8 @@ export function readLdapOrg(
// @public
export type TLSConfig = {
rejectUnauthorized?: boolean;
keys?: string;
certs?: string;
};
// @public
@@ -39,8 +39,8 @@
"@backstage/plugin-catalog-common": "workspace:^",
"@backstage/plugin-catalog-node": "workspace:^",
"@backstage/types": "workspace:^",
"@types/ldapjs": "^2.2.0",
"ldapjs": "^2.2.0",
"@types/ldapjs": "^2.2.5",
"ldapjs": "^2.3.3",
"lodash": "^4.17.21",
"uuid": "^9.0.0",
"winston": "^3.2.1"
@@ -15,16 +15,18 @@
*/
import { ForwardedError, stringifyError } from '@backstage/errors';
import { readFile } from 'fs/promises';
import ldap, { Client, SearchEntry, SearchOptions } from 'ldapjs';
import { cloneDeep } from 'lodash';
import tlsLib from 'tls';
import { Logger } from 'winston';
import { BindConfig, TLSConfig } from './config';
import { createOptions, errorString } from './util';
import {
AEDirVendor,
ActiveDirectoryVendor,
DefaultLdapVendor,
FreeIpaVendor,
AEDirVendor,
LdapVendor,
} from './vendors';
@@ -44,9 +46,22 @@ export class LdapClient {
bind?: BindConfig,
tls?: TLSConfig,
): Promise<LdapClient> {
let secureContext;
if (tls && tls.certs && tls.keys) {
const cert = await readFile(tls.certs, 'utf-8');
const key = await readFile(tls.keys, 'utf-8');
secureContext = tlsLib.createSecureContext({
cert: cert,
key: key,
});
}
const client = ldap.createClient({
url: target,
tlsOptions: tls,
tlsOptions: {
secureContext,
rejectUnauthorized: tls?.rejectUnauthorized,
},
});
// We want to have a catch-all error handler at the top, since the default
@@ -80,7 +80,11 @@ describe('readLdapConfig', () => {
{
target: 'target',
bind: { dn: 'bdn', secret: 's' },
tls: { rejectUnauthorized: false },
tls: {
rejectUnauthorized: false,
keys: '/tmp/keys.pem',
certs: '/tmp/certs.pem',
},
users: {
dn: 'udn',
options: {
@@ -140,7 +144,11 @@ describe('readLdapConfig', () => {
{
target: 'target',
bind: { dn: 'bdn', secret: 's' },
tls: { rejectUnauthorized: false },
tls: {
rejectUnauthorized: false,
keys: '/tmp/keys.pem',
certs: '/tmp/certs.pem',
},
users: {
dn: 'udn',
options: {
@@ -49,6 +49,10 @@ export type LdapProviderConfig = {
export type TLSConfig = {
// Node TLS rejectUnauthorized
rejectUnauthorized?: boolean;
// A file containing private keys in PEM format
keys?: string;
// A file containing cert chains in PEM format
certs?: string;
};
/**
@@ -205,6 +209,8 @@ export function readLdapConfig(config: Config): LdapProviderConfig[] {
}
return {
rejectUnauthorized: c.getOptionalBoolean('rejectUnauthorized'),
keys: c.getOptionalString('keys'),
certs: c.getOptionalString('certs'),
};
}
@@ -105,7 +105,7 @@ function decode(
function formatGUID(objectGUID: string | Buffer): string {
let data: Buffer;
if (typeof objectGUID === 'string') {
data = new Buffer(objectGUID, 'binary');
data = Buffer.from(objectGUID, 'binary');
} else {
data = objectGUID;
}
+4 -4
View File
@@ -5691,9 +5691,9 @@ __metadata:
"@backstage/plugin-catalog-common": "workspace:^"
"@backstage/plugin-catalog-node": "workspace:^"
"@backstage/types": "workspace:^"
"@types/ldapjs": ^2.2.0
"@types/ldapjs": ^2.2.5
"@types/lodash": ^4.14.151
ldapjs: ^2.2.0
ldapjs: ^2.3.3
lodash: ^4.17.21
uuid: ^9.0.0
winston: ^3.2.1
@@ -18988,7 +18988,7 @@ __metadata:
languageName: node
linkType: hard
"@types/ldapjs@npm:^2.2.0":
"@types/ldapjs@npm:^2.2.5":
version: 2.2.5
resolution: "@types/ldapjs@npm:2.2.5"
dependencies:
@@ -33246,7 +33246,7 @@ __metadata:
languageName: node
linkType: hard
"ldapjs@npm:^2.2.0":
"ldapjs@npm:^2.3.3":
version: 2.3.3
resolution: "ldapjs@npm:2.3.3"
dependencies: