Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
- Fixed bug: public key presented not for certificate signature.
- Fixed: YdbDataReader does not throw YdbException when CloseAsync is called for UPDATE/INSERT statements with no
result.

Expand Down
15 changes: 15 additions & 0 deletions examples/src/YC/CmdOptions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
using CommandLine;

namespace YcCloud;

internal class CmdOptions
{
[Option('h', "host", Required = true, HelpText = "Database host")]
public string Host { get; set; } = null!;

[Option('d', "database", Required = true, HelpText = "Database name")]
public string Database { get; set; } = null!;

[Option("saFilePath", Required = true, HelpText = "Sa Key")]
public string SaFilePath { get; set; } = null!;
}
30 changes: 30 additions & 0 deletions examples/src/YC/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
using CommandLine;
using Microsoft.Extensions.Logging;
using YcCloud;
using Ydb.Sdk.Ado;
using Ydb.Sdk.Yc;

await Parser.Default.ParseArguments<CmdOptions>(args).WithParsedAsync(async cmd =>
{
var loggerFactory = LoggerFactory.Create(builder => builder.AddConsole().SetMinimumLevel(LogLevel.Information));

var saProvider = new ServiceAccountProvider(saFilePath: cmd.SaFilePath, loggerFactory: loggerFactory);
await saProvider.Initialize();

var builder = new YdbConnectionStringBuilder
{
UseTls = true,
Host = cmd.Host,
Port = 2135,
Database = cmd.Database,
CredentialsProvider = saProvider,
LoggerFactory = loggerFactory,
ServerCertificates = YcCerts.GetYcServerCertificates()
};

await using var ydbConnection = new YdbConnection(builder);
await ydbConnection.OpenAsync();

Console.WriteLine(await new YdbCommand(ydbConnection) { CommandText = "SELECT 'Hello Dedicated YDB!'u" }
.ExecuteScalarAsync());
});
21 changes: 21 additions & 0 deletions examples/src/YC/YC.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<RootNamespace>YcCloud</RootNamespace>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="CommandLineParser" Version="2.8.0" />
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="5.0.0" />
<PackageReference Include="Microsoft.Extensions.Logging" Version="5.0.0" />
<PackageReference Include="Ydb.Protos" Version="1.1.1" />
<PackageReference Include="Ydb.Sdk.Yc.Auth" Version="0.1.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\src\Ydb.Sdk\src\Ydb.Sdk.csproj" />
</ItemGroup>
</Project>
6 changes: 6 additions & 0 deletions examples/src/YdbExamples.sln
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AdoNet", "AdoNet\AdoNet.csp
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DapperExample", "DapperExample\DapperExample.csproj", "{AC8F1B10-31EB-4A29-BF35-7F49B06E1FA8}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "YC", "YC\YC.csproj", "{753E4F33-CB08-47B9-864F-4CC037B278C4}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand All @@ -39,6 +41,10 @@ Global
{AC8F1B10-31EB-4A29-BF35-7F49B06E1FA8}.Debug|Any CPU.Build.0 = Debug|Any CPU
{AC8F1B10-31EB-4A29-BF35-7F49B06E1FA8}.Release|Any CPU.ActiveCfg = Release|Any CPU
{AC8F1B10-31EB-4A29-BF35-7F49B06E1FA8}.Release|Any CPU.Build.0 = Release|Any CPU
{753E4F33-CB08-47B9-864F-4CC037B278C4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{753E4F33-CB08-47B9-864F-4CC037B278C4}.Debug|Any CPU.Build.0 = Debug|Any CPU
{753E4F33-CB08-47B9-864F-4CC037B278C4}.Release|Any CPU.ActiveCfg = Release|Any CPU
{753E4F33-CB08-47B9-864F-4CC037B278C4}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down
9 changes: 6 additions & 3 deletions src/Ydb.Sdk/src/Ado/YdbConnectionStringBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -137,9 +137,11 @@ public string? RootCertificate

private string? _rootCertificate;

public ILoggerFactory? LoggerFactory { get; set; }
public ILoggerFactory? LoggerFactory { get; init; }

public ICredentialsProvider? CredentialsProvider { get; set; }
public ICredentialsProvider? CredentialsProvider { get; init; }

public X509Certificate2Collection? ServerCertificates { get; init; }

private void SaveValue(string propertyName, object? value)
{
Expand Down Expand Up @@ -189,7 +191,8 @@ internal Task<Driver> BuildDriver()
endpoint: Endpoint,
database: Database,
credentials: credentialsProvider,
customServerCertificate: cert
customServerCertificate: cert,
customServerCertificates: ServerCertificates
), LoggerFactory);
}

Expand Down
16 changes: 13 additions & 3 deletions src/Ydb.Sdk/src/DriverConfig.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,21 +8,31 @@ public class DriverConfig
public string Endpoint { get; }
public string Database { get; }
public ICredentialsProvider Credentials { get; }
public X509Certificate? CustomServerCertificate { get; }

internal X509Certificate2Collection CustomServerCertificates { get; } = new();
internal TimeSpan EndpointDiscoveryInterval = TimeSpan.FromMinutes(1);
internal TimeSpan EndpointDiscoveryTimeout = TimeSpan.FromSeconds(10);

public DriverConfig(
string endpoint,
string database,
ICredentialsProvider? credentials = null,
X509Certificate? customServerCertificate = null)
X509Certificate? customServerCertificate = null,
X509Certificate2Collection? customServerCertificates = null)
{
Endpoint = FormatEndpoint(endpoint);
Database = database;
Credentials = credentials ?? new AnonymousProvider();
CustomServerCertificate = customServerCertificate;

if (customServerCertificate != null)
{
CustomServerCertificates.Add(new X509Certificate2(customServerCertificate));
}

if (customServerCertificates != null)
{
CustomServerCertificates.AddRange(customServerCertificates);
}
}

private static string FormatEndpoint(string endpoint)
Expand Down
40 changes: 20 additions & 20 deletions src/Ydb.Sdk/src/Pool/ChannelPool.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
using Grpc.Core;
using Grpc.Net.Client;
using Microsoft.Extensions.Logging;
using Org.BouncyCastle.Security;

namespace Ydb.Sdk.Pool;

Expand Down Expand Up @@ -79,14 +78,14 @@ public interface IChannelFactory<out T> where T : ChannelBase, IDisposable
internal class GrpcChannelFactory : IChannelFactory<GrpcChannel>
{
private readonly ILoggerFactory _loggerFactory;
private readonly X509Certificate? _x509Certificate;
private readonly ILogger<GrpcChannelFactory> _logger;
private readonly X509Certificate2Collection _x509Certificate2Collection;

internal GrpcChannelFactory(ILoggerFactory loggerFactory, DriverConfig config)
{
_loggerFactory = loggerFactory;
_x509Certificate = config.CustomServerCertificate;
_logger = loggerFactory.CreateLogger<GrpcChannelFactory>();
_x509Certificate2Collection = config.CustomServerCertificates;
}

public GrpcChannel CreateChannel(string endpoint)
Expand All @@ -98,47 +97,48 @@ public GrpcChannel CreateChannel(string endpoint)
LoggerFactory = _loggerFactory
};

if (_x509Certificate == null)
var httpHandler = new SocketsHttpHandler();

// https://github.com/grpc/grpc-dotnet/issues/2312#issuecomment-1790661801
httpHandler.Properties["__GrpcLoadBalancingDisabled"] = true;

channelOptions.HttpHandler = httpHandler;
channelOptions.DisposeHttpClient = true;

if (_x509Certificate2Collection.Count == 0)
{
return GrpcChannel.ForAddress(endpoint, channelOptions);
}

var httpHandler = new SocketsHttpHandler();

var customCertificate = DotNetUtilities.FromX509Certificate(_x509Certificate);

httpHandler.SslOptions.RemoteCertificateValidationCallback =
(_, certificate, _, sslPolicyErrors) =>
httpHandler.SslOptions.RemoteCertificateValidationCallback +=
(_, certificate, chain, sslPolicyErrors) =>
{
if (sslPolicyErrors == SslPolicyErrors.None)
{
return true;
}

if (certificate is null)
if (certificate is null || chain is null)
{
return false;
}

try
{
var cert = DotNetUtilities.FromX509Certificate(certificate);
cert.Verify(customCertificate.GetPublicKey());
chain.ChainPolicy.VerificationFlags = X509VerificationFlags.AllowUnknownCertificateAuthority;
chain.ChainPolicy.ExtraStore.AddRange(_x509Certificate2Collection);

return chain.Build(new X509Certificate2(certificate)) && chain.ChainElements.Any(chainElement =>
_x509Certificate2Collection.Any(trustedCert =>
chainElement.Certificate.Thumbprint == trustedCert.Thumbprint));
}
catch (Exception e)
{
_logger.LogError(e, "Failed to verify remote certificate!");

return false;
}

return true;
};
// https://github.com/grpc/grpc-dotnet/issues/2312#issuecomment-1790661801
httpHandler.Properties["__GrpcLoadBalancingDisabled"] = true;

channelOptions.HttpHandler = httpHandler;
channelOptions.DisposeHttpClient = true;

return GrpcChannel.ForAddress(endpoint, channelOptions);
}
Expand Down
2 changes: 1 addition & 1 deletion src/Ydb.Sdk/src/Transport/AuthGrpcChannelDriver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ ILoggerFactory loggerFactory
new DriverConfig(
endpoint: driverConfig.Endpoint,
database: driverConfig.Database,
customServerCertificate: driverConfig.CustomServerCertificate
customServerCertificates: driverConfig.CustomServerCertificates
), loggerFactory, loggerFactory.CreateLogger<AuthGrpcChannelDriver>())
{
_channel = grpcChannelFactory.CreateChannel(Config.Endpoint);
Expand Down
Loading