Repository navigation
Support one-way SPIFFE compatibility with legacy identities - #149
Merged
bellatrix007 merged 12 commits intoOct 6, 2026
Merged
bellatrix007 merged 12 commits into
bellatrix007 merged 12 commits into
Conversation
DEPEND-89163 follow-up: X509AuthenticationUtil.getClientId() now
recognizes LinkedIn's legacy Grestin-issued
'urn:li:servicePrincipal(<name>;...)' URI SAN automatically and
resolves it to the bare <name>, mirroring how the SPIFFE v1/wl
extraction strips its 'wl/' type prefix. This removes the need for
every cluster to hand-author a clientCertIdSanExtractRegex just to
get parity between legacy Grestin certs and SPIFFE certs during
migration (e.g. Kafka's 'servicePrincipal(kafka' vs SPIFFE's 'kafka'
znode-name mismatch).
The new pattern only runs when clientCertIdType=SAN was never
configured, so clusters that already tuned a SAN extractRegex (e.g.
one that intentionally keeps the 'servicePrincipal(' prefix, or one
that falls back to Subject DN on a misconfigured regex) keep their
existing, unchanged behavior.
Adds 4 unit tests to X509AuthTest covering: auto-extraction with no
config, correct behavior with a sibling servicePrincipalMetadata(...)
SAN present, non-interference with an already-configured
clientCertIdType=SAN cluster, and the existing no-match fallback to
Subject DN.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Remove company and internal-ticket references from the added comments and use generic certificate fixtures. Clarify that configured SAN extraction failures retain the Subject DN fallback; extraction behavior is unchanged. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Replace automatic certificate-side URN normalization with type-aware domain lookup. Return an immutable certificate-type/client-ID pair while preserving the original SPIFFE, configured SAN and Subject DN strings. Only SPIFFE v1/wl identities may fall back to exact application names parsed from legacy service-principal mapping keys; explicit mappings and full-path matching retain precedence and behavior. Cover typed identities, legacy mapping compatibility, exact-match precedence, principal/type isolation and mapping refresh. Use independent temporary databases for the mapping fixture. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Retain the authenticated certificate-type/client-ID pair on each connection and forward connection context through a backward-compatible authentication-provider matcher overload. Share legacy service-principal parsing between domain lookup and direct ACL checks. Allow SPIFFE v1/wl identities to match legacy service-principal ACLs and typed legacy service principals to match bare application ACLs. Bind compatibility to the original authenticated ID so mapped domains, other principal kinds and v2 paths are not reinterpreted. Preserve AuthInfo, creator ACLs, permission checks and exact superuser handling. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Try exact and segment-prefix domain mappings before legacy service-principal fallback. Require an explicit application/product/app path with an optional tag before comparing the application segment; retain existing v1/wl support. Reuse the same rule for direct ACLs without changing certificate extraction, stored identities, permissions or authenticated-ID binding. Preserve legacy-to-bare-name compatibility without inferring product or tag context. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Keep exact superuser matches first and share typed SPIFFE-to-legacy service-principal selection between both X509 providers. The new zookeeper.ssl.x509.legacySuperUserCompatibilityEnabled setting defaults to false. Preserve the original certificate identity and use the matched configured ID as the super marker, retaining explicit-superuser ACL handling without changing cross-domain policy. Document activation and privilege scope and add focused regression coverage. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Allow eligible SPIFFE application identities to match bare or formatted legacy targets without changing certificate-derived IDs. Restrict new bare aliases to conservative ASCII application names and remove the reverse alias introduced by this branch, preserving original legacy matching. Retain authenticated-ID binding, exact-match precedence and default-off superuser compatibility. Document the boundaries and cover structured targets and legacy exact matches. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Remove bare application-name fallback from shared ACL, domain mapping and opt-in superuser matching. Preserve exact client IDs, existing domain grants and formatted service-principal aliases. Update the coverage and documentation to distinguish compatibility from existing exact matches. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Snapshot the authenticated certificate identity on each request and carry it through follower and observer forwarding as reserved, transport-only metadata. Restore the context before ACL matching while preserving ordinary AuthInfo, original IDs and identity-binding rules. Reject malformed metadata and client use of the reserved scheme. Cover request snapshots, quorum serialization and relay paths, provider parity and unchanged creator ACL behavior. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Remove extra blank line separating the static org.junit.Assert imports from the java.security.cert.* imports, which maven-checkstyle-plugin flags as 'Extra separation in import group before java.security.cert.Certificate' under checkstyle-strict.xml.
bellatrix007
force-pushed
the
bellatrix007/spiffe-urn-fallback
branch
from
October 1, 2026 09:05
b7d12b6 to
9804390
Compare
Remove the compatibility configuration constant, getter and matching gate for both X509 providers. Preserve exact-match precedence, stable configured superuser markers, original certificate IDs and formatted-only alias rules. Update documentation and regressions for unconditional matching, including ignored legacy property values and unchanged creator ACL behavior. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Accept restricted bare application names alongside formatted legacy service principals in mapping, direct ACL and superuser comparisons. Preserve original identities, creator ACL storage and request forwarding. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
laxman-ch
reviewed
Oct 5, 2026
laxman-ch
left a comment
Collaborator
There was a problem hiding this comment.
Historically (For grestin as well), the certificate parsing implementation is duplicated here due to open source limitation. This duplication is incorrect and can cause inconsistencies. We need to make this pluggable and integrate this with interservice auth.
With plugin implementation this duplication will be eliminated.
Follow up ticket: https://linkedin.atlassian.net/browse/CICP-51582
laxman-ch
approved these changes
Oct 5, 2026
1 task
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
Builds on the SPIFFE support merged in #142. This branch is rebased onto
branch-3.6; this PR's diff contains only the compatibility changes described below, not the already-merged implementation.Preserve certificate-derived client ID strings while supporting one-way SPIFFE-to-legacy application-name compatibility in URI-domain mappings, direct znode ACLs, and legacy superuser-ID selection. Eligible SPIFFE applications can match bare application names or formatted legacy service-principal targets; legacy SAN/DN clients retain their original pre-PR matching behavior.
getClientId(...)returns an immutableClientIdentitywith the original ID and its extraction type:SPIFFE_V1_WL,SPIFFE_V1_WORKLOAD,SPIFFE_V2,LEGACY_SAN, orSUBJECT_DN. The original SPIFFE/configured SAN/Subject DN extraction order and strings are unchanged; certificate-derived IDs are not normalized or rewritten.Successful authentication retains this pair on the connection, separately from
AuthInfo. Authentication failure clears it. Direct ACL checks therefore use authenticated type information without reparsing certificates per request or changing stored identity strings.Each request snapshots the authenticated identity. Followers and observers carry it to the leader using a versioned, reserved
zookeeper-internal-x509entry in the existing quorum packet'sauthinfovector. The receiver strictly decodes and removes that entry, restoring the identity onRequesteven when no client connection exists locally. The entry is transport-only: it is not added to connection AuthInfo, accepted as a client authentication scheme, or stored in znode ACLs. Invalid versions/types and duplicate metadata are rejected.ACL preparation, eager authorization and read processing pass request identity through the provider context. The same direct legacy ACL match therefore applies to leader-local requests and forwarded writes, including
setData, create, delete,setACLand multi operations. Sync requests and observer relays preserve the context as well.URI-domain matching
Lookup order:
Eligible identities:
SPIFFE_V1_WLkafkakafkaSPIFFE_V1_WORKLOADapplication/example-mp/kafkakafkaSPIFFE_V2application/example-mp/kafkakafkaapplication/example-mp/kafka/cluster-akafkaApplication paths must be exactly
application/<mp>/<app>[/<tag>], with nonempty segments. Product-only paths such asapplication/example-mp, missing app segments, arbitrary v2 paths, and user/group/airflow/workload paths do not gain legacy service-principal compatibility. The product or tag is never used in place of the app name.Bare names (
kafka), truncated names (servicePrincipal(kafka), closed names (servicePrincipal(kafka)), and complete service-principal URNs are supported. Compatible legacy keys contribute a union of their domain sets only on fallback. Legacy SAN/DN and string-only lookup retain their existing behavior.Bare compatibility targets must match
^[A-Za-z0-9][A-Za-z0-9._-]*$. For example,application/example-mp/kafkacan matchkafkain mappings, ACLs or superuser selection. DN/URN-shaped targets such asCN=adminandurn:example:adminare not interpreted as bare application names. Existing exact matching remains valid: a v1/wl or legacy SAN identity extracted askafkastill matches a storedkafkaexactly. A full application-path target is never shortened.The existing znode can remain:
A legacy SAN ID
servicePrincipal(kafkamatches its exact key. A SPIFFE v1/wl IDkafka, or a v1/v2 IDapplication/example-mp/kafka, can reach the same domain through the compatibility fallback without renaming that legacy mapping. A bare mapping such as/zookeeper/uri-domain-map/broker-access/kafkacan also grantbroker-accessto a full SPIFFE application namedkafkawhen exact and prefix lookup miss.Legacy compatibility compares app names only; it does not distinguish products or tags. Full-path or prefix mappings take precedence when product/tag-specific domain selection is configured. The full authenticated identity is retained.
Direct znode ACL matching
(SPIFFE_V1_WL, "kafka")x509:kafka(exact), orx509:servicePrincipal(kafkaapplication/example-mp/kafka[/cluster-a]x509:kafkaorx509:servicePrincipal(kafkathrough compatibility; its full original ID remains an exact match(LEGACY_SAN, "kafka")x509:kafka(existing exact match)(LEGACY_SAN, "servicePrincipal(kafka")x509:servicePrincipal(kafka(existing exact match)Closed and full-URN service-principal ACL forms are supported as well. An application identity is not rewritten into a bare ID. Legacy IDs do not gain reverse aliases to bare or full product/tag-qualified application ACLs.
A bare ACL ID can also denote a domain. A client explicitly mapped into domain
kafkacan still matchx509:kafkaby exact domain identity. That existing grant is independent of application-name compatibility.LegacyServicePrincipalMatchershares the rules between domain lookup and direct ACL checks. The optional request-context-awareAuthenticationProvider.matches(...)overload is forwarded by the existing provider wrapper; other providers retain their original string matcher. Both X509 providers use the authenticated identity pair.The candidate AuthInfo ID must equal the original certificate-derived ID. A mapped domain cannot be reinterpreted as another service principal. Ordinary
x509identities and creator ACL expansion rules are unchanged; no syntheticx509aliases are added. Superuser role markers are described below.When the original client identity is selected for a creator ACL, its string is stored unchanged: v1/wl
kafkaremains bare, while v1/v2application/example-mp/kafkaremains a full application ID. Matching a legacy ACL does not rewrite that ACL or add another entry. ACL entries remain independent grants: a matching bare ALL entry is not restricted by a separate formatted READ entry.User/group/metadata principal names, untyped identities, unrelated application names, and Subject DN identities do not gain compatibility access. Matching is case-sensitive, and normal ACL permission checks remain enforced.
Always-on legacy superuser-ID compatibility
Compatibility is unconditional for both
zookeeper.X509AuthenticationProvider.superUserandzookeeper.X509ZNodeGroupAclProvider.superUserIdselection. Exact configured client-ID matches always take precedence. Otherwise, eligible SPIFFE v1/wl or complete v1/v2 application identities can match a configured bare application name or formatted legacy service principal using the same compatibility rules. Multiple compatible configured IDs select a stable marker.There is no enable/disable flag. The former
zookeeper.ssl.x509.legacySuperUserCompatibilityEnabledproperty is not read, even if a leftover setting specifiesfalse. An actual matching superuser configuration is still required; this does not make every SPIFFE client a superuser.The authenticated certificate-derived identity is unchanged. The
superAuthInfo entry uses the matched configured ID:Keeping the configured marker is important:
PrepRequestProcessor.fixupACL()still recognizes the connection as an explicit superuser rather than a cross-domain component, so it does not accidentally replace that superuser's requested ACLs under the group provider's automatic ACL policy.Only forward SPIFFE-to-legacy compatibility is added for superuser selection, just as for domain and direct-ACL fallback. User/group/airflow/arbitrary-v2/DN identities do not gain superuser aliases, and mapped domain names are not substituted for the authenticated identity. Existing exact legacy SAN/DN superuser settings remain valid, including bare and structured ID strings.
Exact URI-domain and direct-ACL matching, as well as existing cross-domain grants, are unchanged.
Privilege scope: legacy app names do not distinguish products or tags. Configuring a bare or formatted legacy superuser grants full superuser privileges to all eligible trusted identities with that app name. Use exact full application IDs instead of legacy app names when product/tag-specific privileges are required. Treat superuser configuration as a startup setting and restart servers or reconnect clients when changing it; it is not an immediate revocation switch for already authenticated connections.
No workflow, dependency, quorum-TLS, or unrelated refactoring changes are included.
Tests
ZooKeeperServer.checkACLand both X509 providers' matching pathsThe mapping fixture uses the existing temporary-directory helper to isolate its test database.
Focused compatibility test command:
Results: 63 tests passed across these three affected suites, with no failures, errors, or skipped tests. The single-fork setting avoids the existing provider fixture's intermittent parallel port-binding conflicts.
A separate local harness, not included in this PR, exercised 30 expected operation outcomes, 16 exact stored-ACL assertions and 24 data/version assertions across two standalone TLS group-provider profiles using disposable CA-signed certificates. With
setX509ClientIdAsAcl=trueandstoreAuthedClientIdfalse/true, Grestin-style and SPIFFE clients each created and updated their own znode, then updated it from a fresh TLS connection. Stored identities remained unchanged. A SPIFFE client could update the formatted legacy ACL-protected znode; reverse legacy access to the full SPIFFE-ID ACL and unrelated-client access were denied. No mappings or application superuser grants were present. Both local servers were stopped afterward.That standalone integration run does not exercise bare-name targets or quorum forwarding. Bare-name matching and forwarded ACL comparisons are covered by the targeted Java suites; broader live integration coverage was not rerun for the bare-name restoration.
These local tests do not cover production issuer integration, automatic certificate hot-reload/revocation, mixed-version upgrades, quorum-to-quorum TLS, or load testing.
Known inherited gaps
The broader review reproduced behaviors inherited from the base: deleting the last nested mapping leaf can cause its parent to become a broader prefix grant; the basic X509 provider's DN-only ACL validator rejects explicit non-DN ACL authoring even though existing ACL matching succeeds; and explicit group-provider superusers cannot expand
CREATOR_ALL_ACLwhen only asupermarker is present. These limitations are not fixed by the request-context forwarding change.An unauthorized write relayed from an observer with eager ACL checks disabled to a follower/ObserverMaster with eager ACL checks enabled also triggers an inherited null-connection error and critical request-processor failure. This was reproduced with both providers; it is not fixed here.
Changes that Break Backward Compatibility (Optional)
Both public
getClientId(...)overloads returnClientIdentityrather thanString. In-repository callers are updated; external callers must recompile and use.getId()for the previous string result.The request-context-aware
matches(...)method is a default interface overload, preserving existing authentication-provider implementations. String-only domain lookup retains its original behavior.The quorum Jute schema is unchanged, but both the client-facing peer and the leader must support the identity context for forwarded compatibility matches. Missing context never enables an alias; do not rely on this compatibility during a mixed-version rollout. Existing exact identities and ordinary domain/superuser AuthInfo retain their matching behavior.
The intended authorization change is one-way compatibility from eligible SPIFFE application identities to equivalent bare application names or formatted legacy service-principal names. Bare compatibility targets use the restricted grammar documented above. Certificate validation, raw extracted IDs, stored AuthInfo/ACL formats, normal ACL permission checks, and quorum configuration are unchanged. Superuser-ID compatibility is always enabled: existing bare or formatted legacy superuser settings also authorize eligible SPIFFE applications with the same application name. Exact matching and original pre-PR legacy-client matching remain supported.
For the domain provider, direct principal ACL compatibility applies when the original client ID is present in AuthInfo under the existing configuration; domain-only AuthInfo is not promoted to a principal.
Documentation (Optional)
The typed identity result, lookup order, application-name requirements, direct-ACL rules, and compatibility boundaries are documented above and in the implementation. Unconditional superuser matching, its privilege scope, marker semantics, request-context forwarding and mixed-version caveats are documented in
zookeeperAdmin.md.Generated with GitHub Copilot CLI