Commit 5f9a236
Add SPIFFE v2 URI-SAN based principal extraction (DEPEND-89172) (#142)
* Add SPIFFE v2 URI-SAN based principal extraction (DEPEND-89172)
Adds server-side support for SPIFFE-issued client certificates in X509 auth.
The principal is the ILM UID (path-after-/v2/), aligned with the LinkedIn ILM
v2 design: trust-domain stripped, segment-prefix matching for ACL lookup.
Key changes:
- X509AuthenticationUtil.matchAndExtractSpiffeSAN extracts the ILM UID from
v2 SPIFFE URIs. v1 SPIFFE URIs and user-identity URIs (/v<N>/user/...) fall
through to existing URN / Subject-DN extraction.
- X509AuthenticationConfig adds spiffe.sanMatchRegex as the operator-
controlled trust-domain gate.
- ZkClientUriDomainMappingHelper recursively walks the znode subtree below
each domain. Only leaf znodes are registered as keys (path joined by '/'),
letting multi-segment SPIFFE UIDs be expressed as nested znodes (whose
names can't contain '/'). getDomains does exact-match then segment-prefix
walk-up. clientUriToDomainNames is volatile with in-method snapshot.
- Defense-in-depth: SPIFFE path extraction uses URI.getRawPath() and rejects
any path containing '%' to prevent URL-decoding bypass of identity checks.
Tests: 29/29 pass (X509AuthTest 13, X509SpiffeAuthIntegrationTest 6,
ZkClientUriDomainMappingHelperTest 10; includes end-to-end SPIFFE-cert
through X509ZNodeGroupAclProvider with real znode mapping).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* Address review: thread-safe SPIFFE config + wire prefix walk-up to production path
Two fixes addressing the code review on PR #142:
1. X509AuthenticationConfig.getSpiffeSanMatchPattern now uses
double-checked locking with volatile fields and a dedicated lock
object, matching the pattern already used by allowedClientIdAsAclDomains
and other lazy-loaded fields in the same class. The previous lazy-init
was a data race on the per-handshake hot path.
2. X509ZNodeGroupAclProvider's setDomainAuthUpdater lambda now calls
helper.getDomains(clientId) instead of the raw map's getOrDefault,
so the segment-prefix walk-up added to ZkClientUriDomainMappingHelper
is reachable from the production authentication path. This is the
znode-tree analogue of LinkedIn's documented acl-tool wildcard idiom
(`--spiffe "application/<mp>/*"`); without this fix, MP-level prefix
grants documented in the class javadoc would silently no-op.
Adds testA4_SpiffeCertResolvesViaPrefixWalkUpToDomainAuthInfo: real
SPIFFE cert with a 4-segment principal resolves to an MP-level leaf
grant through the full provider->helper.getDomains path. This test
would have failed before fix #2 (the exact-match lookup misses the
4-segment principal against a 2-segment registered prefix).
All 30 SPIFFE-related tests pass (X509AuthTest 13 +
X509SpiffeAuthIntegrationTest 6 + ZkClientUriDomainMappingHelperTest 11).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* Address review: accept SPIFFE v1 workload certs alongside v2
Per @rgodha's review comment, the extractor now accepts both SPIFFE
versions instead of rejecting v1 outright:
- v2 (existing): spiffe://<td>/v2/<path> → principal is the full path
after /v2/ (the ILM UID, e.g. "application/foo-mp/bar-app").
- v1 workload (new): spiffe://<td>/v1/wl/<app-name> → strip the "wl/"
type prefix; principal is just the app-name. This matches how legacy
authZ handled v1 identities. Other v1 paths (e.g. v1/wf/ workflow)
still fall through to URN/DN.
User-identity URIs (/v<N>/user/...) continue to be rejected for both
versions — they must never be promoted to a service principal.
The test match regex broadens to ^spiffe://.*/v[12]/.*$ so existing
test scaffolding exercises both versions. testSpiffeV1FallsBackToDn
becomes testSpiffeV1WlAuth (now asserts extraction). The integration
test for v1 likewise flips from fall-back-to-DN to v1/wl extraction.
LISPIFFE-ID spec reference:
https://github.com/linkedin-multiproduct/gopki/blob/master/LISPIFFE-ID.md#2-uri-path
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* Fix SPIFFE v1/wl principal extraction to reject multi-segment values, and add regression tests for a real Grestin cert's dual urn:li: SAN scenario.
* Make SPIFFE URI-SAN principal extraction always-on, not a feature flag
Previously SPIFFE detection was gated behind the opt-in
ssl.x509.spiffe.sanMatchRegex system property and was a no-op unless an
operator explicitly configured it. This removes that config knob entirely:
X509AuthenticationUtil#getClientId now checks for a spiffe:// URI SAN
unconditionally, before the clientCertIdType-gated legacy URN fallback,
regardless of how (or whether) clientCertIdType is configured.
- Remove SSL_X509_SPIFFE_SAN_MATCH_REGEX and its lazy-loaded Pattern
field/getter/setter from X509AuthenticationConfig.
- X509AuthenticationUtil: replace the configurable SPIFFE match pattern
with an unconditional constant and run SPIFFE detection first,
unconditionally, in getClientId().
- Update SpiffeAuthTestUtil and existing SPIFFE tests to drop references
to the removed config property.
- Add regression tests proving SPIFFE v1/v2 extraction and SPIFFE
user-identity rejection work with zero clientCertIdType configuration
(X509AuthTest, X509SpiffeAuthIntegrationTest).
* Add v1 application/airflow workload sub-type support to SPIFFE extraction
LISPIFFE-ID spec section 2.A lists application/<...> and airflow/<....>
as valid v1 workload sub-types alongside wl/, which were not previously
recognized. Per rgodha's review comment on PR #142, extend the v1
extractor to accept these forms, retaining the type prefix in the
principal (matching v2 semantics), while wl/ keeps its existing
bare-app-name behavior. v1/wf/ (Flyte workflow) remains out of scope.
Adds 6 new tests across X509AuthTest and X509SpiffeAuthIntegrationTest.
* Consolidate SPIFFE auth tests into real-cert integration suite
X509AuthTest previously duplicated most SPIFFE URI-SAN extraction
scenarios using TestCertificate, a hand-rolled X509Certificate whose
getSubjectAlternativeNames() just returns a canned list -- it never
exercises real ASN.1/SAN encoding or the JDK's certificate parsing.
X509SpiffeAuthIntegrationTest already covered several of the same
scenarios using real BouncyCastle-signed certs, but had a few gaps.
Changes:
- Added 8 real-cert tests to X509SpiffeAuthIntegrationTest to close
the coverage gaps: v1/wl zero-config, v1/wl multi-segment fallback,
v1/user rejection, v2/workload extraction, v2/user zero-config
rejection, non-SPIFFE-SAN-falls-back-to-URN (with URN configured),
multiple-SPIFFE-SANs fallback, and SPIFFE-wins-over-URN precedence.
- Removed the now-redundant SPIFFE-specific mock tests from
X509AuthTest (17 tests across ~230 lines), along with the
SPIFFE_V1_URI/SPIFFE_V2_URI fixtures and now-unused imports.
X509AuthTest keeps only the generic (non-SPIFFE) auth/SAN-regex
tests, which still use the lightweight fake certificate since they
don't need real certificate parsing.
- X509SpiffeAuthIntegrationTest is now the authoritative, real-cert
suite for SPIFFE certificate validation and principal extraction.
Verified: X509AuthTest (6 tests) + X509SpiffeAuthIntegrationTest
(18 tests, up from 10) all pass.
* Trigger CI re-run (no code changes)
The previous CI run for this PR is over a month old and can no longer
be rerun via the GitHub Actions UI/API (runs older than ~30 days are
ineligible for rerun). This empty commit triggers a fresh run to get
a current result for the flaky C-client testAuth check.
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>1 parent 1d05536 commit 5f9a236
9 files changed
Lines changed: 1111 additions & 63 deletions
File tree
- zookeeper-server/src
- main/java/org/apache/zookeeper/server/auth
- znode/groupacl
- test/java/org/apache/zookeeper
- common
- server/auth/znode/groupacl
- test
Lines changed: 2 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
83 | 83 | | |
84 | 84 | | |
85 | 85 | | |
| 86 | + | |
86 | 87 | | |
87 | 88 | | |
88 | 89 | | |
| |||
482 | 483 | | |
483 | 484 | | |
484 | 485 | | |
| 486 | + | |
485 | 487 | | |
Lines changed: 170 additions & 13 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
18 | 18 | | |
19 | 19 | | |
20 | 20 | | |
| 21 | + | |
21 | 22 | | |
22 | 23 | | |
23 | 24 | | |
24 | 25 | | |
25 | 26 | | |
| 27 | + | |
26 | 28 | | |
27 | 29 | | |
28 | 30 | | |
| |||
48 | 50 | | |
49 | 51 | | |
50 | 52 | | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
51 | 86 | | |
52 | 87 | | |
53 | 88 | | |
| |||
131 | 166 | | |
132 | 167 | | |
133 | 168 | | |
| 169 | + | |
| 170 | + | |
| 171 | + | |
| 172 | + | |
| 173 | + | |
| 174 | + | |
| 175 | + | |
| 176 | + | |
| 177 | + | |
| 178 | + | |
| 179 | + | |
| 180 | + | |
| 181 | + | |
| 182 | + | |
134 | 183 | | |
135 | 184 | | |
136 | 185 | | |
| |||
140 | 189 | | |
141 | 190 | | |
142 | 191 | | |
143 | | - | |
144 | 192 | | |
145 | 193 | | |
146 | 194 | | |
| 195 | + | |
| 196 | + | |
| 197 | + | |
| 198 | + | |
| 199 | + | |
| 200 | + | |
| 201 | + | |
| 202 | + | |
| 203 | + | |
| 204 | + | |
| 205 | + | |
| 206 | + | |
| 207 | + | |
| 208 | + | |
| 209 | + | |
| 210 | + | |
| 211 | + | |
| 212 | + | |
| 213 | + | |
| 214 | + | |
| 215 | + | |
| 216 | + | |
| 217 | + | |
| 218 | + | |
| 219 | + | |
| 220 | + | |
| 221 | + | |
| 222 | + | |
| 223 | + | |
| 224 | + | |
| 225 | + | |
| 226 | + | |
| 227 | + | |
| 228 | + | |
| 229 | + | |
| 230 | + | |
| 231 | + | |
| 232 | + | |
| 233 | + | |
| 234 | + | |
| 235 | + | |
| 236 | + | |
| 237 | + | |
| 238 | + | |
| 239 | + | |
| 240 | + | |
| 241 | + | |
| 242 | + | |
| 243 | + | |
| 244 | + | |
| 245 | + | |
| 246 | + | |
| 247 | + | |
| 248 | + | |
| 249 | + | |
| 250 | + | |
| 251 | + | |
| 252 | + | |
| 253 | + | |
| 254 | + | |
| 255 | + | |
| 256 | + | |
| 257 | + | |
| 258 | + | |
| 259 | + | |
| 260 | + | |
| 261 | + | |
| 262 | + | |
| 263 | + | |
| 264 | + | |
| 265 | + | |
| 266 | + | |
| 267 | + | |
| 268 | + | |
| 269 | + | |
| 270 | + | |
| 271 | + | |
| 272 | + | |
| 273 | + | |
| 274 | + | |
| 275 | + | |
| 276 | + | |
| 277 | + | |
| 278 | + | |
| 279 | + | |
| 280 | + | |
| 281 | + | |
| 282 | + | |
| 283 | + | |
| 284 | + | |
| 285 | + | |
| 286 | + | |
| 287 | + | |
| 288 | + | |
| 289 | + | |
| 290 | + | |
| 291 | + | |
| 292 | + | |
| 293 | + | |
| 294 | + | |
| 295 | + | |
| 296 | + | |
| 297 | + | |
| 298 | + | |
| 299 | + | |
| 300 | + | |
| 301 | + | |
| 302 | + | |
| 303 | + | |
| 304 | + | |
| 305 | + | |
| 306 | + | |
| 307 | + | |
| 308 | + | |
| 309 | + | |
| 310 | + | |
| 311 | + | |
| 312 | + | |
| 313 | + | |
147 | 314 | | |
148 | 315 | | |
149 | 316 | | |
| |||
204 | 371 | | |
205 | 372 | | |
206 | 373 | | |
207 | | - | |
208 | | - | |
209 | | - | |
210 | | - | |
211 | | - | |
212 | | - | |
213 | | - | |
214 | | - | |
215 | | - | |
216 | | - | |
217 | | - | |
218 | | - | |
| 374 | + | |
| 375 | + | |
219 | 376 | | |
220 | 377 | | |
221 | 378 | | |
| |||
Lines changed: 6 additions & 4 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
19 | 19 | | |
20 | 20 | | |
21 | 21 | | |
22 | | - | |
23 | 22 | | |
24 | 23 | | |
25 | 24 | | |
| |||
164 | 163 | | |
165 | 164 | | |
166 | 165 | | |
167 | | - | |
| 166 | + | |
| 167 | + | |
| 168 | + | |
| 169 | + | |
| 170 | + | |
168 | 171 | | |
169 | 172 | | |
170 | | - | |
171 | | - | |
| 173 | + | |
172 | 174 | | |
173 | 175 | | |
174 | 176 | | |
| |||
0 commit comments