Commit b816bf29 authored by Marc Gravell's avatar Marc Gravell

add new helper API to make it easy to trust issuers; support env-based issuer paths

parent 42e44b92
INDENT=' '
echo "Starting Redis servers for testing..."
echo Starting Redis servers for testing...
#Basic Servers
echo "Starting Basic: 6379-6382"
echo Starting Basic: 6379-6382
pushd Basic > /dev/null
echo "${INDENT}Master: 6379"
echo Master: 6379
redis-server master-6379.conf &>/dev/null &
echo "${INDENT}Slave: 6380"
echo Slave: 6380
redis-server slave-6380.conf &>/dev/null &
echo "${INDENT}Secure: 6381"
echo Secure: 6381
redis-server secure-6381.conf &>/dev/null &
popd > /dev/null
#Failover Servers
echo Starting Failover: 6382-6383
pushd Failover > /dev/null
echo "${INDENT}Master: 6382"
echo Master: 6382
redis-server master-6382.conf &>/dev/null &
echo "${INDENT}Slave: 6383"
echo Slave: 6383
redis-server slave-6383.conf &>/dev/null &
popd > /dev/null
......@@ -35,10 +34,10 @@ popd > /dev/null
#Sentinel Servers
echo Starting Sentinel: 7010-7011,26379-26380
pushd Sentinel > /dev/null
echo "${INDENT}Targets: 7010-7011"
echo Targets: 7010-7011
redis-server redis-7010.conf &>/dev/null &
redis-server redis-7011.conf &>/dev/null &
echo "${INDENT}Monitors: 26379-26380"
echo Monitors: 26379-26380
redis-server sentinel-26379.conf --sentinel &>/dev/null &
redis-server sentinel-26380.conf --sentinel &>/dev/null &
popd > /dev/null
......
......@@ -173,6 +173,11 @@ public void RedisLabsSSL()
Skip.IfNoConfig(nameof(TestConfig.Config.RedisLabsSslServer), TestConfig.Current.RedisLabsSslServer);
Skip.IfNoConfig(nameof(TestConfig.Config.RedisLabsPfxPath), TestConfig.Current.RedisLabsPfxPath);
var cert = new X509Certificate2(TestConfig.Current.RedisLabsPfxPath, "");
Assert.NotNull(cert);
Writer.WriteLine("Thumbprint: " + cert.Thumbprint);
int timeout = 5000;
if (Debugger.IsAttached) timeout *= 100;
var options = new ConfigurationOptions
......@@ -184,6 +189,11 @@ public void RedisLabsSSL()
"subscribe", "unsubscribe", "cluster"
}, false)
};
options.CertificateValidation += ConfigurationOptions.TrustIssuer("redislabs_ca.pem");
if (!Directory.Exists(Me())) Directory.CreateDirectory(Me());
#if LOGOUTPUT
ConnectionMultiplexer.EchoPath = Me();
......@@ -191,7 +201,7 @@ public void RedisLabsSSL()
options.Ssl = true;
options.CertificateSelection += delegate
{
return new X509Certificate2(TestConfig.Current.RedisLabsPfxPath, "");
return cert;
};
RedisKey key = Me();
using (var conn = ConnectionMultiplexer.Connect(options))
......@@ -227,6 +237,10 @@ public void RedisLabsEnvironmentVariableClientCertificate(bool setEnv)
if (setEnv)
{
Environment.SetEnvironmentVariable("SERedis_ClientCertPfxPath", TestConfig.Current.RedisLabsPfxPath);
Environment.SetEnvironmentVariable("SERedis_IssuerCertPath", "redislabs_ca.pem");
// check env worked
Assert.Equal(TestConfig.Current.RedisLabsPfxPath, Environment.GetEnvironmentVariable("SERedis_ClientCertPfxPath"));
Assert.Equal("redislabs_ca.pem", Environment.GetEnvironmentVariable("SERedis_IssuerCertPath"));
}
int timeout = 5000;
if (Debugger.IsAttached) timeout *= 100;
......@@ -239,6 +253,7 @@ public void RedisLabsEnvironmentVariableClientCertificate(bool setEnv)
"subscribe", "unsubscribe", "cluster"
}, false)
};
if (!Directory.Exists(Me())) Directory.CreateDirectory(Me());
#if LOGOUTPUT
ConnectionMultiplexer.EchoPath = Me();
......
......@@ -29,4 +29,10 @@
<ItemGroup Condition=" '$(TargetFramework)' == 'net462' ">
<Reference Include="Microsoft.CSharp" />
</ItemGroup>
<ItemGroup>
<None Update="redislabs_ca.pem">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
</Project>
Certificate:
Data:
Version: 3 (0x2)
Serial Number: 11859567854091286320 (0xa495a620ecc0b730)
Signature Algorithm: sha1WithRSAEncryption
Issuer: O=Garantia Data, CN=SSL Certification Authority
Validity
Not Before: Oct 1 12:14:55 2013 GMT
Not After : Sep 29 12:14:55 2023 GMT
Subject: O=Garantia Data, CN=SSL Certification Authority
Subject Public Key Info:
Public Key Algorithm: rsaEncryption
Public-Key: (2048 bit)
Modulus:
00:b6:6a:92:1f:c3:73:35:8f:26:7c:67:1c:b4:3b:
40:bd:13:e0:1e:02:0c:a5:81:28:27:22:b2:b8:86:
6c:0e:99:78:f5:95:36:8e:21:7c:a4:02:e8:9a:f3:
7d:1f:b4:f3:53:5e:0f:a5:5c:59:48:b3:ae:67:7e:
8e:d3:e1:21:8e:1c:f9:65:50:62:6e:4f:29:a3:7a:
0d:3d:62:99:87:71:43:0e:da:a8:ee:63:d8:a5:02:
12:1f:dc:ce:7a:4b:c5:e4:87:a1:3c:65:47:7e:04:
43:01:76:f1:69:77:7a:0d:af:73:97:2d:f0:b8:d4:
dd:ea:33:59:59:37:81:be:da:97:1f:66:48:0d:92:
82:6b:97:e6:51:10:6b:09:7e:fa:b4:a3:b0:14:ad:
7a:66:36:04:3c:0e:a4:03:17:22:b7:44:c8:ff:dc:
56:7f:26:92:f8:bf:04:3b:39:33:91:be:d3:d8:f4:
81:f8:72:0b:34:56:31:0e:c7:9f:bd:6e:d5:ea:25:
47:1c:15:c6:08:b7:4c:c9:fe:fe:f4:da:15:2a:b1:
2a:38:1c:93:ac:ee:01:88:c1:44:f6:87:7b:ba:8b:
c4:73:6b:d5:2a:3f:31:cf:67:3f:2f:b7:c0:77:9b:
17:06:c8:72:75:28:8f:06:e9:e2:77:2d:91:66:e3:
6f:67
Exponent: 65537 (0x10001)
X509v3 extensions:
X509v3 Subject Key Identifier:
FD:70:86:D7:2B:C9:D9:96:DD:92:5E:B9:2A:0A:64:82:A3:CD:ED:F0
X509v3 Authority Key Identifier:
keyid:FD:70:86:D7:2B:C9:D9:96:DD:92:5E:B9:2A:0A:64:82:A3:CD:ED:F0
X509v3 Basic Constraints:
CA:TRUE
Signature Algorithm: sha1WithRSAEncryption
6d:9e:ad:78:70:44:06:bb:f9:93:81:b3:40:7a:5f:9e:c7:c3:
27:75:47:89:1f:99:77:2c:d2:bb:5a:95:b3:e9:be:05:0b:4a:
20:7e:4c:26:df:dc:46:e1:26:71:c6:ca:f7:42:63:5b:6f:95:
f7:cb:8d:d0:3b:1c:9d:0f:08:e9:fe:61:82:c1:03:4a:53:53:
f7:72:be:b3:7a:4a:ef:0d:b9:2e:72:b9:b9:ed:f6:66:f5:de:
70:c6:62:8d:6b:9e:dd:18:45:fc:4d:fb:c0:cc:dd:f5:c8:56:
bd:37:f0:0d:f4:52:53:d7:d8:eb:b5:13:11:49:4f:43:19:b8:
52:98:e9:9b:cb:74:8e:bf:d5:c6:e0:9a:0b:8c:94:08:4c:f8:
38:4a:c9:5e:92:af:9e:bd:f4:b3:37:ce:a7:88:f3:5e:a9:66:
69:51:10:44:d8:90:6a:fd:d6:ae:e4:06:95:c9:bb:f7:6d:1d:
a1:b1:83:56:46:bb:ac:3f:3c:2b:18:19:47:04:09:61:0d:60:
3e:15:40:f7:7c:37:7d:89:8c:e7:ee:ea:f1:20:a0:40:30:7c:
f3:fe:de:81:a9:67:89:b7:7b:00:02:71:63:80:7a:7a:9f:95:
bf:9c:41:80:b8:3e:c1:7b:a9:b5:c3:99:16:96:ad:b2:a7:b4:
e9:59:de:7d
-----BEGIN CERTIFICATE-----
MIIDTzCCAjegAwIBAgIJAKSVpiDswLcwMA0GCSqGSIb3DQEBBQUAMD4xFjAUBgNV
BAoMDUdhcmFudGlhIERhdGExJDAiBgNVBAMMG1NTTCBDZXJ0aWZpY2F0aW9uIEF1
dGhvcml0eTAeFw0xMzEwMDExMjE0NTVaFw0yMzA5MjkxMjE0NTVaMD4xFjAUBgNV
BAoMDUdhcmFudGlhIERhdGExJDAiBgNVBAMMG1NTTCBDZXJ0aWZpY2F0aW9uIEF1
dGhvcml0eTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALZqkh/DczWP
JnxnHLQ7QL0T4B4CDKWBKCcisriGbA6ZePWVNo4hfKQC6JrzfR+081NeD6VcWUiz
rmd+jtPhIY4c+WVQYm5PKaN6DT1imYdxQw7aqO5j2KUCEh/cznpLxeSHoTxlR34E
QwF28Wl3eg2vc5ct8LjU3eozWVk3gb7alx9mSA2SgmuX5lEQawl++rSjsBStemY2
BDwOpAMXIrdEyP/cVn8mkvi/BDs5M5G+09j0gfhyCzRWMQ7Hn71u1eolRxwVxgi3
TMn+/vTaFSqxKjgck6zuAYjBRPaHe7qLxHNr1So/Mc9nPy+3wHebFwbIcnUojwbp
4nctkWbjb2cCAwEAAaNQME4wHQYDVR0OBBYEFP1whtcrydmW3ZJeuSoKZIKjze3w
MB8GA1UdIwQYMBaAFP1whtcrydmW3ZJeuSoKZIKjze3wMAwGA1UdEwQFMAMBAf8w
DQYJKoZIhvcNAQEFBQADggEBAG2erXhwRAa7+ZOBs0B6X57Hwyd1R4kfmXcs0rta
lbPpvgULSiB+TCbf3EbhJnHGyvdCY1tvlffLjdA7HJ0PCOn+YYLBA0pTU/dyvrN6
Su8NuS5yubnt9mb13nDGYo1rnt0YRfxN+8DM3fXIVr038A30UlPX2Ou1ExFJT0MZ
uFKY6ZvLdI6/1cbgmguMlAhM+DhKyV6Sr5699LM3zqeI816pZmlREETYkGr91q7k
BpXJu/dtHaGxg1ZGu6w/PCsYGUcECWENYD4VQPd8N32JjOfu6vEgoEAwfPP+3oGp
Z4m3ewACcWOAenqflb+cQYC4PsF7qbXDmRaWrbKntOlZ3n0=
-----END CERTIFICATE-----
......@@ -6,6 +6,7 @@
using System.Net;
using System.Net.Security;
using System.Security.Authentication;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using System.Threading.Tasks;
......@@ -175,6 +176,38 @@ public static string TryNormalize(string value)
/// </summary>
public RedisChannel ChannelPrefix { get; set; }
/// <summary>
/// Create a certificate validation check that checks against the supplied issuer even if not known by the machine
/// </summary>
public static RemoteCertificateValidationCallback TrustIssuer(string issuerCertificatePath)
=> TrustIssuer(new X509Certificate2(issuerCertificatePath));
/// <summary>
/// Create a certificate validation check that checks against the supplied issuer even if not known by the machine
/// </summary>
public static RemoteCertificateValidationCallback TrustIssuer(X509Certificate2 issuer)
{
if (issuer == null) throw new ArgumentNullException(nameof(issuer));
return (object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyError)
=> sslPolicyError == SslPolicyErrors.RemoteCertificateChainErrors && certificate is X509Certificate2 v2
&& CheckTrustedIssuer(v2, issuer);
}
static bool CheckTrustedIssuer(X509Certificate2 certificateToValidate, X509Certificate2 authority)
{
// reference: https://stackoverflow.com/questions/6497040/how-do-i-validate-that-a-certificate-was-created-by-a-particular-certification-a
X509Chain chain = new X509Chain();
chain.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck;
chain.ChainPolicy.RevocationFlag = X509RevocationFlag.ExcludeRoot;
chain.ChainPolicy.VerificationFlags = X509VerificationFlags.AllowUnknownCertificateAuthority;
chain.ChainPolicy.VerificationTime = DateTime.Now;
chain.ChainPolicy.UrlRetrievalTimeout = new TimeSpan(0, 0, 0);
chain.ChainPolicy.ExtraStore.Add(authority);
return chain.Build(certificateToValidate);
}
/// <summary>
/// The client name to use for all connections
/// </summary>
......
......@@ -944,7 +944,20 @@ private static void WriteUnified(PipeWriter writer, long value)
internal int GetAvailableInboundBytes() => _socket?.Available ?? 0;
private static LocalCertificateSelectionCallback GetAmbientCertificateCallback()
private RemoteCertificateValidationCallback GetAmbientIssuerCertificateCallback()
{
try
{
var issuerPath = Environment.GetEnvironmentVariable("SERedis_IssuerCertPath");
if (!string.IsNullOrEmpty(issuerPath)) return ConfigurationOptions.TrustIssuer(issuerPath);
}
catch (Exception ex)
{
Debug.WriteLine(ex.Message);
}
return null;
}
private static LocalCertificateSelectionCallback GetAmbientClientCertificateCallback()
{
try
{
......@@ -963,8 +976,10 @@ private static LocalCertificateSelectionCallback GetAmbientCertificateCallback()
return delegate { return new X509Certificate2(pfxPath, pfxPassword ?? "", flags ?? X509KeyStorageFlags.DefaultKeySet); };
}
}
catch
{ }
catch (Exception ex)
{
Debug.WriteLine(ex.Message);
}
return null;
}
......@@ -987,8 +1002,9 @@ internal async ValueTask<bool> ConnectedAsync(Socket socket, TextWriter log, Soc
var host = config.SslHost;
if (string.IsNullOrWhiteSpace(host)) host = Format.ToStringHostOnly(Bridge.ServerEndPoint.EndPoint);
var ssl = new SslStream(new NetworkStream(socket), false, config.CertificateValidationCallback,
config.CertificateSelectionCallback ?? GetAmbientCertificateCallback(),
var ssl = new SslStream(new NetworkStream(socket), false,
config.CertificateValidationCallback ?? GetAmbientIssuerCertificateCallback(),
config.CertificateSelectionCallback ?? GetAmbientClientCertificateCallback(),
EncryptionPolicy.RequireEncryption);
try
{
......@@ -996,8 +1012,9 @@ internal async ValueTask<bool> ConnectedAsync(Socket socket, TextWriter log, Soc
{
ssl.AuthenticateAsClient(host, config.SslProtocols);
}
catch
catch(Exception ex)
{
Debug.WriteLine(ex.Message);
Multiplexer?.SetAuthSuspect();
throw;
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment