Skip to content

Redis Cloud Connection Example #98

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 44 commits into from
May 2, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
61c8103
TestRedisCloudConnection
shacharPash Mar 23, 2023
b161901
using .env for the enviroment vars
shacharPash Mar 27, 2023
cb23cbc
Merge branch 'master' into Issue96/TlsExample
shacharPash Mar 27, 2023
eb6747e
DotEnv class
shacharPash Mar 27, 2023
955efac
delete 'all' var
shacharPash Mar 27, 2023
742f238
add env vars to yml
shacharPash Mar 27, 2023
4d14627
delete DotEnv class
shacharPash Mar 27, 2023
7228065
test TLS Connecting in .NET Core
shacharPash Mar 29, 2023
e6d4601
add to gitignor
shacharPash Apr 2, 2023
482fae9
Experiments
shacharPash Apr 4, 2023
24a9df4
adding client cert parsing + root CA validation
slorello89 Apr 4, 2023
ca3567d
Merge branch 'master' into Issue96/TlsExample
shacharPash Apr 13, 2023
ba36fad
using env vars
shacharPash Apr 19, 2023
dbd4df9
add vars to integration
shacharPash Apr 19, 2023
46d638d
change to Convert.FromHexString
shacharPash Apr 19, 2023
b600cb6
using files
shacharPash Apr 19, 2023
4d4f8f4
quoting echo
chayim Apr 19, 2023
ea49ede
add files to net7.0
shacharPash Apr 19, 2023
7879a70
try #if !WINDOWS
shacharPash Apr 19, 2023
45c00d0
wrap all
shacharPash Apr 19, 2023
c823576
delete usings
shacharPash Apr 19, 2023
d082e73
adding bouncyCastle+4.8.1 example
slorello89 Apr 19, 2023
1c27b8f
Merge branch 'Issue96/TlsExample' of https://github.com/redis/NRedisS…
slorello89 Apr 19, 2023
97851fa
adding back usings
slorello89 Apr 19, 2023
f46ee25
echoing vars to files
slorello89 Apr 19, 2023
24abe51
same pattern for windows
shacharPash Apr 24, 2023
db5bfb1
Merge branch 'master' into Issue96/TlsExample
shacharPash Apr 24, 2023
74826dd
try different way
shacharPash Apr 24, 2023
663656c
Save test certificates with wsl-bash
shacharPash Apr 25, 2023
d78893f
trying
shacharPash Apr 25, 2023
a678aa0
Merge branch 'master' into Issue96/TlsExample
shacharPash Apr 25, 2023
7b2eeef
delete mkdir line
shacharPash Apr 25, 2023
244ba7b
one run try
shacharPash Apr 25, 2023
bb9e4f6
return back
shacharPash Apr 25, 2023
75fe572
add .env to gitignor
shacharPash Apr 30, 2023
0c28eeb
change ImportPrivateKey
shacharPash May 1, 2023
e30e2c5
return to how it was before
shacharPash May 1, 2023
f450a89
comment repeated lines
shacharPash May 1, 2023
e646964
fixing 4.8.1 tests
slorello89 May 1, 2023
a5eaf02
adding some light debugging
slorello89 May 1, 2023
1966aea
could it be?
slorello89 May 1, 2023
7f10958
adding trim
slorello89 May 1, 2023
ad11e0b
removing debugging
slorello89 May 1, 2023
53d9f04
Merge branch 'Issue96/TlsExample' of https://github.com/redis/NRedisS…
shacharPash May 2, 2023
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
33 changes: 30 additions & 3 deletions .github/workflows/integration.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,11 @@ jobs:
build_and_Test:
name: Build and test
runs-on: ubuntu-latest
environment: REDIS_USER
env:
USER_NAME: ${{ secrets.USER_NAME }}
PASSWORD: ${{ secrets.PASSWORD }}
ENDPOINT: ${{ secrets.ENDPOINT }}
steps:
- uses: actions/checkout@v3
- name: .NET Core 6
Expand All @@ -35,9 +40,19 @@ jobs:
- name: Build
run: dotnet build --no-restore /p:ContinuousIntegrationBuild=true
- name: Test
run: dotnet test -f net6.0 --no-build --verbosity normal /p:CollectCoverage=true /p:CoverletOutputFormat=opencover
run: |
echo "${{secrets.REDIS_CA_PEM}}" > tests/NRedisStack.Tests/bin/Debug/net6.0/redis_ca.pem
echo "${{secrets.REDIS_USER_CRT}}" > tests/NRedisStack.Tests/bin/Debug/net6.0/redis_user.crt
echo "${{secrets.REDIS_USER_PRIVATE_KEY}}" > tests/NRedisStack.Tests/bin/Debug/net6.0/redis_user_private.key
ls -R
dotnet test -f net6.0 --no-build --verbosity normal /p:CollectCoverage=true /p:CoverletOutputFormat=opencover
- name: Test
run: dotnet test -f net7.0 --no-build --verbosity normal /p:CollectCoverage=true /p:CoverletOutputFormat=opencover
run: |
echo "${{secrets.REDIS_CA_PEM}}" > tests/NRedisStack.Tests/bin/Debug/net7.0/redis_ca.pem
echo "${{secrets.REDIS_USER_CRT}}" > tests/NRedisStack.Tests/bin/Debug/net7.0/redis_user.crt
echo "${{secrets.REDIS_USER_PRIVATE_KEY}}" > tests/NRedisStack.Tests/bin/Debug/net7.0/redis_user_private.key
ls -R
dotnet test -f net7.0 --no-build --verbosity normal /p:CollectCoverage=true /p:CoverletOutputFormat=opencover
- name: Codecov
uses: codecov/codecov-action@v3
with:
Expand All @@ -48,6 +63,11 @@ jobs:
build_and_test_windows:
name: Build and Test on Windows
runs-on: windows-latest
environment: REDIS_USER
env:
USER_NAME: ${{ secrets.USER_NAME }}
PASSWORD: ${{ secrets.PASSWORD }}
ENDPOINT: ${{ secrets.ENDPOINT }}
steps:
- uses: actions/checkout@v3
- uses: Vampire/setup-wsl@v2
Expand All @@ -59,11 +79,18 @@ jobs:
sudo apt-get update
sudo apt-get install curl -y && sudo apt-get install gpg -y && apt-get install lsb-release -y && apt-get install libgomp1 -y
curl https://packages.redis.io/redis-stack/redis-stack-server-${{env.redis_stack_version}}.jammy.x86_64.tar.gz -o redis-stack.tar.gz
tar xf redis-stack.tar.gz
tar xf redis-stack.tar.gz
- name: Restore dependencies
run: dotnet restore
- name: Build
run: dotnet build --no-restore /p:ContinuousIntegrationBuild=true
- name: Save test certificates
shell: wsl-bash {0}
run: |
echo "${{secrets.REDIS_CA_PEM}}" > tests/NRedisStack.Tests/bin/Debug/net481/redis_ca.pem
echo "${{secrets.REDIS_USER_CRT}}" > tests/NRedisStack.Tests/bin/Debug/net481/redis_user.crt
echo "${{secrets.REDIS_USER_PRIVATE_KEY}}" > tests/NRedisStack.Tests/bin/Debug/net481/redis_user_private.key
ls -R
- name: Test
shell: cmd
run: |
Expand Down
7 changes: 6 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -399,4 +399,9 @@ FodyWeavers.xsd
.idea
tests/NRedisStack.Tests/lcov.net7.0.info
tests/NRedisStack.Tests/lcov.net6.0.info
tests/NRedisStack.Tests/lcov.info
tests/NRedisStack.Tests/lcov.info
tests/NRedisStack.Tests/.env
tests/NRedisStack.Tests/redis_ca.pem
tests/NRedisStack.Tests/redis_credentials/redis_user_private.key
tests/NRedisStack.Tests/redis_credentials/redis_user.crt
.env
282 changes: 280 additions & 2 deletions tests/NRedisStack.Tests/Examples/ExamplesTests.cs
Original file line number Diff line number Diff line change
@@ -1,20 +1,32 @@
using System.Net.Security;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using Moq;
using NRedisStack.DataTypes;
using NRedisStack.RedisStackCommands;
using NRedisStack.Search;
using NRedisStack.Search.Aggregation;
using NRedisStack.Search.Literals.Enums;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Math;
using Org.BouncyCastle.OpenSsl;
using StackExchange.Redis;
using Xunit;
using Xunit.Abstractions;
using static NRedisStack.Search.Schema;

namespace NRedisStack.Tests;

public class ExaplesTests : AbstractNRedisStackTest, IDisposable
{
private readonly ITestOutputHelper testOutputHelper;
Mock<IDatabase> _mock = new Mock<IDatabase>();
private readonly string key = "EXAMPLES_TESTS";
public ExaplesTests(RedisFixture redisFixture) : base(redisFixture) { }
public ExaplesTests(RedisFixture redisFixture, ITestOutputHelper testOutputHelper) : base(redisFixture)
{
this.testOutputHelper = testOutputHelper;
}

public void Dispose()
{
Expand Down Expand Up @@ -292,6 +304,272 @@ public void TestJsonConvert()
Assert.Equal(10, docs.Count());
}

#if NET481
[Fact]
public void TestRedisCloudConnection_net481()
{
var root = Path.GetFullPath(Directory.GetCurrentDirectory());
var redisCaPath = Path.GetFullPath(Path.Combine(root, "redis_ca.pem"));
var redisUserCrtPath = Path.GetFullPath(Path.Combine(root, "redis_user.crt"));
var redisUserPrivateKeyPath = Path.GetFullPath(Path.Combine(root, "redis_user_private.key"));

var password = Environment.GetEnvironmentVariable("PASSWORD") ?? throw new Exception("PASSWORD is not set.");
var endpoint = Environment.GetEnvironmentVariable("ENDPOINT") ?? throw new Exception("ENDPOINT is not set.");

// Load the Redis credentials
var redisUserCertificate = new X509Certificate2(File.ReadAllBytes(redisUserCrtPath));
var redisCaCertificate = new X509Certificate2(File.ReadAllBytes(redisCaPath));

var rsa = RSA.Create();

var redisUserPrivateKeyText = File.ReadAllText(redisUserPrivateKeyPath).Trim();
rsa.ImportParameters(ImportPrivateKey(redisUserPrivateKeyText));

var clientCert = redisUserCertificate.CopyWithPrivateKey(rsa);

// Connect to Redis Cloud
var redisConfiguration = new ConfigurationOptions
{
EndPoints = { endpoint },
Ssl = true,
Password = password
};

redisConfiguration.CertificateSelection += (_, _, _, _, _) => new X509Certificate2(clientCert.Export(X509ContentType.Pfx));

redisConfiguration.CertificateValidation += (_, cert, _, errors) =>
{
if (errors == SslPolicyErrors.None)
{
return true;
}

var privateChain = new X509Chain();
privateChain.ChainPolicy = new X509ChainPolicy { RevocationMode = X509RevocationMode.NoCheck };
X509Certificate2 cert2 = new X509Certificate2(cert!);
privateChain.ChainPolicy.ExtraStore.Add(redisCaCertificate);
privateChain.Build(cert2);

bool isValid = true;

// we're establishing the trust chain so if the only complaint is that that the root CA is untrusted, and the root CA root
// matches our certificate, we know it's ok
foreach (X509ChainStatus chainStatus in privateChain.ChainStatus.Where(x =>
x.Status != X509ChainStatusFlags.UntrustedRoot))
{
if (chainStatus.Status != X509ChainStatusFlags.NoError)
{
isValid = false;
break;
}
}

return isValid;
};


var redis = ConnectionMultiplexer.Connect(redisConfiguration);
var db = redis.GetDatabase();
db.Ping();
}

public static RSAParameters ImportPrivateKey(string pem)
{
using var sr = new StringReader(pem);
PemReader pr = new PemReader(sr);
RSAParameters rp = new RSAParameters();
while (sr.Peek() != -1)
{
var privKey = pr.ReadObject() as AsymmetricCipherKeyPair;
if (privKey != null)
{
var pkParamaters = (RsaPrivateCrtKeyParameters)privKey.Private;
rp.Modulus = pkParamaters.Modulus.ToByteArrayUnsigned();
rp.Exponent = pkParamaters.PublicExponent.ToByteArrayUnsigned();
rp.P = pkParamaters.P.ToByteArrayUnsigned();
rp.Q = pkParamaters.Q.ToByteArrayUnsigned();
rp.D = ConvertRSAParametersField(pkParamaters.Exponent, rp.Modulus.Length);
rp.DP = ConvertRSAParametersField(pkParamaters.DP, rp.P.Length);
rp.DQ = ConvertRSAParametersField(pkParamaters.DQ, rp.Q.Length);
rp.InverseQ = ConvertRSAParametersField(pkParamaters.QInv, rp.Q.Length);
}
else
{
throw new ArgumentException("Pem is malformed and could not be parsed");
}
}
pr.ReadObject();
return rp;
}

private static byte[] ConvertRSAParametersField(BigInteger n, int size)
{
byte[] bs = n.ToByteArrayUnsigned();
if (bs.Length == size)
return bs;
if (bs.Length > size)
throw new ArgumentException("Specified size too small", "size");
byte[] padded = new byte[size];
Array.Copy(bs, 0, padded, size - bs.Length, bs.Length);
return padded;
}
#endif

#if NET6_0_OR_GREATER
[Fact]
public void TestRedisCloudConnection()
{
var root = Path.GetFullPath(Directory.GetCurrentDirectory());
var redisCaPath = Path.GetFullPath(Path.Combine(root, "redis_ca.pem"));
var redisUserCrtPath = Path.GetFullPath(Path.Combine(root, "redis_user.crt"));
var redisUserPrivateKeyPath = Path.GetFullPath(Path.Combine(root, "redis_user_private.key"));

var password = Environment.GetEnvironmentVariable("PASSWORD") ?? throw new Exception("PASSWORD is not set.");
var endpoint = Environment.GetEnvironmentVariable("ENDPOINT") ?? throw new Exception("ENDPOINT is not set.");

// Load the Redis credentials
var redisUserCertificate = new X509Certificate2(File.ReadAllBytes(redisUserCrtPath));
var redisCaCertificate = new X509Certificate2(File.ReadAllBytes(redisCaPath));

var rsa = RSA.Create();

var redisUserPrivateKeyText = File.ReadAllText(redisUserPrivateKeyPath);
var pemFileData = File.ReadAllLines(redisUserPrivateKeyPath).Where(x => !x.StartsWith("-"));
var binaryEncoding = Convert.FromBase64String(string.Join(null, pemFileData));

rsa.ImportRSAPrivateKey(binaryEncoding, out _);
redisUserCertificate.CopyWithPrivateKey(rsa);
rsa.ImportFromPem(redisUserPrivateKeyText.ToCharArray());
var clientCert = redisUserCertificate.CopyWithPrivateKey(rsa);

// Connect to Redis Cloud
var redisConfiguration = new ConfigurationOptions
{
EndPoints = { endpoint },
Ssl = true,
Password = password
};

redisConfiguration.CertificateSelection += (_, _, _, _, _) => clientCert;

redisConfiguration.CertificateValidation += (_, cert, _, errors) =>
{
if (errors == SslPolicyErrors.None)
{
return true;
}

var privateChain = new X509Chain();
privateChain.ChainPolicy = new X509ChainPolicy { RevocationMode = X509RevocationMode.NoCheck };
X509Certificate2 cert2 = new X509Certificate2(cert!);
privateChain.ChainPolicy.ExtraStore.Add(redisCaCertificate);
privateChain.Build(cert2);

bool isValid = true;

// we're establishing the trust chain so if the only complaint is that that the root CA is untrusted, and the root CA root
// matches our certificate, we know it's ok
foreach (X509ChainStatus chainStatus in privateChain.ChainStatus.Where(x =>
x.Status != X509ChainStatusFlags.UntrustedRoot))
{
if (chainStatus.Status != X509ChainStatusFlags.NoError)
{
isValid = false;
break;
}
}

return isValid;
};


var redis = ConnectionMultiplexer.Connect(redisConfiguration);
var db = redis.GetDatabase();
db.Ping();
}

[Fact]
public void TestRedisCloudConnection_DotnetCore3()
{
// Replace this with your own Redis Cloud credentials
var root = Path.GetFullPath(Directory.GetCurrentDirectory());
var redisCaPath = Path.GetFullPath(Path.Combine(root, "redis_ca.pem"));
var redisUserCrtPath = Path.GetFullPath(Path.Combine(root, "redis_user.crt"));
var redisUserPrivateKeyPath = Path.GetFullPath(Path.Combine(root, "redis_user_private.key"));

var password = Environment.GetEnvironmentVariable("PASSWORD") ?? throw new Exception("PASSWORD is not set.");
var endpoint = Environment.GetEnvironmentVariable("ENDPOINT") ?? throw new Exception("ENDPOINT is not set.");

// Load the Redis credentials
var redisUserCertificate = new X509Certificate2(File.ReadAllBytes(redisUserCrtPath));
var redisCaCertificate = new X509Certificate2(File.ReadAllBytes(redisCaPath));

var rsa = RSA.Create();

var redisUserPrivateKeyText = File.ReadAllText(redisUserPrivateKeyPath);
var pemFileData = File.ReadAllLines(redisUserPrivateKeyPath).Where(x => !x.StartsWith("-"));
var binaryEncoding = Convert.FromBase64String(string.Join(null, pemFileData));

rsa.ImportRSAPrivateKey(binaryEncoding, out _);
redisUserCertificate.CopyWithPrivateKey(rsa);
rsa.ImportFromPem(redisUserPrivateKeyText.ToCharArray());
var clientCert = redisUserCertificate.CopyWithPrivateKey(rsa);

var sslOptions = new SslClientAuthenticationOptions
{
CertificateRevocationCheckMode = X509RevocationMode.NoCheck,
LocalCertificateSelectionCallback = (_, _, _, _, _) => clientCert,
RemoteCertificateValidationCallback = (_, cert, _, errors) =>
{
if (errors == SslPolicyErrors.None)
{
return true;
}

var privateChain = new X509Chain();
privateChain.ChainPolicy = new X509ChainPolicy { RevocationMode = X509RevocationMode.NoCheck };
X509Certificate2 cert2 = new X509Certificate2(cert!);
privateChain.ChainPolicy.ExtraStore.Add(redisCaCertificate);
privateChain.Build(cert2);

bool isValid = true;

// we're establishing the trust chain so if the only complaint is that that the root CA is untrusted, and the root CA root
// matches our certificate, we know it's ok
foreach (X509ChainStatus chainStatus in privateChain.ChainStatus.Where(x=>x.Status != X509ChainStatusFlags.UntrustedRoot))
{
if (chainStatus.Status != X509ChainStatusFlags.NoError)
{
isValid = false;
break;
}
}

return isValid;
},
TargetHost = endpoint
};
// Connect to Redis Cloud
var redisConfiguration = new ConfigurationOptions
{
EndPoints = { endpoint },
Ssl = true,
SslHost = sslOptions.TargetHost,
SslClientAuthenticationOptions = host => sslOptions,
Password = password
};


var redis = ConnectionMultiplexer.Connect(redisConfiguration);
var db = redis.GetDatabase();
db.Ping();

db.StringSet("testKey", "testValue");
var value = db.StringGet("testKey");
Assert.Equal("testValue", value);
}
#endif

[Fact]
public void BasicJsonExamplesTest()
{
Expand Down Expand Up @@ -1067,4 +1345,4 @@ private static void SortAndCompare(List<string> expectedList, List<string> res)
Assert.Equal(expectedList[i], res[i].ToString());
}
}
}
}
Loading