Skip to content

Enable RavenDB secured mode for filtered Hub/Sink pull replication#4

Open
Mateosssss wants to merge 6 commits into
mainfrom
secured-replication
Open

Enable RavenDB secured mode for filtered Hub/Sink pull replication#4
Mateosssss wants to merge 6 commits into
mainfrom
secured-replication

Conversation

@Mateosssss

@Mateosssss Mateosssss commented Jun 11, 2026

Copy link
Copy Markdown
Collaborator

Summary

WithFiltering = true on Hub Pull Replication (VerityReplicationHub) requires a secured (TLS) RavenDB server. This PR switches the AppHost from unsecured to secured mode and wires up everything needed for the filtered Hub → Sink pull replication to work end-to-end.

  • Generate a self-signed server certificate (≤4 months, per Developer license limits) and a sink replication certificate; mount both into the RavenDB container and inject the server cert into all .NET clients via Aspire__RavenDB__Client__CertificatePath
  • Bind RavenDB on a single host-mapped port (9534) reachable from both host and container, overriding Aspire's hardcoded 443 endpoint
  • Accept the self-signed cert in all .NET clients via RequestExecutor.RemoteCertificateValidationCallback, restricted to Development (chain errors only)
  • Guard Remote Attachments with try/catch — secured mode enforces a license check on it that was previously skipped silently. (An earlier license-loading bug also caused 003/004 to need similar guards; that bug is now fixed and they were reverted to fail loudly instead.)
  • Pass the server cert to the Sink database client in 005_ConfigureHubSink for authenticated database creation
  • Fix a pre-existing build error in VerityAgentApi (body.UserId does not exist on GenerateAuditRequest)
  • README: document installing the generated client certificate into the Windows certificate store for RavenDB Studio access

This is a focused split-out of the replication/secured-mode work from #3, which mixed in unrelated identity changes. Base is identity so the diff only shows replication-related changes; once #1 merges this can be retargeted to main.

Test plan

  • dotnet build succeeds
  • aspire run — verify RavenDB starts in secured mode, hub/sink replication transfers Companies/Reports documents, and RavenDB Studio is reachable after importing server.pfx

Comment thread src/RavenDB.Samples.Verity.Setup/Migrations/003_ConfigureAi.cs Outdated
Comment thread src/RavenDB.Samples.Verity.Setup/Migrations/003_ConfigureAi.cs Outdated
Comment thread src/RavenDB.Samples.Verity.Setup/Migrations/004_ConfigureQueueEtl.cs Outdated
Comment thread src/RavenDB.Samples.Verity.Setup/Migrations/005_ConfigureHubSink.cs Outdated
Comment thread src/RavenDB.Samples.Verity.Setup/Migrations/005_ConfigureHubSink.cs Outdated
Comment thread src/RavenDB.Samples.Verity.Setup/Program.cs Outdated
Comment thread src/RavenDB.Samples.Verity.AppHost/AppHost.cs Outdated
Comment thread src/RavenDB.Samples.Verity.AppHost/AppHost.cs Outdated

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

Pull request overview

Enables RavenDB secured (TLS) mode in the Aspire AppHost so Hub → Sink pull replication can run with WithFiltering = true, and wires certificates/config needed for end-to-end filtered replication.

Changes:

  • Switch RavenDB from unsecured to secured mode in the AppHost, generating/mounting certificates and overriding the secured endpoint port mapping to 9534.
  • Extend the setup/migrations flow to register Hub access + configure the Sink pull replication task with certificate-based authentication and allowed paths.
  • Apply self-signed cert acceptance for RavenDB client calls across services; fix a pre-existing VerityAgentApi logging/build issue; update README with Studio certificate steps.

Reviewed changes

Copilot reviewed 15 out of 15 changed files in this pull request and generated 9 comments.

Show a summary per file
File Description
src/RavenDB.Samples.Verity.Setup/Program.cs Adds RavenDB TLS cert-chain acceptance and passes new cert-related env vars into migration context.
src/RavenDB.Samples.Verity.Setup/Migrations/MigrationContext.cs Extends migration context with sink certs + server cert path.
src/RavenDB.Samples.Verity.Setup/Migrations/005_ConfigureHubSink.cs Creates Hub with filtering, registers Hub access, creates sink DB, configures sink pull replication with cert + allowed paths.
src/RavenDB.Samples.Verity.Setup/Migrations/004_ConfigureQueueEtl.cs Minor whitespace-only change.
src/RavenDB.Samples.Verity.Setup/Migrations/003_ConfigureAi.cs Formatting/whitespace-only changes.
src/RavenDB.Samples.Verity.Setup/Migrations/001_ConfigureDatabase.cs Wraps Remote Attachments configuration in try/catch with warning output.
src/RavenDB.Samples.Verity.Setup/Constants.cs Adds env var names for sink certs + server cert path.
src/RavenDB.Samples.Verity.Model/HubSinks/VerityReplicationSink.cs Adds access name + allowed paths; removes old Create() helper.
src/RavenDB.Samples.Verity.Model/HubSinks/VerityReplicationHub.cs Enables WithFiltering = true.
src/RavenDB.Samples.Verity.IdentityServer/Program.cs Adds RavenDB TLS cert-chain acceptance.
src/RavenDB.Samples.Verity.DataSubscriptionsApp/Program.cs Adds RavenDB TLS cert-chain acceptance.
src/RavenDB.Samples.Verity.AppHost/AppHost.cs Generates certs, configures secured RavenDB + port override, injects cert path into dependent services.
src/RavenDB.Samples.Verity.App/Program.cs Adds RavenDB TLS cert-chain acceptance.
src/RavenDB.Samples.Verity.App/Infrastructure/VerityAgentApi.cs Fixes logging to use user.Id instead of nonexistent body.UserId.
README.md Documents importing the generated PFX for RavenDB Studio access.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/RavenDB.Samples.Verity.Setup/Migrations/005_ConfigureHubSink.cs Outdated
Comment thread src/RavenDB.Samples.Verity.Setup/Program.cs Outdated
Comment thread src/RavenDB.Samples.Verity.App/Program.cs Outdated
Comment on lines +9 to +11
// Accept self-signed server certs — chain errors only; name and revocation checks still apply.
RequestExecutor.RemoteCertificateValidationCallback +=
(_, _, _, errors) => (errors & ~SslPolicyErrors.RemoteCertificateChainErrors) == SslPolicyErrors.None;

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Fixed in 677aff6 — gated behind DOTNET_ENVIRONMENT == "Development".

Comment thread src/RavenDB.Samples.Verity.IdentityServer/Program.cs Outdated
Comment thread src/RavenDB.Samples.Verity.AppHost/AppHost.cs Outdated
Comment thread src/RavenDB.Samples.Verity.AppHost/AppHost.cs Outdated
Comment thread src/RavenDB.Samples.Verity.Setup/Migrations/003_ConfigureAi.cs

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

Pull request overview

Copilot reviewed 15 out of 15 changed files in this pull request and generated 3 comments.

Comment thread src/RavenDB.Samples.Verity.Setup/Migrations/005_ConfigureHubSink.cs Outdated
Comment thread src/RavenDB.Samples.Verity.Setup/Migrations/005_ConfigureHubSink.cs
Comment thread src/RavenDB.Samples.Verity.Setup/Program.cs Outdated
@Scooletz

Copy link
Copy Markdown
Collaborator

When pulled and trying to run this is what I receive:

image
Waiting for resource ready to execute for 'RavenDB'.

                                Finished waiting for resource 'RavenDB'.

                                Finished waiting for resource 'RavenDB'.

                                Finished waiting for resource 'Verity-sink'.

                                 Unhandled exception. System.Net.Http.HttpRequestException: The SSL connection could not be established, see inner exception.

                                  ---> System.Security.Authentication.AuthenticationException: The remote certificate was rejected by the provided RemoteCertificateValidationCallback.

                                    at System.Net.Security.SslStream.CompleteHandshake(SslAuthenticationOptions sslAuthenticationOptions)

                                    at System.Net.Security.SslStream.ForceAuthenticationAsync[TIOAdapter](Boolean receiveFirst, Byte[] reAuthenticationData, CancellationToken cancellationToken)

                                    at System.Net.Http.ConnectHelper.EstablishSslConnectionAsync(SslClientAuthenticationOptions sslOptions, HttpRequestMessage request, Boolean async, Stream stream, CancellationToken cancellationToken)

                                    --- End of inner exception stack trace ---

                                    at System.Net.Http.ConnectHelper.EstablishSslConnectionAsync(SslClientAuthenticationOptions sslOptions, HttpRequestMessage request, Boolean async, Stream stream, CancellationToken cancellationToken)

                                    at System.Net.Http.HttpConnectionPool.ConnectAsync(HttpRequestMessage request, Boolean async, CancellationToken cancellationToken)

                                    at System.Net.Http.HttpConnectionPool.InjectNewHttp2ConnectionAsync(QueueItem queueItem)

                                    at System.Threading.Tasks.TaskCompletionSourceWithCancellation`1.WaitWithCancellationAsync(CancellationToken cancellationToken)

                                    at System.Net.Http.HttpConnectionPool.SendWithVersionDetectionAndRetryAsync(HttpRequestMessage request, Boolean async, Boolean doRequestAuth, CancellationToken cancellationToken)

                                    at System.Net.Http.RedirectHandler.SendAsync(HttpRequestMessage request, Boolean async, CancellationToken cancellationToken)

                                    at System.Net.Http.DecompressionHandler.SendAsync(HttpRequestMessage request, Boolean async, CancellationToken cancellationToken)

                                    at System.Net.Http.SocketsHttpHandler.<SendAsync>g__CreateHandlerAndSendAsync|115_0(HttpRequestMessage request, CancellationToken cancellationToken)

                                    at System.Net.Http.HttpClient.<SendAsync>g__Core|83_0(HttpRequestMessage request, HttpCompletionOption completionOption, CancellationTokenSource cts, Boolean disposeCts, CancellationTokenSource pendingRequestsCts, CancellationToken originalCancellationToken)

                                    at Raven.Client.Http.RavenCommand`1.SendAsync(HttpClient client, HttpRequestMessage request, CancellationToken token)

                                    at Raven.Client.Http.RavenCommand`1.SendAsync(HttpClient client, HttpRequestMessage request, CancellationToken token)

                                    at Raven.Client.Http.RequestExecutor.SendAsync[TResult](ServerNode chosenNode, RavenCommand`1 command, SessionInfo sessionInfo, HttpRequestMessage request, CancellationToken token)

                                    at Raven.Client.Http.RequestExecutor.SendRequestToServer[TResult](ServerNode chosenNode, Nullable`1 nodeIndex, JsonOperationContext context, RavenCommand`1 command, Boolean shouldRetry, SessionInfo sessionInfo, HttpRequestMessage request, String url, CancellationToken token)

                                    at Raven.Client.Http.RequestExecutor.SendRequestToServer[TResult](ServerNode chosenNode, Nullable`1 nodeIndex, JsonOperationContext context, RavenCommand`1 command, Boolean shouldRetry, SessionInfo sessionInfo, HttpRequestMessage request, String url, CancellationToken token)

                                    at Raven.Client.Http.RequestExecutor.ExecuteAsync[TResult](ServerNode chosenNode, Nullable`1 nodeIndex, JsonOperationContext context, RavenCommand`1 command, Boolean shouldRetry, SessionInfo sessionInfo, CancellationToken token)

                                    at Raven.Client.Http.ClusterRequestExecutor.UpdateTopologyAsync(UpdateTopologyParameters parameters)

WithFiltering = true on Hub Pull Replication requires a secured (TLS)
RavenDB server, so the AppHost now runs RavenDB in secured mode:

- Generate a self-signed server certificate (<=4 months, per Developer
  license limits) and a sink replication certificate; both are mounted
  into the RavenDB container and injected into clients via
  Aspire__RavenDB__Client__CertificatePath
- Bind RavenDB on a single host-mapped port (9534) reachable from both
  host and container, overriding Aspire's hardcoded 443 endpoint
- Accept the self-signed cert in all .NET clients via
  RequestExecutor.RemoteCertificateValidationCallback
- Guard Enterprise-only features (Remote Attachments, Time Series,
  AI Agent, GenAI tasks, Queue ETL) with try/catch, since secured mode
  now enforces license checks previously skipped silently
- Pass the server cert to the Sink database client in
  005_ConfigureHubSink for authenticated database creation
- Fix a pre-existing build error in VerityAgentApi (body.UserId does
  not exist on GenerateAuditRequest)
- Document installing the client certificate for RavenDB Studio access
  in README
…o English

Replace inline env var name string literals in AppHost.cs with named
consts (matching the existing envXxx pattern) and translate Polish
migration comments in 005_ConfigureHubSink.cs to English.
License-guard catches were added defensively but the project requires a
Developer license, so these operations always succeed. Letting failures
propagate makes startup failures visible rather than silently skipped.
…o dev, fix port comment

- 005_ConfigureHubSink: guard RegisterReplicationHubAccessOperation with the same
  kind of try/catch already used for hub/database creation, so rerunning the
  migration against an existing access entry doesn't fail
- Gate RequestExecutor.RemoteCertificateValidationCallback behind a Development
  check in all 5 entry points (Setup, App, DataSubscriptionsApp, IdentityServer,
  AppHost) so TLS chain validation is never relaxed outside local dev
- Fix a stale comment in AppHost.cs claiming Hub/Sink share port 443 — the actual
  port is overridden to 9534 via the EndpointAnnotation TargetPort above
…emove unused using

- 005_ConfigureHubSink: throw on empty SinkCertPublicBase64/SinkCertPfxBase64
- 005_ConfigureHubSink: make ServerCertPath mandatory in secured mode
- Setup/Program.cs: remove unused X509Certificates using
@Mateosssss Mateosssss force-pushed the secured-replication branch from 2cc801c to d3315d2 Compare July 6, 2026 07:41
@Mateosssss Mateosssss changed the base branch from identity to main July 6, 2026 07:45
@Scooletz Scooletz self-requested a review July 6, 2026 14:08

@Scooletz Scooletz left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Right now, the database is not accessible via Aspire dashboard. Is it because of the certificate? As this is aspire based, maybe there's the option to leverage the aspire certificate? https://aspire.dev/app-host/certificate-configuration/#use-the-development-certificate

I think we should be able to visit the database as sometimes you need to show what is inside.

Cert is now reused across AppHost runs (regenerated only when missing or
expiring) and auto-installed into the Windows CurrentUser\My store on
startup. Chrome then offers it as the mTLS client certificate when
RavenDB Studio requests authentication, so the Aspire dashboard link
works with a single click-through for the self-signed server cert.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants