Skip to content

Fix setting TLS verify - #488

Merged
wprzytula merged 8 commits into
scylladb:masterfrom
wprzytula:fix-tls-verification
Aug 7, 2026
Merged

Fix setting TLS verify#488
wprzytula merged 8 commits into
scylladb:masterfrom
wprzytula:fix-tls-verification

Conversation

@wprzytula

@wprzytula wprzytula commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

Summary

This PR follows up #489 and cleans up setting verify flags for TLS.

What's done

Repaired the default (security-critical)

There was a bug that caused mismatch between documentation (VERIFY_PEER_CERT) and reality (VERIFY_NONE).
By fixing the bug, the real default moves from "no verification at all" to verifying the peer's certificate, . So for callers who never called cass_ssl_set_verify_flags(), a trusted certificate must now be configured with cass_ssl_add_trusted_cert(), otherwise every connection fails. The previous behaviour remains available as CASS_SSL_VERIFY_NONE.

Fixed CASS_SSL_VERIFY_PEER_CERT semantics

After Rust Driver turned on hostname verification by IP in SAN (scylladb/scylla-rust-driver#1491), semantics of our implementation silently changed, requiring identity verification even with CASS_SSL_VERIFY_PEER_CERT option set.
To support chain-only validation, a bit hacky logic via custom verify callback was introduced: after openssl finishes identity verification (either with success or failure), the callback is fired; the callback matches on an error and ignores errors related to failing identity verification. This emulates no identity verification, just certificate chain verification.

Detailed description of the hack by Opus

Support for PEER_CERT is hacked in — but the hack looks solid

CASS_SSL_VERIFY_PEER_CERT means "certificate is present and valid" — chain
validation without an identity check. We could not express that, because the
Rust driver pins the identity itself, in network/connection.rs:

crate::network::tls::Tls::OpenSsl010(mut ssl) => {
    ssl.param_mut()
        .set_ip(node_address.ip())?;

That runs per-connection, on the Ssl created from our SslContext, so nothing
we configure on the SSL_CTX can undo it. PEER_CERT therefore behaved
identically to PEER_IDENTITY, and an intermediate commit in this PR does no
more than log a warning saying so.

The way out is a custom verification callback that tolerates exactly the
identity-mismatch errors
. When the identity check fails, OpenSSL's
check_id() does not abort outright — it sets the error and asks the verify
callback, continuing if the callback returns 1:

if (vpm->ip != NULL && X509_check_ip(...) <= 0) {
    if (!check_id_error(ctx, X509_V_ERR_IP_ADDRESS_MISMATCH))
        return 0;
}

So PEER_CERT installs:

extern "C" fn verify_peer_cert_callback(preverify_ok: c_int, x509_ctx: *mut X509_STORE_CTX) -> c_int {
    if preverify_ok == 1 { return 1; }

    let error = unsafe { X509_STORE_CTX_get_error(x509_ctx) };
    match error {
        X509_V_ERR_HOSTNAME_MISMATCH
        | X509_V_ERR_EMAIL_MISMATCH
        | X509_V_ERR_IP_ADDRESS_MISMATCH => {
            unsafe { X509_STORE_CTX_set_error(x509_ctx, X509_V_OK) };
            1
        }
        _ => 0,
    }
}

Why I think this is solid:

  • It is narrow. Only the three identity-mismatch codes are accepted. An
    untrusted issuer, an expired certificate, a self-signed leaf, a broken chain
    — all still fail, at any depth. It cannot degrade into "accept anything".
  • It is not a bypass of chain validation. SSL_VERIFY_PEER stays on and
    the full chain walk runs unchanged; we only suppress the identity check that
    the layer below forces on us.
  • It only applies to PEER_CERT. PEER_IDENTITY still passes None, so
    switching flags in either direction is clean.
  • Resetting the error to X509_V_OK keeps SSL_get_verify_result() consistent
    with the handshake outcome; returning 1 alone would let the handshake succeed
    while leaving a stale mismatch error on the session.
  • It is covered by the test that asserts both directions: mismatched SAN
    accepted, untrusted CA rejected.

The honest caveat: this is a workaround for the driver forcing set_ip(). The clean fix belongs in the Rust Driver — letting the caller opt out of identity verification — after which the callback can go away.

Flags handling

  • CASS_SSL_VERIFY_PEER_IDENTITY is now supported. After Rust Driver turned on hostname verification by IP in SAN (Openssl hostname verification scylla-rust-driver#1491), this is easily turned on.
  • CASS_SSL_VERIFY_PEER_IDENTITY_DNS is mapped to CASS_SSL_VERIFY_PEER_IDENTITY with a warning — the driver verifies the IP, not a hostname, and hostname resolution is a separate feature we do not implement.
  • cass_ssl_set_verify_flags() now treats the flags as the bitmask they are, so documented combinations such as CASS_SSL_VERIFY_PEER_CERT | CASS_SSL_VERIFY_PEER_IDENTITY work. Values carrying unknown bits log an
    error and leave the settings untouched, rather than silently switching verification on.

Updated documentation

  • Noted that certificates that identify a node only by subject common name are rejected — they need an iPAddress subject alternative name. OpenSSL's X509_VERIFY_PARAM_set1_ip(), which the Rust driver uses, never falls back to the subject for IP checks, unlike the C/C++ driver's own verification code. This is documented in cassandra.h and in the TLS guide.

Possible follow-ups

  • Upstream cpp-driver documents CASS_SSL_VERIFY_PEER_CERT as the default; we may deliberately default to the stricter CASS_SSL_VERIFY_PEER_IDENTITY, which is the Rust Driver's choice.
  • Real CASS_SSL_VERIFY_PEER_IDENTITY_DNS (requires Rust Driver's support).
  • Push identity-verification opt-out into the Rust Driver, so the PEER_CERT callback can be dropped.

Pre-review checklist

  • I have split my patch into logically separate commits.
  • All commit messages clearly explain what they change and why.
  • PR description sums up the changes and reasons why they should be introduced.
  • I have implemented Rust unit tests for the features/changes introduced.
  • I have enabled appropriate tests in Makefile in {SCYLLA,CASSANDRA}_(NO_VALGRIND_)TEST_FILTER.
  • [ ] I added appropriate Fixes: annotations to PR description.

Copilot AI balanced review requested due to automatic review settings August 4, 2026 09:46
@coderabbitai

coderabbitai Bot commented Aug 4, 2026

Copy link
Copy Markdown

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: Organization UI

Review profile: QUIET

Plan: Pro Plus

Run ID: 031dbefb-9312-4d88-bb4f-23505fcd152f

📥 Commits

Reviewing files that changed from the base of the PR and between 2a49017 and fcc6af6.

📒 Files selected for processing (6)
  • docs/source/topics/security/tls.md
  • include/cassandra.h
  • scylla-rust-wrapper/build.rs
  • scylla-rust-wrapper/src/lib.rs
  • scylla-rust-wrapper/src/ssl.rs
  • scylla-rust-wrapper/tests/integration/ccm/tls.rs
🔗 Linked repositories identified

CodeRabbit considers these linked repositories for cross-repo context during reviews:

  • scylladb/scylladb (auto-detected)

📝 Walkthrough

Walkthrough

OpenSSL peer certificate-chain verification is now enabled by default. Verification flags support validated combinations for disabled, chain-only, identity, and combined verification. The chain-only callback tolerates identity mismatches but rejects other certificate errors. Rust bindings expose CassSslVerifyFlags. TLS tests cover untrusted chains, identity mismatches, and chain-only verification. Documentation defines IP SAN requirements and unsupported DNS verification.

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant CAPIConnection
  participant OpenSSL
  participant VerificationCallback

  Client->>CAPIConnection: Set SSL verification flags
  CAPIConnection->>OpenSSL: Configure peer verification
  OpenSSL->>VerificationCallback: Validate certificate chain
  VerificationCallback-->>OpenSSL: Accept or reject result
  OpenSSL-->>CAPIConnection: TLS handshake result
  CAPIConnection-->>Client: Connection success or failure
Loading

Possibly related PRs

Suggested labels: area/testing

Suggested reviewers: lorak-mmk

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly identifies the primary change: fixing TLS verification behavior and flag handling.
Description check ✅ Passed The description explains the motivation, behavior changes, tests, documentation updates, and checklist status in sufficient detail.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
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.

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

@wprzytula wprzytula self-assigned this Aug 4, 2026
@wprzytula wprzytula added bug Something isn't working area/testing Related to unit/integration testing labels Aug 4, 2026

Copilot AI 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.

🟡 Not ready to approve

Valid bitmask combinations are rejected, CN-only certificates violate the advertised contract, and related documentation and regression coverage remain incomplete.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

This review doesn't count toward merge requirements. Sign up for the private preview to control whether Copilot approvals count.

Pull request overview

Fixes TLS verification defaults and adds CCM-backed end-to-end TLS coverage through the C API.

Changes:

  • Defaults TLS to peer identity verification and adds chain-only verification.
  • Adds TLS, mutual-TLS, and certificate validation integration tests.
  • Updates dependencies and Makefile test targets.
File summaries
File Description
Makefile Runs CCM TLS tests and updates ScyllaDB version.
include/cassandra.h Documents the new TLS default.
scylla-rust-wrapper/Cargo.toml Adds CCM and certificate-generation dependencies.
scylla-rust-wrapper/Cargo.lock Locks updated dependencies.
scylla-rust-wrapper/src/ssl.rs Implements verification defaults and flag handling.
scylla-rust-wrapper/tests/integration/main.rs Registers CCM tests.
scylla-rust-wrapper/tests/integration/ccm/mod.rs Adds shared CCM test support.
scylla-rust-wrapper/tests/integration/ccm/tls.rs Adds end-to-end TLS tests.
Review details

Suppressed comments (1)

scylla-rust-wrapper/src/ssl.rs:246

  • flags is a bitmask in the public API, but this catch-all rejects valid documented combinations such as CASS_SSL_VERIFY_PEER_CERT | CASS_SSL_VERIFY_PEER_IDENTITY (0x03) and the DNS variant (0x05); both are used in docs/source/topics/security/tls.md:176-179 and handled bitwise by the C++ implementation. With the new stricter default, those calls silently retain IP verification instead of applying the requested mode. Accept combinations of known bits while rejecting only unknown bits, and add coverage for the documented combinations.
        _ => {
            tracing::error!(
                "Provided unknown CASS_SSL_VERIFY flags variant: {}. Leaving settings unchanged.",
                flags
            );
  • Files reviewed: 7/8 changed files
  • Comments generated: 5
  • Review effort level: Balanced

We're testing this review assessment. Please use 👍 or 👎 to tell us if it's correct.

Comment thread scylla-rust-wrapper/src/ssl.rs Outdated
Comment thread scylla-rust-wrapper/tests/integration/ccm/tls.rs Outdated
Comment thread include/cassandra.h Outdated
Comment thread scylla-rust-wrapper/src/ssl.rs Outdated
Comment thread scylla-rust-wrapper/src/ssl.rs Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

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 (1)
Makefile (1)

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

Update the stale version example.

The comment uses release:2025.3.8, but the default SCYLLA_VERSION is now release:2026.2.2.

📝 Proposed fix
-	@# Prefer a fully-qualified SCYLLA_VERSION (e.g. release:2025.3.8 rather
-	@# than release:2025.3): scylla-ccm re-resolves the version on every `ccm`
+	@# Prefer a fully-qualified SCYLLA_VERSION (e.g. release:2026.2.2 rather
+	@# than release:2026.2): scylla-ccm re-resolves the version on every `ccm`
🤖 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 `@Makefile` at line 503, Update the version example in the Makefile comment
near SCYLLA_VERSION from release:2025.3.8 to release:2026.2.2, matching the
current default value while preserving the surrounding guidance.
🤖 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 `@scylla-rust-wrapper/tests/integration/ccm/tls.rs`:
- Around line 225-270: The if let Some((cert_pem, key_pem)) =
client_cert_and_key_pem conditional block that calls cass_ssl_set_cert and
cass_ssl_set_private_key is duplicated verbatim in the code. Remove the second
occurrence of this entire block (the duplicate starting after the first one
completes) to ensure the certificate and key are set only once on the CassSsl
instance.
- Around line 52-59: Prevent concurrent TLS integration tests from sharing the
`cluster_tls_3_node` directory: update `cluster_3_nodes` or the four
`run_ccm_tls_test` callers to use test-specific cluster names, or serialize
those test functions with `#[serial_test::serial]`. Preserve the existing
cluster configuration while ensuring each test’s `File::create_new()` calls
cannot race.

---

Nitpick comments:
In `@Makefile`:
- Line 503: Update the version example in the Makefile comment near
SCYLLA_VERSION from release:2025.3.8 to release:2026.2.2, matching the current
default value while preserving the surrounding guidance.
🪄 Autofix

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: Organization UI

Review profile: QUIET

Plan: Pro Plus

Run ID: e2bca14a-10b3-4886-a1a5-bfa78716d346

📥 Commits

Reviewing files that changed from the base of the PR and between 9e47e3b and 8382678.

⛔ Files ignored due to path filters (1)
  • scylla-rust-wrapper/Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (7)
  • Makefile
  • include/cassandra.h
  • scylla-rust-wrapper/Cargo.toml
  • scylla-rust-wrapper/src/ssl.rs
  • scylla-rust-wrapper/tests/integration/ccm/mod.rs
  • scylla-rust-wrapper/tests/integration/ccm/tls.rs
  • scylla-rust-wrapper/tests/integration/main.rs

Comment thread scylla-rust-wrapper/tests/integration/ccm/tls.rs
Comment thread scylla-rust-wrapper/tests/integration/ccm/tls.rs Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Note

Quiet mode is enabled, so only the most important comments were posted inline. Other review comments are grouped below.

🟡 Other comments (2)
docs/source/topics/security/tls.md-17-21 (1)

17-21: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Align TLS documentation with the IP-SAN-only implementation.

CN-only and DNS-SAN setup paths do not work with default identity verification.

  • docs/source/topics/security/tls.md#L17-L21: Update Line 15 and Lines 23-43. Require an iPAddress SAN. Mark CN-only and DNS-SAN certificates as requiring CASS_SSL_VERIFY_PEER_CERT or CASS_SSL_VERIFY_NONE.
  • include/cassandra.h#L4313-L4316: Remove the DNS hostname-matching and hostname-resolution behavior. State only that DNS verification is unsupported and maps to IP identity verification.

As per coding guidelines, “Adjust the documentation under ./docs/source/ when changes require documentation updates.”

🤖 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 `@docs/source/topics/security/tls.md` around lines 17 - 21, Update
docs/source/topics/security/tls.md around the SAN setup guidance to require an
iPAddress subject alternative name and clearly mark CN-only and DNS-SAN
certificates as requiring CASS_SSL_VERIFY_PEER_CERT or CASS_SSL_VERIFY_NONE;
also update include/cassandra.h around the DNS verification documentation to
remove hostname matching and resolution claims, stating only that DNS
verification is unsupported and maps to IP identity verification.

Source: Coding guidelines

scylla-rust-wrapper/tests/integration/ccm/tls.rs-365-371 (1)

365-371: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Test the default identity mode.

Lines 365-371 only test an untrusted CA. Chain-only PEER_CERT also rejects that case. In tls_verifies_hostname, test None with ca_pem and the existing mismatched SAN.

As per coding guidelines, **/*{_test.rs,.rs}: Implement Rust unit tests for introduced features and changes.

Also applies to: 398-434

🤖 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 `@scylla-rust-wrapper/tests/integration/ccm/tls.rs` around lines 365 - 371, The
current test for the default identity mode (when `None` is passed) only covers
the untrusted CA rejection case. Add a test case that passes `None` (default
mode) with `ca_pem` (a trusted CA) to verify that the default identity mode
correctly accepts connections with a valid certificate chain. This test should
use the existing mismatched SAN scenario in `tls_verifies_hostname` to ensure
the default behavior handles hostname verification appropriately when a trusted
CA is present, complementing the existing untrusted CA test.

Source: Coding guidelines

🤖 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.

Other comments:
In `@docs/source/topics/security/tls.md`:
- Around line 17-21: Update docs/source/topics/security/tls.md around the SAN
setup guidance to require an iPAddress subject alternative name and clearly mark
CN-only and DNS-SAN certificates as requiring CASS_SSL_VERIFY_PEER_CERT or
CASS_SSL_VERIFY_NONE; also update include/cassandra.h around the DNS
verification documentation to remove hostname matching and resolution claims,
stating only that DNS verification is unsupported and maps to IP identity
verification.

In `@scylla-rust-wrapper/tests/integration/ccm/tls.rs`:
- Around line 365-371: The current test for the default identity mode (when
`None` is passed) only covers the untrusted CA rejection case. Add a test case
that passes `None` (default mode) with `ca_pem` (a trusted CA) to verify that
the default identity mode correctly accepts connections with a valid certificate
chain. This test should use the existing mismatched SAN scenario in
`tls_verifies_hostname` to ensure the default behavior handles hostname
verification appropriately when a trusted CA is present, complementing the
existing untrusted CA test.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: QUIET

Plan: Pro Plus

Run ID: cea8c6c0-f4f6-4c9e-8bcd-2601a547c981

📥 Commits

Reviewing files that changed from the base of the PR and between 8382678 and 58342dd.

📒 Files selected for processing (6)
  • .github/pull_request_template.md
  • Makefile
  • docs/source/topics/security/tls.md
  • include/cassandra.h
  • scylla-rust-wrapper/src/ssl.rs
  • scylla-rust-wrapper/tests/integration/ccm/tls.rs

@wprzytula
wprzytula marked this pull request as draft August 5, 2026 09:49
@coderabbitai coderabbitai Bot mentioned this pull request Aug 5, 2026
4 tasks
CassSslVerifyFlags were duplicated and re-defined in ssl.rs manually.
This commit sets up bindgen to generate the corresponding Rust
definition automatically based on cassandra.h definition.
CASS_SSL_VERIFY_PEER_IDENTITY is now recognized as supported, because
the Rust Driver since Dec '25 (https://github.com/scylladb/scylla-rust-
driver/pull/1491) pins the expected identity to the node's IP on every
connection, so peer verification already implies identity verification.

Note that CPP Driver supported both CN and SAN field-based verification.
Current implementation supports only SAN, which is OK due to CN being
considered obsolete.

The same Rust Driver's change made CASS_SSL_VERIFY_PEER_CERT incorrectly
supported in CPP RS Driver. We add a comment and we'll come back to it
in a next commit.
Rust Driver assumes that hostname verification is always done by IP
address in the SAN field, so the *_VERIFY_PEER_IDENTITY_DNS flag is not
supported. This change maps it to *_VERIFY_PEER_IDENTITY and adds a
warning message when it is used.
@wprzytula wprzytula changed the title Fix TLS verification defaults & test TLS functionality using CCM Fix setting TLS verify Aug 6, 2026
@wprzytula
wprzytula force-pushed the fix-tls-verification branch from 58342dd to 4d62b04 Compare August 6, 2026 09:17
@wprzytula wprzytula removed the area/testing Related to unit/integration testing label Aug 6, 2026
@wprzytula
wprzytula requested a balanced review from Copilot August 6, 2026 09:31
@wprzytula

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Aug 6, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai

coderabbitai Bot commented Aug 6, 2026

Copy link
Copy Markdown

Note

GitHub couldn't provide a complete incremental comparison for this pull request, so CodeRabbit is performing a full review instead. This review may take a little longer.

@wprzytula
wprzytula requested a review from Lorak-mmk August 6, 2026 09:32
@wprzytula
wprzytula marked this pull request as ready for review August 6, 2026 09:32
@wprzytula wprzytula added this to the 1.1.1 milestone Aug 6, 2026
@coderabbitai coderabbitai Bot added the area/testing Related to unit/integration testing label Aug 6, 2026

Copilot AI 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.

🟡 Changes recommended

The TLS guide misstates the security default, and its chain-only default semantics lack direct regression coverage.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

This review doesn't count toward merge requirements. Sign up for the private preview to control whether Copilot approvals count.

Review details

Suppressed comments (3)

docs/source/topics/security/tls.md:163

  • This states that identity verification is the default, but the constructor and public header now default to CASS_SSL_VERIFY_PEER_CERT, which performs chain-only verification. As written, users may incorrectly assume the node IP is checked when they do not call cass_ssl_set_verify_flags(); document the chain-only default and keep identity verification explicit.
**NOTE:** This is enabled by default, as part of the default
`CASS_SSL_VERIFY_PEER_IDENTITY`. The flags form a bitmask, so it can also be
requested explicitly, on its own or combined with `CASS_SSL_VERIFY_PEER_CERT`:

scylla-rust-wrapper/src/ssl.rs:58

  • The security-critical default is only tested without a trusted CA, which fails under both chain-only and identity verification. A regression that drops the default callback and silently makes the default PEER_IDENTITY would therefore pass every current default-mode assertion. Add a verify_flags: None connection using the trusted CA and deliberately mismatched SAN from tls_peer_cert_verifies_chain_only to prove the default has the advertised chain-only semantics.
        SSL_CTX_set_verify(
            ssl_context,
            SslVerifyMode::PEER.bits(),
            Some(verify_peer_cert_callback),
        );

docs/source/topics/security/tls.md:15

  • certficate is misspelled four times in this updated paragraph; use certificate.

This issue also appears on line 161 of the same file.

The most secure method of setting up TLS is to verify that DNS or IP address used to connect to the server matches identity information found in the TLS certificate. This helps to prevent man-in-the-middle attacks. ScyllaDB/Cassandra uses IP addresses internally so those can be used directly for verification (a domain name currently cannot be used via reverse DNS - PTR record). That means that the IP address of the ScyllaDB/Cassandra server where the certficate is installed needs to be present in one of the certficate's subject alternative names (SANs). It's possible to create the certficate without them, but then it will not be possible to verify the server's identity. Although this is not as secure, it eases the deployment of TLS by allowing the same certficate to be deployed across the entire ScyllaDB/Cassandra cluster.
  • Files reviewed: 6/6 changed files
  • Comments generated: 0 new
  • Review effort level: Balanced

We're testing this review assessment. Please use 👍 or 👎 to tell us if it's correct.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Note

Quiet mode is enabled, so only the most important comments were posted inline. Other review comments are grouped below.

🟡 Other comments (1)
scylla-rust-wrapper/tests/integration/ccm/tls.rs-351-370 (1)

351-370: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Test default chain-only verification.

These assertions only prove that an untrusted chain fails. A peer-verification context without verify_peer_cert_callback also fails this case. Add a trusted CA with a mismatched IP SAN and verify_flags = None; assert that the connection succeeds.

As per coding guidelines, "Implement Rust unit tests for features and changes introduced by the patch."

🤖 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 `@scylla-rust-wrapper/tests/integration/ccm/tls.rs` around lines 351 - 370,
Extend the TLS integration coverage around assert_conn_fails and try_tls_connect
to add a trusted CA whose certificate has a mismatched IP SAN, then connect with
verify_flags set to None and assert CassError::CASS_OK. Preserve the existing
untrusted-CA failure assertions while specifically proving default verification
validates the chain without requiring peer identity matching.

Source: Coding guidelines

🧹 Nitpick comments (1)
scylla-rust-wrapper/build.rs (1)

175-175: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚖️ Poor tradeoff

Split binding exposure from SSL behavior. Commit 41f9d1a682543cc4c49f210c9d2dd18a00d5b44e combines both changes.

🤖 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 `@scylla-rust-wrapper/build.rs` at line 175, Separate the binding exposure
change from the SSL behavior change in the build configuration around
prepare_cppdriver_data. Keep CassSslVerifyFlags exposure in its own change and
isolate any SSL behavior modifications into a separate commit or implementation
change.

Source: Coding guidelines

🤖 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 `@docs/source/topics/security/tls.md`:
- Around line 161-175: Align TLS documentation and API comments with runtime
behavior: in docs/source/topics/security/tls.md:161-175, state that the default
is CASS_SSL_VERIFY_PEER_CERT and require explicitly enabling identity
verification for IP checks; in docs/source/topics/security/tls.md:15-19, update
the keytool example to generate only an iPAddress SAN; in
include/cassandra.h:4314-4317, remove hostname/CN/resolution behavior and
document that CASS_SSL_VERIFY_PEER_IDENTITY_DNS maps to IP identity
verification.

---

Other comments:
In `@scylla-rust-wrapper/tests/integration/ccm/tls.rs`:
- Around line 351-370: Extend the TLS integration coverage around
assert_conn_fails and try_tls_connect to add a trusted CA whose certificate has
a mismatched IP SAN, then connect with verify_flags set to None and assert
CassError::CASS_OK. Preserve the existing untrusted-CA failure assertions while
specifically proving default verification validates the chain without requiring
peer identity matching.

---

Nitpick comments:
In `@scylla-rust-wrapper/build.rs`:
- Line 175: Separate the binding exposure change from the SSL behavior change in
the build configuration around prepare_cppdriver_data. Keep CassSslVerifyFlags
exposure in its own change and isolate any SSL behavior modifications into a
separate commit or implementation change.
🪄 Autofix

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: Organization UI

Review profile: QUIET

Plan: Pro Plus

Run ID: 0e833bc9-cad7-4719-9bd9-d6c3a4eb8d1e

📥 Commits

Reviewing files that changed from the base of the PR and between c40975d and 4d62b04.

📒 Files selected for processing (6)
  • docs/source/topics/security/tls.md
  • include/cassandra.h
  • scylla-rust-wrapper/build.rs
  • scylla-rust-wrapper/src/lib.rs
  • scylla-rust-wrapper/src/ssl.rs
  • scylla-rust-wrapper/tests/integration/ccm/tls.rs
🔗 Linked repositories identified

CodeRabbit considers these linked repositories for cross-repo context during reviews:

  • scylladb/scylladb (auto-detected)

Comment thread docs/source/topics/security/tls.md Outdated
@wprzytula
wprzytula force-pushed the fix-tls-verification branch from 4d62b04 to 6c23c59 Compare August 6, 2026 09:38
@coderabbitai

coderabbitai Bot commented Aug 6, 2026

Copy link
Copy Markdown

Note

GitHub couldn't provide a complete incremental comparison for this pull request, so CodeRabbit is performing a full review instead. This review may take a little longer.

Comment thread scylla-rust-wrapper/src/ssl.rs Outdated
Previously any flags value we did not recognise fell through to a catch-
all that enabled SSL_VERIFY_PEER, silently and without a trace. Passing
garbage - or a flag from a newer header - therefore changed the security
settings with no indication that the request was not honoured.

An unrecognised value cannot be honoured, and this API returns void, so
there is no way to report that back to the caller. Leaving the SSL_CTX
untouched would be the wrong direction to fail in: a previously
requested CASS_SSL_VERIFY_NONE would silently stay in effect. The only
thing we control is the resulting security posture, so keep enforcing
the strictest verification we support and log an error saying so.
The C API defines CASS_SSL_VERIFY_PEER_CERT as "certificate is present
and valid", i.e. chain validation without an identity check, in contrast
to CASS_SSL_VERIFY_PEER_IDENTITY which additionally requires the node's
IP to match the certificate.

We could not express that so far, because the Rust driver pins the
expected identity itself, calling `ssl.param_mut().set_ip()` on every
connection right after `Ssl::new()`. Nothing we put in the SSL_CTX can
undo that, so PEER_CERT ended up behaving exactly like PEER_IDENTITY and
we only logged a warning saying so.

Install a verification callback for PEER_CERT instead. OpenSSL reports a
failed identity check to the callback as X509_V_ERR_{HOSTNAME,EMAIL,
IP_ADDRESS}_MISMATCH; accepting precisely those, and resetting the
stored error so that SSL_get_verify_result() reports success too, yields
chain-only verification. Every other failure - an untrusted issuer, an
expired certificate, a broken chain - remains fatal.

The callback is installed only when certificate verification was asked
for without any of the identity bits, which is exactly when the two
differ.

This un-ignores tls_peer_cert_verifies_chain_only, which asserts both
halves: a mismatched IP SAN is tolerated, while an untrusted CA is not.
cass_ssl_new() configured SSL_VERIFY_NONE, so a caller who set up TLS
but never called cass_ssl_set_verify_flags() got an encrypted connection
to an entirely unauthenticated peer. Verify the peer by default instead.

This is in line with the CPP Driver, which documents PEER_CERT as its
default. We have also documented such default, but due to a bug didn't
respect it. Now the bug fix is completed, i.e., the default is actually
set to the declared variant **and** PEER_CERT is now correctly supported
(not accidentally requiring IP verification as before).
`CassSslVerifyFlags` is a bitmask: CASS_SSL_VERIFY_PEER_CERT (0x01),
_PEER_IDENTITY (0x02) and _PEER_IDENTITY_DNS (0x04) are disjoint bits
meant to be combined, and CASS_SSL_VERIFY_NONE (0x00) is the absence of
all of them. The C/C++ driver tests them with `&`, and our own TLS guide
tells users to write

    cass_ssl_set_verify_flags(ssl, CASS_SSL_VERIFY_PEER_CERT |
                                   CASS_SSL_VERIFY_PEER_IDENTITY);

Matching on the flags value as a whole cannot express that: every
combination lands in the catch-all arm. Test the bits instead, and cover
the documented combination in `tls_verifies_hostname` test.
The guide was outdated. While rewriting the section:
- Say that identity is matched against iPAddress subject alternative
  names (SANs) only, and that the common name (CN) is not consulted,
  both where certificates are generated and where verification is
  configured. The guide previously presented CN and SAN as
  interchangeable.
- Document CASS_SSL_VERIFY_PEER_CERT as the way to validate the chain
  without checking the peer's identity, which it now genuinely does.
- Say plainly that CASS_SSL_VERIFY_PEER_IDENTITY_DNS is not supported
  and is treated as CASS_SSL_VERIFY_PEER_IDENTITY, instead of implying
  that enabling reverse DNS makes it work.
@wprzytula
wprzytula force-pushed the fix-tls-verification branch from 6c23c59 to fcc6af6 Compare August 6, 2026 17:49
@coderabbitai

coderabbitai Bot commented Aug 6, 2026

Copy link
Copy Markdown

Note

GitHub couldn't provide a complete incremental comparison for this pull request, so CodeRabbit is performing a full review instead. This review may take a little longer.

@coderabbitai
coderabbitai Bot requested a review from Lorak-mmk August 6, 2026 17:49
@wprzytula
wprzytula merged commit 74a2f2d into scylladb:master Aug 7, 2026
11 of 16 checks passed
@wprzytula
wprzytula deleted the fix-tls-verification branch August 7, 2026 07:08
@wprzytula wprzytula mentioned this pull request Aug 7, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/testing Related to unit/integration testing bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants