Skip to content

Add filter chain matching and SNI-based TLS selection for server-side xDS - #6837

Merged
jrhee17 merged 6 commits into
line:mainfrom
jrhee17:feat/filter-chain-tls
Jul 6, 2026
Merged

Add filter chain matching and SNI-based TLS selection for server-side xDS#6837
jrhee17 merged 6 commits into
line:mainfrom
jrhee17:feat/filter-chain-tls

Conversation

@jrhee17

@jrhee17 jrhee17 commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

Motivation:

This is a subset of #6797 that adds server-side filter chain matching and SNI-based TLS certificate selection to the xDS module. When Armeria acts as an xDS-managed server, incoming connections need to be matched against the listener's filter_chains to determine which TLS configuration and routing to apply — replicating Envoy's filter chain matching semantics.

Modifications:

  • Added FilterChainMatcher which implements Envoy's BFS-style multi-pass narrowing algorithm. Supported match dimensions (in priority order): destination_port, server_names, transport_protocol, application_protocols. Each level precomputes specific/wildcard partitions to avoid redundant iteration.
  • Added ServerTlsSpecSelector which implements SNI-based certificate selection: exact DNS SAN match → wildcard match (e.g. *.example.com) → fallback to first certificate. Follows Envoy's DefaultTlsCertificateSelector behavior.
  • Added ListenerSnapshot.matchFilterChain(ConnectionContext) that delegates to FilterChainMatcher.
  • Added TransportSocketSnapshot.serverTlsSpec(ConnectionContext) that delegates to ServerTlsSpecSelector. The downstream constructor builds a ServerTlsSpec per certificate using the shared applyCommonTlsConfig method (same trust/verifier logic as the client path).
  • Marked destination_port, server_names, transport_protocol, and application_protocols fields as supported in listener_components.proto.
  • Added ServerFilterChainMatchTest — verifies transport protocol matching, default filter chain fallback, and unmatched connection rejection.
  • Added ServerTlsSpecSelectorTest — verifies exact SNI match, wildcard fallback, and no-SNI fallback.

Result:

  • ListenerSnapshot can now select the correct filter chain for an incoming connection based on destination port, SNI hostname, transport protocol, and ALPN.
  • TransportSocketSnapshot can select the correct server TLS certificate based on SNI, supporting multi-certificate downstream listeners.

@jrhee17 jrhee17 added this to the 1.41.0 milestone Jul 2, 2026
@coderabbitai

coderabbitai Bot commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 000d8a9c-37cb-418b-acdc-433be40c408a

📥 Commits

Reviewing files that changed from the base of the PR and between 33cf876 and f193fce.

📒 Files selected for processing (1)
  • xds/src/main/java/com/linecorp/armeria/xds/FilterChainMatcher.java
🚧 Files skipped from review as they are similar to previous changes (1)
  • xds/src/main/java/com/linecorp/armeria/xds/FilterChainMatcher.java

📝 Walkthrough

Walkthrough

Adds Envoy filter-chain matching support and SNI-based server TLS certificate selection. Listener snapshots now use a matcher for chain resolution, transport sockets use a selector for certificate choice, and tests cover matching behavior and YAML fixture quoting.

Changes

XDS filter chain and TLS spec matching

Layer / File(s) Summary
Proto field annotations
xds-api/.../listener_components.proto
Adds supported-field annotations to destination_port, server_names, transport_protocol, and application_protocols.
FilterChainMatcher narrowing algorithm
xds/.../FilterChainMatcher.java
Implements multi-pass narrowing by destination port, server names, transport protocol, and application protocols, with default-chain fallback.
ListenerSnapshot wiring
xds/.../ListenerSnapshot.java
Adds a cached matcher and delegates filter-chain selection to it.
ServerTlsSpecSelector SNI selection
xds/.../ServerTlsSpecSelector.java
Builds SNI lookup from certificate DNS names or CN and selects a TLS spec by exact, wildcard, or first-spec fallback.
TransportSocketSnapshot wiring
xds/.../TransportSocketSnapshot.java
Replaces single cached downstream TLS spec with a selector and delegates serverTlsSpec(ConnectionContext) to it.
Filter-chain and TLS integration tests
it/xds-client/.../ServerFilterChainMatchTest.java, it/xds-client/.../ServerTlsSpecSelectorTest.java
Adds tests for transport-protocol matching, default filter-chain fallback, wildcard SNI matching, unmatched rejection, and SNI-based certificate selection.
Quoted TLS filename fixtures
it/xds-client/.../*.java
Updates embedded YAML templates across xDS integration tests to quote certificate and trusted CA filename placeholders.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant ConnectionContext
  participant ListenerSnapshot
  participant FilterChainMatcher

  ConnectionContext->>ListenerSnapshot: matchFilterChain(ctx)
  ListenerSnapshot->>FilterChainMatcher: match(defaultFilterChain, ctx)
  FilterChainMatcher->>FilterChainMatcher: narrow by destination port
  FilterChainMatcher->>FilterChainMatcher: narrow by server names
  FilterChainMatcher->>FilterChainMatcher: narrow by transport protocol
  FilterChainMatcher->>FilterChainMatcher: narrow by application protocols
  FilterChainMatcher-->>ListenerSnapshot: matched or default filter chain
Loading
sequenceDiagram
  participant ConnectionContext
  participant TransportSocketSnapshot
  participant ServerTlsSpecSelector

  ConnectionContext->>TransportSocketSnapshot: serverTlsSpec(ctx)
  TransportSocketSnapshot->>ServerTlsSpecSelector: select(ctx)
  ServerTlsSpecSelector->>ServerTlsSpecSelector: exact SNI lookup
  ServerTlsSpecSelector->>ServerTlsSpecSelector: wildcard SNI lookup
  ServerTlsSpecSelector-->>TransportSocketSnapshot: selected ServerTlsSpec
Loading

Possibly related PRs

  • line/armeria#6820: Introduces the filter-chain resolution groundwork that this PR extends with Envoy-style matching and TLS selection.
  • line/armeria#6833: Completes the XDS server-side filter-chain and TLS-spec selection flow that builds on the earlier plugin and placeholder snapshot wiring.

Suggested reviewers: trustin, ikhoon, minwoox

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main change: server-side xDS filter chain matching and SNI-based TLS selection.
Description check ✅ Passed The description is directly related to the changeset and outlines the matching and TLS selection updates and tests.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (5)
xds/src/main/java/com/linecorp/armeria/xds/ServerTlsSpecSelector.java (1)

136-145: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Parsing the DN with split(",") is fragile; prefer LdapName.

X500Principal.getName() returns an RFC 2253 string where commas inside attribute values are escaped (e.g. CN=Doe\, John) and RDNs can be multi-valued (+). Splitting on , will mis-parse such CNs and yield a truncated/incorrect name, leading to wrong SNI keying for that certificate. Use javax.naming.ldap.LdapName to parse robustly.

♻️ Proposed refactor using LdapName
import javax.naming.InvalidNameException;
import javax.naming.ldap.LdapName;
import javax.naming.ldap.Rdn;
 `@Nullable`
 private static String extractCn(X509Certificate cert) {
-    final String dn = cert.getSubjectX500Principal().getName();
-    for (String rdn : dn.split(",")) {
-        final String trimmed = rdn.trim();
-        if (trimmed.toUpperCase(Locale.ROOT).startsWith("CN=")) {
-            return trimmed.substring(3);
-        }
-    }
-    return null;
+    final String dn = cert.getSubjectX500Principal().getName();
+    try {
+        final LdapName ldapName = new LdapName(dn);
+        for (Rdn rdn : ldapName.getRdns()) {
+            if ("CN".equalsIgnoreCase(rdn.getType())) {
+                return String.valueOf(rdn.getValue());
+            }
+        }
+    } catch (InvalidNameException e) {
+        // Ignore malformed DN and fall through.
+    }
+    return null;
 }

Please confirm javax.naming.ldap.LdapName is acceptable within this module's allowed dependencies.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@xds/src/main/java/com/linecorp/armeria/xds/ServerTlsSpecSelector.java` around
lines 136 - 145, The CN extraction in extractCn() is parsing the X500Principal
DN string by splitting on commas, which can break on escaped commas and
multi-valued RDNs. Replace that logic with robust DN parsing using
javax.naming.ldap.LdapName (and Rdn iteration) inside
ServerTlsSpecSelector.extractCn(), and handle InvalidNameException
appropriately; this will preserve the correct CN for SNI keying. Please also
confirm that javax.naming.ldap is allowed for this module before applying the
refactor.

Source: Path instructions

it/xds-client/src/test/java/com/linecorp/armeria/xds/it/server/ServerFilterChainMatchTest.java (1)

90-144: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Extract shared YAML boilerplate to reduce duplication.

The envoy.filters.network.http_connection_manager block (route config, virtual hosts, http_filters) is copy-pasted near-identically across all three tests (and twice within matchByTransportProtocol/defaultFilterChainFallback). Consider a small helper that builds this repeated block (and optionally the tls_certificates snippet) given parameters, then compose the filter-chain YAML from it. This would make future edits (e.g., adding a new match field) less error-prone.

As per path instructions, dev-guide recommends preferring "top-down organization, early-return, avoid redundancy" style.

Also applies to: 176-242, 276-313

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@it/xds-client/src/test/java/com/linecorp/armeria/xds/it/server/ServerFilterChainMatchTest.java`
around lines 90 - 144, The YAML in ServerFilterChainMatchTest repeats the same
envoy.filters.network.http_connection_manager setup across multiple test cases,
so extract that shared route/virtual_hosts/http_filters block into a helper and
reuse it when building the listener YAML. Use the existing test methods such as
matchByTransportProtocol and defaultFilterChainFallback to compose the per-chain
parts (including the optional tls_certificates snippet) from the shared builder
so future changes only need to be made once.

Source: Path instructions

xds/src/main/java/com/linecorp/armeria/xds/ListenerSnapshot.java (1)

109-117: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Javadoc slightly understates the new matching semantics.

"Returns the first FilterChainSnapshot that matches" no longer fully captures the multi-criteria, priority-ordered narrowing now performed by FilterChainMatcher. Consider linking to FilterChainMatcher's more detailed doc for clarity.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@xds/src/main/java/com/linecorp/armeria/xds/ListenerSnapshot.java` around
lines 109 - 117, Update the Javadoc on ListenerSnapshot.matchFilterChain to
reflect the newer multi-criteria, priority-ordered matching behavior instead of
saying it simply returns the “first” matching FilterChainSnapshot. Clarify that
the method delegates to FilterChainMatcher.match, and reference
FilterChainMatcher in the comment so readers can find the detailed matching
semantics. Keep the default-filter-chain fallback mention intact.
it/xds-client/src/test/java/com/linecorp/armeria/xds/it/server/ServerTlsSpecSelectorTest.java (2)

120-188: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Consider extracting shared request/assert boilerplate.

All three tests repeat the same build-client/execute/assert-OK-and-"hello" sequence. A small private helper (e.g. assertHelloOk(ClientTlsSpec, Endpoint)) would reduce duplication and make each test method focus on its distinguishing setup.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@it/xds-client/src/test/java/com/linecorp/armeria/xds/it/server/ServerTlsSpecSelectorTest.java`
around lines 120 - 188, The three tests in ServerTlsSpecSelectorTest repeat the
same WebClient build, execute, and OK/"hello" assertions, so extract that shared
flow into a small private helper such as assertHelloOk(ClientTlsSpec, Endpoint).
Update exactSniMatch, fallbackToFirstCert, and noSniReturnsFirstCert to call the
helper after their distinct TLS/Endpoint setup so the test methods only describe
their scenario-specific differences.

153-188: 🔒 Security & Privacy | 🔵 Trivial | ⚡ Quick win

Missing wildcard-match test coverage.

ServerTlsSpecSelector.select() implements three selection paths (exact, wildcard, fallback-to-first), but the tests here only exercise exact match and fallback. The wildcard branch (dotPos substring logic in ServerTlsSpecSelector.select) is untested, which is exactly the kind of subtle string-index logic that benefits from a dedicated test.

Suggested addition
`@Test`
void wildcardSniMatch() {
    // requires a cert with a wildcard SAN, e.g. "*.example.com"
    // SNI "www.example.com" → should resolve via wildcard match, not fallback.
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@it/xds-client/src/test/java/com/linecorp/armeria/xds/it/server/ServerTlsSpecSelectorTest.java`
around lines 153 - 188, Add a dedicated test in ServerTlsSpecSelectorTest to
cover the wildcard SNI branch in ServerTlsSpecSelector.select(), not just
exact-match and fallback behavior. Create a TLS setup with a wildcard
certificate (for example a cert whose SAN matches *.example.com), then send a
request using an SNI like www.example.com and assert that the connection
succeeds via wildcard selection rather than falling back to the first cert. Keep
the test рядом with fallbackToFirstCert and noSniReturnsFirstCert so the three
selection paths are covered together.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@it/xds-client/src/test/java/com/linecorp/armeria/xds/it/server/ServerFilterChainMatchTest.java`:
- Around line 258-267: The TLS failure assertion in ServerFilterChainMatchTest
is too engine-specific because Flags.tlsEngineType() can switch between JDK and
OpenSSL, making the SignatureException root-cause check brittle. Update the
assertion around wrongClient.execute(...) to use a provider-agnostic TLS failure
expectation, such as checking for SSLHandshakeException as the cause, or remove
the root-cause type check entirely if UnprocessedRequestException is sufficient.
Keep the change localized to the wrongTlsSpec / wrongClient test path.

In `@xds/src/main/java/com/linecorp/armeria/xds/FilterChainMatcher.java`:
- Around line 170-195: Normalize the SNI value before server name matching in
ServerNamesStep so casing differences do not prevent a match. In
FilterChainMatcher.ServerNamesStep.matchSpecifics, use the
ConnectionContext.sniHostname() value in a case-insensitive way (for example,
normalize it once before checking against
FilterChainSnapshot.filterChainMatch().getServerNamesList()), while keeping the
existing empty/null guard.

---

Nitpick comments:
In
`@it/xds-client/src/test/java/com/linecorp/armeria/xds/it/server/ServerFilterChainMatchTest.java`:
- Around line 90-144: The YAML in ServerFilterChainMatchTest repeats the same
envoy.filters.network.http_connection_manager setup across multiple test cases,
so extract that shared route/virtual_hosts/http_filters block into a helper and
reuse it when building the listener YAML. Use the existing test methods such as
matchByTransportProtocol and defaultFilterChainFallback to compose the per-chain
parts (including the optional tls_certificates snippet) from the shared builder
so future changes only need to be made once.

In
`@it/xds-client/src/test/java/com/linecorp/armeria/xds/it/server/ServerTlsSpecSelectorTest.java`:
- Around line 120-188: The three tests in ServerTlsSpecSelectorTest repeat the
same WebClient build, execute, and OK/"hello" assertions, so extract that shared
flow into a small private helper such as assertHelloOk(ClientTlsSpec, Endpoint).
Update exactSniMatch, fallbackToFirstCert, and noSniReturnsFirstCert to call the
helper after their distinct TLS/Endpoint setup so the test methods only describe
their scenario-specific differences.
- Around line 153-188: Add a dedicated test in ServerTlsSpecSelectorTest to
cover the wildcard SNI branch in ServerTlsSpecSelector.select(), not just
exact-match and fallback behavior. Create a TLS setup with a wildcard
certificate (for example a cert whose SAN matches *.example.com), then send a
request using an SNI like www.example.com and assert that the connection
succeeds via wildcard selection rather than falling back to the first cert. Keep
the test рядом with fallbackToFirstCert and noSniReturnsFirstCert so the three
selection paths are covered together.

In `@xds/src/main/java/com/linecorp/armeria/xds/ListenerSnapshot.java`:
- Around line 109-117: Update the Javadoc on ListenerSnapshot.matchFilterChain
to reflect the newer multi-criteria, priority-ordered matching behavior instead
of saying it simply returns the “first” matching FilterChainSnapshot. Clarify
that the method delegates to FilterChainMatcher.match, and reference
FilterChainMatcher in the comment so readers can find the detailed matching
semantics. Keep the default-filter-chain fallback mention intact.

In `@xds/src/main/java/com/linecorp/armeria/xds/ServerTlsSpecSelector.java`:
- Around line 136-145: The CN extraction in extractCn() is parsing the
X500Principal DN string by splitting on commas, which can break on escaped
commas and multi-valued RDNs. Replace that logic with robust DN parsing using
javax.naming.ldap.LdapName (and Rdn iteration) inside
ServerTlsSpecSelector.extractCn(), and handle InvalidNameException
appropriately; this will preserve the correct CN for SNI keying. Please also
confirm that javax.naming.ldap is allowed for this module before applying the
refactor.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 2bbb6185-fb2a-4cf0-a721-3c313edb0a4a

📥 Commits

Reviewing files that changed from the base of the PR and between f253133 and 8d2c7b5.

📒 Files selected for processing (7)
  • it/xds-client/src/test/java/com/linecorp/armeria/xds/it/server/ServerFilterChainMatchTest.java
  • it/xds-client/src/test/java/com/linecorp/armeria/xds/it/server/ServerTlsSpecSelectorTest.java
  • xds-api/src/main/proto/envoy/config/listener/v3/listener_components.proto
  • xds/src/main/java/com/linecorp/armeria/xds/FilterChainMatcher.java
  • xds/src/main/java/com/linecorp/armeria/xds/ListenerSnapshot.java
  • xds/src/main/java/com/linecorp/armeria/xds/ServerTlsSpecSelector.java
  • xds/src/main/java/com/linecorp/armeria/xds/TransportSocketSnapshot.java

@jrhee17

jrhee17 commented Jul 2, 2026

Copy link
Copy Markdown
Contributor Author

@coderabbitai full review

@coderabbitai

coderabbitai Bot commented Jul 2, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Full review finished.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (2)
xds/src/main/java/com/linecorp/armeria/xds/TransportSocketSnapshot.java (1)

97-102: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick win

Build server specs from the copied certificate snapshot.

ServerTlsSpecSelector indexes specs and tlsCertificates together. Build specs from this.tlsCertificates so the selector always receives two lists from the same immutable snapshot.

Proposed fix
         final List<ServerTlsSpec> specs =
-                tlsCertificates.stream()
+                this.tlsCertificates.stream()
                                .map(cert -> buildServerTlsSpec(downstreamTlsContext, cert,
                                                                this.validationContext))
                                .collect(ImmutableList.toImmutableList());
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@xds/src/main/java/com/linecorp/armeria/xds/TransportSocketSnapshot.java`
around lines 97 - 102, Build the server TLS specs from the copied certificate
snapshot instead of the original list so ServerTlsSpecSelector always receives
matching immutable data. In TransportSocketSnapshot, update the spec creation in
the constructor/initializer that builds specs and passes them to
ServerTlsSpecSelector so it iterates over this.tlsCertificates, keeping the
specs list aligned with the same snapshot used by the selector.
it/xds-client/src/test/java/com/linecorp/armeria/xds/it/server/ServerTlsSpecSelectorTest.java (1)

120-188: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Add wildcard-SNI coverage in ServerTlsSpecSelectorTest. The existing TlsPeerVerificationIntegrationTest wildcard cert case covers SAN validation, not ServerTlsSpecSelector’s wildcard lookup branch.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@it/xds-client/src/test/java/com/linecorp/armeria/xds/it/server/ServerTlsSpecSelectorTest.java`
around lines 120 - 188, Add a new test in ServerTlsSpecSelectorTest to cover
wildcard SNI selection, since exactSniMatch(), fallbackToFirstCert(), and
noSniReturnsFirstCert() do not exercise the wildcard lookup branch. Create a
client/server setup using a wildcard hostname like “*.example.com” and verify
the server presents the wildcard certificate when the SNI is a matching
subdomain, using the existing helpers such as ClientTlsSpec, Endpoint, and
WebClient. Keep the test focused on ServerTlsSpecSelector behavior rather than
SAN validation, which is already covered elsewhere.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@xds/src/main/java/com/linecorp/armeria/xds/FilterChainMatcher.java`:
- Around line 171-198: The ServerNamesStep in FilterChainMatcher only normalizes
the incoming SNI, so mixed-case configured server_names still fail to match.
Update matchSpecifics to compare against a normalized form of each
FilterChainSnapshot’s filterChainMatch().getServerNamesList() entry (or
normalize once when building the match set) so the lookup is truly
case-insensitive while preserving the existing exact-match behavior.

In `@xds/src/main/java/com/linecorp/armeria/xds/ServerTlsSpecSelector.java`:
- Around line 116-130: In ServerTlsSpecSelector, the SAN parsing logic in the
method that extracts subject names should not fall back to the CN when a SAN
extension is present but contains no DNS entries. Update the existing
SAN-handling branch so that the presence of any SAN extension returns only DNS
SANs if available, and otherwise returns an empty result instead of continuing
to extractCn and using the CN. Keep the CN fallback only for certificates with
no SAN extension at all.

---

Nitpick comments:
In
`@it/xds-client/src/test/java/com/linecorp/armeria/xds/it/server/ServerTlsSpecSelectorTest.java`:
- Around line 120-188: Add a new test in ServerTlsSpecSelectorTest to cover
wildcard SNI selection, since exactSniMatch(), fallbackToFirstCert(), and
noSniReturnsFirstCert() do not exercise the wildcard lookup branch. Create a
client/server setup using a wildcard hostname like “*.example.com” and verify
the server presents the wildcard certificate when the SNI is a matching
subdomain, using the existing helpers such as ClientTlsSpec, Endpoint, and
WebClient. Keep the test focused on ServerTlsSpecSelector behavior rather than
SAN validation, which is already covered elsewhere.

In `@xds/src/main/java/com/linecorp/armeria/xds/TransportSocketSnapshot.java`:
- Around line 97-102: Build the server TLS specs from the copied certificate
snapshot instead of the original list so ServerTlsSpecSelector always receives
matching immutable data. In TransportSocketSnapshot, update the spec creation in
the constructor/initializer that builds specs and passes them to
ServerTlsSpecSelector so it iterates over this.tlsCertificates, keeping the
specs list aligned with the same snapshot used by the selector.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: da0cb38e-3845-4445-b8ee-b2c1a11fc99e

📥 Commits

Reviewing files that changed from the base of the PR and between f253133 and 37fd9b6.

📒 Files selected for processing (7)
  • it/xds-client/src/test/java/com/linecorp/armeria/xds/it/server/ServerFilterChainMatchTest.java
  • it/xds-client/src/test/java/com/linecorp/armeria/xds/it/server/ServerTlsSpecSelectorTest.java
  • xds-api/src/main/proto/envoy/config/listener/v3/listener_components.proto
  • xds/src/main/java/com/linecorp/armeria/xds/FilterChainMatcher.java
  • xds/src/main/java/com/linecorp/armeria/xds/ListenerSnapshot.java
  • xds/src/main/java/com/linecorp/armeria/xds/ServerTlsSpecSelector.java
  • xds/src/main/java/com/linecorp/armeria/xds/TransportSocketSnapshot.java

Comment thread xds/src/main/java/com/linecorp/armeria/xds/FilterChainMatcher.java Outdated
Comment thread xds/src/main/java/com/linecorp/armeria/xds/ServerTlsSpecSelector.java Outdated
@codecov

codecov Bot commented Jul 2, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 56.64336% with 62 lines in your changes missing coverage. Please review.
✅ Project coverage is 75.11%. Comparing base (8150425) to head (1deec8e).
⚠️ Report is 521 commits behind head on main.

Files with missing lines Patch % Lines
...a/com/linecorp/armeria/xds/FilterChainMatcher.java 55.29% 35 Missing and 3 partials ⚠️
...om/linecorp/armeria/xds/ServerTlsSpecSelector.java 54.90% 15 Missing and 8 partials ⚠️
.../linecorp/armeria/xds/TransportSocketSnapshot.java 80.00% 0 Missing and 1 partial ⚠️
Additional details and impacted files
@@             Coverage Diff              @@
##               main    #6837      +/-   ##
============================================
+ Coverage     74.46%   75.11%   +0.65%     
- Complexity    22234    25275    +3041     
============================================
  Files          1963     2246     +283     
  Lines         82437    93770   +11333     
  Branches      10764    12260    +1496     
============================================
+ Hits          61385    70440    +9055     
- Misses        15918    17498    +1580     
- Partials       5134     5832     +698     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
xds/src/main/java/com/linecorp/armeria/xds/ServerTlsSpecSelector.java (1)

132-142: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Parse the subject DN with LdapName instead of splitting on commas. dn.split(",") can mis-read escaped commas in RFC 2253 names like CN=Doe\, John,O=Example, so extractCn may return the wrong value.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@xds/src/main/java/com/linecorp/armeria/xds/ServerTlsSpecSelector.java` around
lines 132 - 142, The CN extraction in extractCn currently splits the subject DN
on commas, which can misparse RFC 2253 names with escaped commas. Update
ServerTlsSpecSelector.extractCn to parse the DN using LdapName and iterate its
RDNs instead of string-splitting, preserving correct CN handling for names like
CN=Doe\, John,O=Example.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@xds/src/main/java/com/linecorp/armeria/xds/ServerTlsSpecSelector.java`:
- Around line 132-142: The CN extraction in extractCn currently splits the
subject DN on commas, which can misparse RFC 2253 names with escaped commas.
Update ServerTlsSpecSelector.extractCn to parse the DN using LdapName and
iterate its RDNs instead of string-splitting, preserving correct CN handling for
names like CN=Doe\, John,O=Example.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 3c16bb8d-78a6-476c-80bd-61ab1e0693bc

📥 Commits

Reviewing files that changed from the base of the PR and between 37fd9b6 and b76ab4a.

📒 Files selected for processing (1)
  • xds/src/main/java/com/linecorp/armeria/xds/ServerTlsSpecSelector.java

@jrhee17
jrhee17 marked this pull request as ready for review July 2, 2026 08:39
@mergify

mergify Bot commented Jul 2, 2026

Copy link
Copy Markdown

Tick the box to add this pull request to the merge queue (same as @mergifyio queue).

  • Queue this pull request

@ikhoon ikhoon left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍 👍

}

// Uses DNS SANs if present; falls back to CN per RFC 6125 §6.4.4.
static List<String> extractDnsNames(X509Certificate cert) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Optional) Would it be possible to reuse CertificateUtil with a small refactoring?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Moved to CertificateUtil and shares extractCommonName now

@minwoox minwoox left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍 👍

}
// Wildcard match: "www.example.com" → ".example.com"
final int dotPos = normalizedSni.indexOf('.', 1);
if (dotPos > 0 && dotPos < normalizedSni.length() - 1) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this from Envoy? it looks weird to try with .com when the normalizedSni is example.com

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Left a comment on the code with a reference to envoy's implementation

final String normalizedSni = sniHostname.toLowerCase(Locale.ROOT);
final ImmutableList.Builder<FilterChainSnapshot> matched = ImmutableList.builder();
for (FilterChainSnapshot fcs : specifics) {
if (fcs.filterChainMatch().getServerNamesList().contains(normalizedSni)) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Question: We don't have to consider a wildcard here. Right?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had dropped support initially for wildcard as I thought the implementation would become complex.
https://github.com/line/armeria/pull/6837/changes/BASE..1deec8e6ede7f862a185a3a156bd9fb534164c4b#diff-84f432e7c5ecb6f5e87b83f1bd0060923bcc570df872f0c0d25c628e8374f874R171

Added support at 33cf876

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@xds/src/main/java/com/linecorp/armeria/xds/FilterChainMatcher.java`:
- Around line 223-233: The hasWildcardServerName helper in FilterChainMatcher
should defensively handle empty server_names entries before calling
name.charAt(0). Add a guard for empty strings inside the loop so a blank entry
is skipped rather than throwing StringIndexOutOfBoundsException, while
preserving the existing wildcard matching logic for valid names.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 18b3ba3f-7dcd-47d6-b538-c6d960db4a14

📥 Commits

Reviewing files that changed from the base of the PR and between b76ab4a and 33cf876.

📒 Files selected for processing (22)
  • core/src/main/java/com/linecorp/armeria/internal/common/util/CertificateUtil.java
  • it/xds-client/src/test/java/com/linecorp/armeria/xds/it/BootstrapSecretsTest.java
  • it/xds-client/src/test/java/com/linecorp/armeria/xds/it/CertificateValidationContextTest.java
  • it/xds-client/src/test/java/com/linecorp/armeria/xds/it/ControlPlaneTlsIntegrationTest.java
  • it/xds-client/src/test/java/com/linecorp/armeria/xds/it/DataSourcePolicyTest.java
  • it/xds-client/src/test/java/com/linecorp/armeria/xds/it/DataSourceTest.java
  • it/xds-client/src/test/java/com/linecorp/armeria/xds/it/DynamicSecretTest.java
  • it/xds-client/src/test/java/com/linecorp/armeria/xds/it/ErrorHandlingTest.java
  • it/xds-client/src/test/java/com/linecorp/armeria/xds/it/PipeEndpointTest.java
  • it/xds-client/src/test/java/com/linecorp/armeria/xds/it/ResourceNodeMetricTest.java
  • it/xds-client/src/test/java/com/linecorp/armeria/xds/it/TlsPeerVerificationIntegrationTest.java
  • it/xds-client/src/test/java/com/linecorp/armeria/xds/it/XdsEndpointGroupTest.java
  • it/xds-client/src/test/java/com/linecorp/armeria/xds/it/XdsPreprocessorTest.java
  • it/xds-client/src/test/java/com/linecorp/armeria/xds/it/server/ServerDecoratorTest.java
  • it/xds-client/src/test/java/com/linecorp/armeria/xds/it/server/ServerFallbackTest.java
  • it/xds-client/src/test/java/com/linecorp/armeria/xds/it/server/ServerFilterChainMatchTest.java
  • it/xds-client/src/test/java/com/linecorp/armeria/xds/it/server/ServerMultiPortTest.java
  • it/xds-client/src/test/java/com/linecorp/armeria/xds/it/server/ServerMultiplePluginTest.java
  • it/xds-client/src/test/java/com/linecorp/armeria/xds/it/server/ServerTlsSpecSelectorTest.java
  • it/xds-client/src/test/java/com/linecorp/armeria/xds/it/server/ServerXdsTest.java
  • xds/src/main/java/com/linecorp/armeria/xds/FilterChainMatcher.java
  • xds/src/main/java/com/linecorp/armeria/xds/ServerTlsSpecSelector.java
✅ Files skipped from review due to trivial changes (5)
  • it/xds-client/src/test/java/com/linecorp/armeria/xds/it/PipeEndpointTest.java
  • it/xds-client/src/test/java/com/linecorp/armeria/xds/it/server/ServerMultiPortTest.java
  • it/xds-client/src/test/java/com/linecorp/armeria/xds/it/server/ServerFallbackTest.java
  • it/xds-client/src/test/java/com/linecorp/armeria/xds/it/server/ServerMultiplePluginTest.java
  • it/xds-client/src/test/java/com/linecorp/armeria/xds/it/server/ServerDecoratorTest.java
🚧 Files skipped from review as they are similar to previous changes (2)
  • it/xds-client/src/test/java/com/linecorp/armeria/xds/it/server/ServerTlsSpecSelectorTest.java
  • it/xds-client/src/test/java/com/linecorp/armeria/xds/it/server/ServerFilterChainMatchTest.java

Comment thread xds/src/main/java/com/linecorp/armeria/xds/FilterChainMatcher.java
@jrhee17
jrhee17 merged commit 051d14e into line:main Jul 6, 2026
15 of 17 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants