Skip to content

Commit 92a710e

Browse files
committed
LDAP Support - Part 1
1. ldap_client 2. support assume_role_with_web_identity Signed-off-by: jackyalbo <[email protected]>
1 parent ed5f39b commit 92a710e

File tree

13 files changed

+725
-80
lines changed

13 files changed

+725
-80
lines changed

config.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,11 @@ config.IAM_SERVICE_CERT_PATH = '/etc/iam-secret';
7777
config.MGMT_SERVICE_CERT_PATH = '/etc/mgmt-secret';
7878
config.EXTERNAL_DB_SERVICE_CERT_PATH = '/etc/external-db-secret';
7979

80+
/////////////////
81+
// LDAP CONFIG //
82+
/////////////////
83+
config.LDAP_CONFIG_PATH = '/etc/noobaa-server/ldap_config';
84+
8085
//////////////////
8186
// NODES CONFIG //
8287
//////////////////

docs/design/ldap.md

Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
2+
# User Database support (LDAP) - POC
3+
4+
5+
## Goal
6+
7+
We propose adding support for administrators who maintain an existing LDAP-compatible user directory, enabling them to reuse their current username and password credentials to obtain temporary access tokens for S3 operations.
8+
This feature will leverage the AWS STS operation AssumeRoleWithWebIdentity. In the proposed workflow:
9+
1. The client sends a signed(with a predfined constant signature) or unsigned JWT as the web identity token.
10+
2. The system parses the JWT to extract the LDAP username and password.
11+
3. These credentials are validated against a configured external LDAP server.
12+
4. Upon successful authentication, the system issues a temporary STS token, granting the user access to S3 resources for a limited duration.
13+
14+
This approach ensures secure integration with existing identity infrastructures while eliminating the need to store or manage separate S3 credentials for LDAP users.
15+
16+
## Configuring the external LDAP
17+
The administrator must store the LDAP configuration in the following file:
18+
/etc/noobaa-server/ldap_config
19+
20+
The configuration should include:
21+
22+
uri (Required) – The FQDN of the external LDAP server, in the format:
23+
ldaps://[server-ip-or-hostname]:[port] (e.g., port 636 for LDAPS)
24+
25+
admin (Required) – An administrator username with permission to execute search queries on the LDAP server.
26+
27+
secret (Required) – The password for the administrator account.
28+
29+
search_dn (Required) – The distinguished name (DN) under which search queries will be performed.
30+
31+
dn_attribute (Optional) – The DN attribute to be used in search queries (default: uid).
32+
33+
search_scope (Optional) – Determines how deep the LDAP search should go from the search_dn (default: sub):
34+
35+
* base – Search only the entry specified by search_dn.
36+
37+
* one – Search immediate children of search_dn, but not deeper levels.
38+
39+
* sub – Search the base DN and all its descendants recursively.
40+
41+
jwt_secret (Optional) - The JWT secret the administrator will use to sign the token sent in AssumeRoleWithWebIdentity requests (default: unsigned). Once this option is set - unsigned tokens or tokens signed with different secret will be dropped as access denied.
42+
43+
for example:
44+
```json
45+
{
46+
"uri": "ldap://ldap.example.com:636",
47+
"admin": "cn=admin,dc=example,dc=com",
48+
"secret": "SuperSecurePassword123",
49+
"search_dn": "ou=users,dc=example,dc=com",
50+
"dn_attribute": "uid",
51+
"search_scope": "sub",
52+
"jwt_secret": "IAMTHEADMIN123!(SHOULDBE256BITS)"
53+
}
54+
```
55+
56+
## Sending an AssumeRoleWithWebIdentity request
57+
First create the json to be decoded:
58+
```json
59+
{
60+
"user": "TheUserName",
61+
"password": "TheUserPassword",
62+
"type": "ldap"
63+
}
64+
```
65+
Sign it
66+
node.js example:
67+
```js
68+
const jwt = require('jsonwebtoken');
69+
console.log(jwt.sign({ user: "TheUserName", password: "TheUserPassword", type: "ldap" }, "IAMTHEADMIN123!(SHOULDBE256BITS)"));
70+
eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJ1c2VyIjoiVGhlVXNlck5hbWUiLCJwYXNzd29yZCI6IlRoZVVzZXJQYXNzd29yZCIsInR5cGUiOiJsZGFwIiwiaWF0IjoxNzU1MTgxOTE3fQ.
71+
```
72+
Or you can use 'none' algorithm if you are not interested with verifying the token
73+
```js
74+
const jwt = require('jsonwebtoken');
75+
console.log(jwt.sign({ user: "TheUserName", password: "TheUserPassword", type: "ldap" }, undefined, { algorithm: 'none' }));
76+
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjoiVGhlVXNlck5hbWUiLCJwYXNzd29yZCI6IlRoZVVzZXJQYXNzd29yZCIsInR5cGUiOiJsZGFwIiwiaWF0IjoxNzU1MTgyMzQ2fQ.P6WYcdM0kJagNK4D0M8AHiGFcUZ-DhTOKHlC1-AxcT0
77+
```
78+
Now use this token with AWS STS AssumeRoleWithWebIdentity:
79+
```bash
80+
aws sts assume-role-with-web-identity --endpoint [endpoint] --role-arn [role-arn] --role-session-name [session-name] --web-identity-token eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJ1c2VyIjoiVGhlVXNlck5hbWUiLCJwYXNzd29yZCI6IlRoZVVzZXJQYXNzd29yZCIsInR5cGUiOiJsZGFwIiwiaWF0IjoxNzU1MTgxOTE3fQ.
81+
```
82+
role-name - ARN of the role that the caller is assuming. Make sure you created this role in advance using account-api or noobaa-cli (See Appendix. A). Format is: `arn:aws:sts::[user-access-key]:role/[role-name]`
83+
84+
user-access-key - the access key of the account (temp secret key and token will be returned by the call)
85+
86+
session-name - An identifier for the assumed role session. Typically, you pass the name or identifier that is associated with the user who is using your application.
87+
88+
Output: The temporary security credentials, which include an access key ID, a secret access key, and a security token.
89+
90+
read more here: https://docs.aws.amazon.com/cli/latest/reference/sts/assume-role-with-web-identity.html
91+
92+
## Next steps
93+
94+
1. See if we can move to using C open-ldap client as part of our native code instead of ldapts (for better performance and fewer security issues). see here: https://www.openldap.org/software/repo.html (We will mainly need bind and search)
95+
2. Add a system test that will create an external ldap and will check the full authentication flow. You can base on dockers I used for testing:
96+
* AD image: `docker run --rm --privileged -p 636:636 quay.io/samba.org/samba-ad-server:latest` (https://github.com/samba-in-kubernetes/samba-container)
97+
* LDAP image: `docker run --rm --privileged -p 636:636 ghcr.io/ldapjs/docker-test-openldap/openldap:latest` (https://github.com/ldapjs/docker-test-openldap/pkgs/container/docker-test-openldap%2Fopenldap)
98+
3. Add support to the operator side:
99+
* CLI command for configuring external LDAP
100+
* Create K8s Secret for LDAP info and mount to /etc/noobaa-server/ldap_config to the relevant pods
101+
4. Better align and adapt to the IAM effort also in POC stage
102+
5. See if we want to support encrypted password as part of the JWT token. see here: https://auth0.com/docs/secure/tokens/access-tokens/json-web-encryption
103+
6. We should maybe move ldap authentication to the authentication scope if possible
104+
105+
## Appendix A: Creating account w/ role config using NooBaa API:
106+
```bash
107+
curl http://127.0.0.1:5001/rpc/ -sd '{
108+
"api": "account_api",
109+
"method": "create_account",
110+
"params": {
111+
"name": "ldap",
112+
"email": "ldap",
113+
"has_login": false,
114+
"s3_access": true,
115+
"role_config":
116+
{
117+
"role_name": "ldap_user",
118+
"assume_role_policy":
119+
{
120+
"statement": [
121+
{
122+
"effect": "allow",
123+
"action": ["sts:*"],
124+
"principal": ["*"]
125+
}]
126+
}
127+
}
128+
},
129+
"auth_token": "'$(cat .nbtoken)'"
130+
}'
131+
```
132+
in order to assume this role:
133+
```bash
134+
aws sts assume-role-with-web-identity --endpoint https://127.0.0.1:7443 --role-arn arn:aws:sts::pQII1cm5kFmpwqP6bzJh:role/ldap_user --role-session-name fry1 --web-identity-token eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJ1c2VyIjoiZnJ5IiwicGFzc3dvcmQiOiJmcnkiLCJ0eXBlIjoibGRhcCIsImlhdCI6MTc1NTQzMzkxOX0. --no-verify-ssl
135+
{
136+
"Credentials": {
137+
"AccessKeyId": "vTFcHUuPqjKdtzTsMULP",
138+
"SecretAccessKey": "qBQxFcx99JOlABkZNq2fNRj6qGe18F9IREcHf9DZ",
139+
"SessionToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhY2Nlc3Nfa2V5IjoidlRGY0hVdVBxaktkdHpUc01VTFAiLCJzZWNyZXRfa2V5IjoicUJReEZjeDk5Sk9sQUJrWk5xMmZOUmo2cUdlMThGOUlSRWNIZjlEWiIsImFzc3VtZWRfcm9sZV9hY2Nlc3Nfa2V5IjoicFFJSTFjbTVrRm1wd3FQNmJ6SmgiLCJpYXQiOjE3NTU0MzU3OTgsImV4cCI6MTc1NTQzOTM5OH0.z5Uap_7IPAbWCJyZC5zj8JleNshGxsYuhdbI9hOjr78",
140+
"Expiration": "2025-08-17T14:03:18+00:00"
141+
},
142+
"AssumedRoleUser": {
143+
"AssumedRoleId": "pQII1cm5kFmpwqP6bzJh:fry1",
144+
"Arn": "arn:aws:sts::pQII1cm5kFmpwqP6bzJh:assumed-role/ldap_user/fry1"
145+
},
146+
"SourceIdentity": "cn=Philip J. Fry,ou=people,dc=planetexpress,dc=com"
147+
}
148+
```
149+

package-lock.json

Lines changed: 121 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,7 @@
100100
"jsonwebtoken": "9.0.2",
101101
"linux-blockutils": "0.2.0",
102102
"lodash": "4.17.21",
103+
"ldapts": "7.3.1",
103104
"mime-types": "3.0.1",
104105
"minimist": "1.2.8",
105106
"moment": "2.30.1",

0 commit comments

Comments
 (0)