Skip to content

Commit 90dc197

Browse files
authored
Sync master into feature/trusted-certs (#6903)
2 parents a6e1046 + 420b584 commit 90dc197

62 files changed

Lines changed: 1456 additions & 1714 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/main.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ jobs:
3232
ocaml-version: "4.14.2"
3333
experimental: true
3434
- runs-on: "ubuntu-22.04"
35-
ocaml-version: "5.3.0"
35+
ocaml-version: "5.4.0"
3636
experimental: true
3737

3838
continue-on-error: ${{ matrix.experimental }}
Lines changed: 340 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,340 @@
1+
---
2+
title: Secure LDAP (LDAPS) Support for External Authentication
3+
layout: default
4+
design_doc: true
5+
revision: 1
6+
status: draft
7+
---
8+
9+
## Terminology
10+
11+
| Term | Meaning |
12+
|------|---------|
13+
| AD | Windows Active Directory |
14+
| samba/winbind | Client used in xapi to talk to AD |
15+
| DC | Windows AD domain controller |
16+
| ldap/ldaps | Lightweight Directory Access Protocol / over SSL |
17+
| Joining host | The host joining to a pool |
18+
19+
## 1. Background
20+
21+
To integrate XenServer with AD, XenServer performs LDAP queries in the following use cases:
22+
23+
- **Enable external authentication/Join domain**: Samba LDAP queries DC details
24+
- **Session revalidation**: xapi queries user details (e.g., whether user is still valid, password expired, etc.) to decide whether to destroy a session
25+
26+
Currently XenServer uses plain LDAP queries, which is a concern for some enterprise customers.
27+
28+
## 2. Xapi Database
29+
30+
### 2.1 External Auth Configuration
31+
32+
External auth details are stored in the `host` (table) → `external_auth_configuration` (field). For example:
33+
34+
```
35+
external_auth_configuration: {
36+
domain: xenrt16718.local,
37+
user: Administrator,
38+
workgroup: XENRTXENRT16718,
39+
netbios_name: genus-35-103d,
40+
machine_pwd_last_change_time: 1767508709
41+
}
42+
```
43+
44+
A new field `ldaps` (bool, optional) will be added to `external_auth_configuration` field to state whether LDAPS should be used instead of LDAP. If not set, LDAP will be used for backward compatibility.
45+
46+
So the field will look like:
47+
48+
```
49+
external_auth_configuration: {
50+
domain: xenrt16718.local,
51+
user: Administrator,
52+
workgroup: XENRTXENRT16718,
53+
netbios_name: genus-35-103d,
54+
machine_pwd_last_change_time: 1767508709,
55+
ldaps: true
56+
}
57+
```
58+
59+
### 2.2 Certificate
60+
61+
To enforce security, if customer uses self-signed certificate, they need to upload the root CA certificate to XenServer, so XenServer can verify the certificate/public key used talking to DC for LDAPS.
62+
63+
The [trusted-certificates.md](https://github.com/xapi-project/xen-api/blob/master/doc/content/design/trusted-certificates.md) design enhanced the `Certificate` table and introduced a new field `purpose` for security, which limits the certificate only for specific purpose. `ldaps` will be added to `purpose` field as a value for LDAPS.
64+
65+
## 3. Interfaces
66+
67+
### 3.1 pool.enable_external_auth
68+
69+
#### 3.1.1 Interface
70+
71+
To enable external auth, the current API arguments are as follows:
72+
73+
- `pool` (Ref _pool): The pool whose external authentication should be enabled
74+
- `config` (Map (String, String)): A list of key-values containing the configuration data
75+
- `service_name` (String): The name of the service
76+
- `auth_type` (String): The type of authentication (e.g., AD for Active Directory)
77+
78+
For example:
79+
80+
```bash
81+
xe pool-enable-external-auth uuid=<uuid> auth-type=AD service-name=<domain> config:user=<user> config:pass=<pwd>
82+
```
83+
84+
This API signature does not change. Regarding the config map, one new option is added:
85+
86+
- `config:ldaps`: whether LDAPS is required, default to `false`
87+
- Set `client ldap sasl wrapping` to `ldaps` if true, `seal` otherwise
88+
- This item will be stored in database in section 2.1
89+
90+
Given `ldaps` default to `false`, this feature is **NOT** enabled until explicitly set.
91+
92+
#### 3.1.2 Error code
93+
Following new error codes added to indicate ldaps enable related error
94+
- AUTH_NO_CERT, no certs can be used for ldaps, refer to 4.1.2 for certs finding.
95+
- AUTH_INVALID_CERT, found certs, but none of the certs can be used to connect to DC
96+
97+
### 3.2 Set/Get Pool LDAPS Status
98+
99+
#### 3.2.1 pool.external_auth_set_ldaps
100+
#### 3.2.1.1 Interface
101+
102+
User can specify LDAPS during join domain as in 3.1.
103+
104+
For the existing joined domain, user can switch between LDAP and LDAPS with this new API. Args as follows:
105+
106+
- `pool` (Ref _pool): pool to set LDAPS
107+
- `ldaps` (Bool): whether LDAPS is required
108+
- `force` (Bool): whether to set ldaps even when ldaps is currently set
109+
110+
This API will set the `ldaps` in database (Refer to 2.1).
111+
112+
This API performs following sanity check and rejects update if check fails:
113+
114+
- AD has already been enabled
115+
- ldaps has already been enabled without force
116+
- Find proper certificate (Refer to 4.1 for the details)
117+
- Do a `ldaps` query to embedded user `krbtgt` for the joined domain
118+
119+
**Note:**
120+
- This API allow re-entry with `force` to perform an extra `ldaps ping` for debug purpose
121+
- This API will not do the LDAPS query on the trusted domains, as xapi does not have trusted domain details
122+
- The joined domain likely has multiple DCs. LDAPS query tries every DC of the domain. Check pass if LDAPS query succeeds on any DC of the domain. This implies iterate and locate a DC supporting LDAPS (with proper certificate trust setup) before LDAPS query. However, this does not introduce performance problems as the LDAPS query happens in backend and refreshes result into XAPI DB
123+
- Pool coordinator dispatches this API request to every host, and only succeeds if all hosts pass the check
124+
- This API needs to be synced with other APIs. For example, `authenticate_username_password` should fail if this API is performing checking and configuration
125+
126+
This API will refresh `winbind` configuration (Refer to 4.1).
127+
128+
So following xe command can be used to switch between LDAP and LDAPS:
129+
130+
```bash
131+
xe pool-external-auth-set-ldaps uuid=<uuid> ldaps=<true|false>
132+
```
133+
134+
#### 3.2.1.2 Error code
135+
This API may raise following errors
136+
- AUTH_NO_CERT, no certs found to enable ldaps, refer to 4.1.2 for certs finding
137+
- AUTH_INVALID_CERT, found certs, but none of the certs can be used to connect to DC
138+
- AUTH_IS_DISABLED, AD is not enabled
139+
- AUTH_LDAPS_PING_FAILED, failed to do ldaps query on all DCs with valid certs
140+
141+
#### 3.2.2 Get Pool LDAPS Status
142+
143+
xapi generates a get message for each field automatically. To query the LDAPS status, client only needs to query the get method of `host` (class) → `external-auth-configuration` (field), and parse the result. The example as follows:
144+
145+
```bash
146+
xe host-param-get uuid=<uuid> param-name=external-auth-configuration
147+
```
148+
149+
### 3.3 Install Certificate
150+
151+
If the certificate for LDAPS in DC is signed by a private CA (vs a trusted public CA), user needs to import their Root or Intermediate CA certificate into XenServer.
152+
153+
`pool.install_trusted_certificate` can install the certificate with following parameters, refer to [trusted-certificates.md](https://github.com/xapi-project/xen-api/blob/master/doc/content/design/trusted-certificates.md) for the details:
154+
155+
- `session` (ref session_id): reference to a valid session
156+
- `self` (ref Pool): reference to the pool
157+
- `ca` (boolean): should always be `true` for `ldaps`. xapi should reject this CA otherwise
158+
- `cert` (string): the trusted certificate in PEM format
159+
- `purpose` (string list): the purposes of this cert. It can be one of following:
160+
- contain `ldaps` if for specific this specific purpose
161+
- empty set, thus would take as general purpose CA. It will be used for `ldaps` if no `ldaps` specific purpose found
162+
163+
**Note:** If the DCs (of joining domain and trusted domain in use) are signed by different CAs, all the CAs need to be uploaded to XenServer.
164+
165+
## 4. Configuration Item
166+
167+
To enforce LDAPS, following are required:
168+
169+
- Samba needs to be updated to 4.21+ (Already done)
170+
- LDAPS needs to be enabled in smb.conf
171+
172+
### 4.1 Samba Configuration
173+
174+
#### 4.1.1 smb.conf
175+
176+
To enforce LDAPS, xapi just passthrough the configuration to winbind. Following configuration needs to be updated to `/etc/samba/smb.conf`, details refer to [smb.conf](https://www.samba.org/samba/docs/current/man-html/smb.conf.5.html):
177+
178+
```ini
179+
client ldap sasl wrapping = <ldaps | seal>
180+
tls verify peer = ca_and_name_if_available
181+
tls trust system cas = yes
182+
tls cafile = /etc/trusted-certs/ca-bundle-[ldaps|general].pem
183+
```
184+
185+
- Switch between `ldap` and `ldaps` will flip `client ldap sasl wrapping` between `seal` and `ldaps`
186+
- `tls cafile` points to a CA bundle used to verify DC certs. Details refer to 4.1.2
187+
188+
#### 4.1.2 Certificate Selection
189+
190+
This design is following [trusted-certificates.md](https://github.com/xapi-project/xen-api/blob/master/doc/content/design/trusted-certificates.md):
191+
192+
- Use `/etc/trusted-certs/ca-bundle-ldaps.pem` if exists
193+
- Fall back to `/etc/trusted-certs/ca-bundle-general.pem` if exists and previous not match
194+
- Report error if none of above match
195+
196+
**Note:** The selection/configuration is only refreshed on following cases:
197+
198+
- xapi (re)start
199+
- `pool.external_auth_set_ldaps` API
200+
- (Re)join domain
201+
202+
### 4.2 Xapi Configuration
203+
204+
#### 4.2.1 winbind-tls-verify-peer
205+
206+
For security, xapi asks winbind to verify CA certificate. `ca_and_name_if_available` is the default.
207+
208+
However, user may want to disable this verification for debug purpose.
209+
210+
`winbind-tls-verify-peer` is introduced for xapi configuration, and the possible values are `no_check`, `ca_only`, `ca_and_name_if_available`, `ca_and_name` and `as_strict_as_possible`.
211+
The configured value will override `tls verify peer` value in xapi generated samba configuration. Refer to [smb.conf](https://www.samba.org/samba/docs/current/man-html/smb.conf.5.html) for the details.
212+
213+
214+
**Note:** This item is not intended for public documentation. This is only for debug purpose, or system tuning for specific scenarios from engineering/support team.
215+
216+
#### 4.2.2 ad-warning-message-interval
217+
218+
xapi sends warning message to user with this interval on LDAP query failure. Default to 1 week. Refer to section "Session revalidate" for the details.
219+
220+
## 5. Session Revalidate
221+
222+
xapi LDAP queries domain user status (if user has been added to manage XenServer) at configurable interval, and destroys the session created by domain user if user no longer in healthy status.
223+
224+
However, the LDAP query may fail due to various issues as follows:
225+
226+
- Temporary network issues
227+
- CA certificate is not properly configured, or expired, etc.
228+
229+
Instead of destroying user session for stability, a warning message will be sent to user with the details at configurable interval `ad-warning-message-interval`.
230+
231+
- If no LDAP error, do nothing
232+
- If error happens, send the warning message if:
233+
- first time see the error through xapi start up (so no need to persist last send time) or
234+
- `current_time - last_sent_time > winbind_warning_message_interval`
235+
236+
The message is defined as follows:
237+
- name: AD_DC_LDAP_CHECK
238+
- priority: Warning
239+
- cls: `Host
240+
- Body: LDAP(S) query check to `<DC>` of `<domain>` failed from `<host>` of `<pool>`
241+
242+
Note:
243+
- The backend session revalidate check only performs on pool coordinator, thus the backend LDAP(S) query check only on coordinator
244+
- `external_auth_set_ldaps` perform LDAP(S) query check on every host
245+
- All previous AD_DC_LDAP_CHECK warning of a host will be cleaned on a successful LDAP(s) query from that host
246+
247+
## 6. Pool Join/Leave
248+
249+
### 6.1 pool.join
250+
251+
#### 6.1.1 AD Pre-checks
252+
253+
Currently the pool.join pre-check checks the following:
254+
255+
- `external_auth_type`: whether joined AD
256+
- `external_auth_service_name`: whether joined the same domain
257+
258+
The pre-check is good enough, no matter whether `ldaps` is in use, as this ensures host can talk to AD. There are following cases:
259+
260+
- pool with AD, joining host with same AD: check pass as before this design
261+
- pool without AD, joining host without AD: check pass as before this design
262+
- pool without AD, joining host with AD: check failed as before this design
263+
- pool with AD, joining host without AD:
264+
- LDAPS not enabled: joining host needs to join to same AD as before this design
265+
- LDAPS enabled: joining host needs to enable AD without certificate check, details refer to 6.1.2
266+
267+
#### 6.1.2 Join Host to Pool with LDAPS Enabled
268+
269+
When joining a host without AD to a pool with LDAPS enabled, the host may not have the (CA) certificate for the domain. It can be trivial to enforce customer to upload the CA certificate to every joining host, thus client would help to orchestrate certificates.
270+
271+
The workflow:
272+
273+
~~~mermaid
274+
275+
sequenceDiagram
276+
participant user as User
277+
participant client as Client
278+
participant join as Joining host
279+
participant coor as Pool Coordinator
280+
participant dc as AD/DC
281+
282+
user->>client: pool.join
283+
Note over client: precheck
284+
alt precheck failed
285+
client-->>user: precheck failed
286+
end
287+
288+
Note over client,coor: sync all ldaps certs
289+
client->>coor: pool.download_trusted_certificate
290+
coor-->>client:
291+
client->>join: pool.install_trusted_certificate
292+
join-->>client:
293+
294+
user->>client: join domain username/password
295+
client->>join: join domain username/password
296+
join->>dc: join domain
297+
dc-->>join:
298+
join-->>client:
299+
300+
client->>join: pool.join
301+
Note over join,coor: join pool ops<br/>certs sync
302+
join-->>client:
303+
client-->>user: pool.join succeed
304+
305+
~~~
306+
307+
**Detailed Steps:**
308+
309+
1. Client find proper `ldaps certs` from pool coordinator as `certs_pool`
310+
- a. find all certs `ldaps in purpose`
311+
- b. if no LDAPS certs, find all `general` certs
312+
2. Client find all certs in joining host as `certs_joining_host`
313+
3. Client identify the certs needs to be synced to joining host as `certs_to_sync = certs_pool - certs_joining_host` (certs in `certs_pool`, but not in `certs_joining_host`), the certs fingerprint should be used to identify the certs
314+
4. Client download all `certs_to_sync`, `pool.download_trusted_certificate` from coordinator
315+
5. Client upload all certs to joining pool, `pool.install_trusted_certificate` to joining pool, with the same purpose
316+
6. Client trigger `pool.join` again with domain username and password
317+
7. After pool.join:
318+
- If pool.join failed, Client call `pool.uninstall_trusted_certificate` on joining host to revert the certs
319+
- If pool.join succeed, do nothing as pool.join would sync the certs anyway
320+
321+
### 6.2 pool.leave
322+
323+
`pool.disable_external_auth` is called during pool leave, thus the `ldaps` status is cleaned.
324+
325+
This design does not change it.
326+
327+
## 7. Telemetry Support
328+
329+
### 7.1 External Auth Enabled
330+
331+
`host` (table) → `external_auth_type` (field) = `AD`
332+
333+
### 7.2 LDAPS Enabled
334+
335+
`host` (table) → `external_auth_configuration` (field) → `ldaps` (key) = `true`
336+
337+
## References
338+
339+
- [trusted-certificates.md](https://github.com/xapi-project/xen-api/blob/master/doc/content/design/trusted-certificates.md)
340+
- [Samba smb.conf manual](https://www.samba.org/samba/docs/current/man-html/smb.conf.5.html)

dune

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,13 @@
33
(ocamlopt_flags (:standard -g -p -w -39))
44
(flags (:standard -w -39))
55
)
6-
(dev (flags (:standard -g -w -39)))
6+
(dev
7+
(flags (:standard -g -w -39))
8+
(env-vars (LANG C)))
79
(release
810
(flags (:standard -w -39-6@5))
9-
(env-vars (ALCOTEST_COMPACT 1))
11+
(env-vars (ALCOTEST_COMPACT 1)
12+
(LANG C))
1013
)
1114
)
1215

ocaml/idl/datamodel_errors.ml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1123,6 +1123,11 @@ let _ =
11231123
"The source SR does not have sufficient temporary space available to \
11241124
proceed the operation."
11251125
() ;
1126+
error Api_errors.sr_suspend_space_insufficient ["sr"]
1127+
~doc:
1128+
"The suspend SR does not have sufficient free space to store the VM \
1129+
suspend image."
1130+
() ;
11261131
error Api_errors.pbd_exists ["sr"; "host"; "pbd"]
11271132
~doc:"A PBD already exists connecting the SR to the server." () ;
11281133
error Api_errors.sr_has_pbd ["sr"]

ocaml/idl/datamodel_host.ml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2033,6 +2033,13 @@ let disable_external_auth =
20332033
; param_release= george_release
20342034
; param_default= Some (VMap [])
20352035
}
2036+
; {
2037+
param_type= Bool
2038+
; param_name= "force"
2039+
; param_doc= "Disable external auth even when not enabled"
2040+
; param_release= numbered_release "26.2.0-next"
2041+
; param_default= Some (VBool false)
2042+
}
20362043
]
20372044
~doc:"This call disables external authentication on the local host"
20382045
~allowed_roles:_R_POOL_ADMIN ()

0 commit comments

Comments
 (0)