Skip to content
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

Aws kms #120

Merged
merged 20 commits into from
Mar 7, 2019
6 changes: 1 addition & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,12 +45,8 @@ Kamus has 3 components:
The encrypt and decrypt APIs handle encryption and decryption requests.
The KMS is a wrapper for various cryptographic solutions. Currently supported:
* AES - uses one key for all secrets
* Azure KeyVault - creates one key per service account.
* Google Cloud KMS - creates one key per service account.
* AWS KMS, Azure KeyVault, Google Cloud KMS - creates one key per service account.

We look forward to add support for other cloud solutions, like AWS KMS.
Copy link
Contributor

Choose a reason for hiding this comment

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

Maybe keep this section just say "add support for other solutions, like HSM"?

If you're interested in such a feature, please let us know.
We would like help with testing it out.
Consult the [installation guide](docs/install.md) for more details on how to deploy Kamus using the relevant KMS.

### Utilities
Expand Down
28 changes: 27 additions & 1 deletion docs/install.md
Original file line number Diff line number Diff line change
Expand Up @@ -94,9 +94,35 @@ keyManagement:
googleKms:
location: <location>
keyRing: <key ring name>
protectionLevelP: HSM
protectionLevel: HSM
```
And use the following command to deploy kamus:
```
helm upgrade --install kamus soluto/kamus -f values.yaml --set-string keyManagement.googleKms.credentials="$(cat credentials.json | base64)"
```

### AWS KMS
Using [AWS KMS](https://docs.aws.amazon.com/kms/latest/developerguide/overview.html) as the key managment solution is the secure solution when running a cluster on AWS Cloud.
There are 2 options to authentication with the KMS:
1. Kamus by default will try to use the regular AWS SDK discovery mechinisem, if your cluster in AWS you need to map IAM role to kamus POD by using one of the community tools, for example [kiam](https://github.com/uswitch/kiam).
2. Provide user access key and secret with KMS access.
Copy link
Contributor

Choose a reason for hiding this comment

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

Add a quick note on what is required when initializing KMS?

Copy link
Contributor

Choose a reason for hiding this comment

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

Also, maybe add a note on the required IAM permissions for Kamus?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Can you elaborate? I can't think of anything required other than permissions to KMS.

Copy link
Contributor

Choose a reason for hiding this comment

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

My bad, you're right. Just specify the IAM role?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I've added below that the required IAM role/user permissions for kamus to work properly is full KMS access.


Typical values.yaml for AWS :
```yaml
keyManagement:
provider: AwsKms
```
If you want to pass user access key and secret to Kamus deploy use the following values.yaml command:
```yaml
keyManagement:
provider: AwsKms
awsKms:
region: <>
key: <>
secret: <>
```
And now deploy Kamus using the following helm command:
```
helm upgrade --install kamus soluto/kamus -f <path/to/values.yaml>
```

43 changes: 32 additions & 11 deletions src/decrypt-api/Startup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,14 @@
using System.Linq;
using Kamus.KeyManagement;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.DataProtection;
using Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ConfigurationModel;
using Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption;
using Google.Apis.Auth.OAuth2;
using Google.Apis.CloudKMS.v1;
using Google.Apis.Services;
using System.IO;
using System.Reflection;

using Google.Apis.Auth.OAuth2;
using Google.Apis.CloudKMS.v1;
using Google.Apis.Services;
using System.IO;
using System.Reflection;
using Amazon;
using Amazon.KeyManagementService;

namespace Kamus
{
public class Startup {
Expand Down Expand Up @@ -73,6 +72,8 @@ public void ConfigureServices (IServiceCollection services) {
var provider = Configuration.GetValue<string>("KeyManagement:Provider");
switch (provider)
{
case "AwsKms":
return GetAwsKeyManagement(s.GetRequiredService<ILogger>());
case "GoogleKms":
return GetGoogleCloudKeyManagment();
case "AzureKeyVault":
Expand Down Expand Up @@ -147,8 +148,8 @@ public void Configure (IApplicationBuilder app, IHostingEnvironment env) {
app.UseAuthentication();

app.UseMvc ();
}

}
private IKeyManagement GetGoogleCloudKeyManagment()
{
var location = Configuration.GetValue<string>("KeyManagement:GoogleKms:Location");
Expand Down Expand Up @@ -180,5 +181,25 @@ private IKeyManagement GetGoogleCloudKeyManagment()
location,
protectionLevel);
}

private IKeyManagement GetAwsKeyManagement(ILogger logger)
{
AmazonKeyManagementServiceClient kmsService;
var region = Configuration.GetValue<string>("KeyManagement:AwsKms:Region");
var awsKey = Configuration.GetValue<string>("KeyManagement:AwsKms:Key");
shaikatz marked this conversation as resolved.
Show resolved Hide resolved
var awsSecret = Configuration.GetValue<string>("KeyManagement:AwsKms:Secret");

if (string.IsNullOrEmpty(region) || string.IsNullOrEmpty(awsKey) || string.IsNullOrEmpty(awsSecret))
{
logger.Information("AwsKms credentials were not provided, using default AWS SDK credentials discovery");
kmsService = new AmazonKeyManagementServiceClient();
}
else
{
kmsService = new AmazonKeyManagementServiceClient(awsKey, awsSecret, RegionEndpoint.GetBySystemName(region));
}

return new AwsKeyManagement(kmsService, new SymmetricKeyManagement());
}
}
}
1 change: 1 addition & 0 deletions src/decrypt-api/decrypt-api.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
<ProjectReference Include="..\key-managment\key-managment.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="AWSSDK.KeyManagementService" Version="3.3.7.21" />
<PackageReference Include="System.Net.Security" Version="4.3.2" />
<PackageReference Include="App.Metrics.AspNetCore.Mvc" Version="2.0.0" />
<PackageReference Include="App.Metrics.Formatters.Prometheus" Version="2.0.0" />
Expand Down
44 changes: 34 additions & 10 deletions src/encrypt-api/Startup.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
using System;
using System.IO;
using System.Reflection;
using System.IO;
using System.Reflection;
using System.Threading.Tasks;
using Google.Apis.Auth.OAuth2;
using Google.Apis.CloudKMS.v1;
using Google.Apis.Services;
using Amazon;
using Amazon.KeyManagementService;
using Google.Apis.Auth.OAuth2;
using Google.Apis.CloudKMS.v1;
using Google.Apis.Services;
using Kamus.KeyManagement;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
Expand Down Expand Up @@ -35,8 +37,8 @@ public Startup(IHostingEnvironment env)
.AddJsonFile("secrets/appsettings.secrets.json", optional: true)
.AddEnvironmentVariables();

Configuration = builder.Build();

Configuration = builder.Build();
var version = Assembly.GetExecutingAssembly().GetName().Version.ToString();
Console.WriteLine($"Kamus Encryptor API {version} starting");
}
Expand All @@ -57,7 +59,9 @@ public void ConfigureServices (IServiceCollection services) {
{
var provider = Configuration.GetValue<string>("KeyManagement:Provider");
switch (provider)
{
{
case "AwsKms":
return GetAwsKeyManagement(s.GetRequiredService<ILogger>());
case "GoogleKms":
return GetGoogleCloudKeyManagment();
case "AzureKeyVault":
Expand Down Expand Up @@ -126,7 +130,7 @@ public void Configure (IApplicationBuilder app, IHostingEnvironment env) {
app.UseMvc ();
}

private IKeyManagement GetGoogleCloudKeyManagment()
private IKeyManagement GetGoogleCloudKeyManagment()
{
var location = Configuration.GetValue<string>("KeyManagement:GoogleKms:Location");
var keyRingName = Configuration.GetValue<string>("KeyManagement:GoogleKms:KeyRingName");
Expand Down Expand Up @@ -155,7 +159,27 @@ private IKeyManagement GetGoogleCloudKeyManagment()
serviceAccountCredential.ProjectId,
keyRingName,
location,
protectionLevel);
protectionLevel);
}

private IKeyManagement GetAwsKeyManagement(ILogger logger)
{
AmazonKeyManagementServiceClient kmsService;
var region = Configuration.GetValue<string>("KeyManagement:AwsKms:Region");
var awsKey = Configuration.GetValue<string>("KeyManagement:AwsKms:Key");
var awsSecret = Configuration.GetValue<string>("KeyManagement:AwsKms:Secret");

if (string.IsNullOrEmpty(region) || string.IsNullOrEmpty(awsKey) || string.IsNullOrEmpty(awsSecret))
{
logger.Information("AwsKms credentials were not provided, using default AWS SDK credentials discovery");
kmsService = new AmazonKeyManagementServiceClient();
}
else
{
kmsService = new AmazonKeyManagementServiceClient(awsKey, awsSecret, RegionEndpoint.GetBySystemName(region));
}

return new AwsKeyManagement(kmsService, new SymmetricKeyManagement());
}
}
}
1 change: 1 addition & 0 deletions src/encrypt-api/encrypt-api.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
<ProjectReference Include="..\key-managment\key-managment.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="AWSSDK.KeyManagementService" Version="3.3.7.21" />
<PackageReference Include="System.Net.Security" Version="4.3.2" />
<PackageReference Include="App.Metrics.AspNetCore.Mvc" Version="2.0.0" />
<PackageReference Include="App.Metrics.Formatters.Prometheus" Version="2.0.0" />
Expand Down
81 changes: 81 additions & 0 deletions src/key-managment/AwsKeyManagement.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
using System;
using System.IO;
using System.Net;
using System.Threading.Tasks;
using Amazon.KeyManagementService;
using Amazon.KeyManagementService.Model;

namespace Kamus.KeyManagement
{
public class AwsKeyManagement : IKeyManagement
{
private readonly IAmazonKeyManagementService mAmazonKeyManagementService;
private readonly SymmetricKeyManagement mSymmetricKeyManagement;

public AwsKeyManagement(IAmazonKeyManagementService amazonKeyManagementService, SymmetricKeyManagement symmetricKeyManagement)
{
mAmazonKeyManagementService = amazonKeyManagementService;
mSymmetricKeyManagement = symmetricKeyManagement;
}

public async Task<string> Encrypt(string data, string serviceAccountId, bool createKeyIfMissing = true)
{
var masterKeyAlias = $"alias/kamus/{KeyIdCreator.Create(serviceAccountId)}";
shaikatz marked this conversation as resolved.
Show resolved Hide resolved
(var encryptionKey, var encryptedDataKey ) = await GenerateEncryptionKey(masterKeyAlias);
shaikatz marked this conversation as resolved.
Show resolved Hide resolved
mSymmetricKeyManagement.SetEncryptionKey(Convert.ToBase64String(encryptionKey.ToArray()));
var encryptedData = await mSymmetricKeyManagement.Encrypt(data, serviceAccountId);
shaikatz marked this conversation as resolved.
Show resolved Hide resolved

return "env" + "$" + encryptedDataKey + "$" + encryptedData;

}

public async Task<string> Decrypt(string encryptedData, string serviceAccountId)
{
var encryptedEncryptionKey = encryptedData.Split('$')[1];
shaikatz marked this conversation as resolved.
Show resolved Hide resolved
var actualEncryptedData = encryptedData.Split('$')[2];

var decryptionResult = await mAmazonKeyManagementService.DecryptAsync(new DecryptRequest
shaikatz marked this conversation as resolved.
Show resolved Hide resolved
{
CiphertextBlob = new MemoryStream(Convert.FromBase64String(encryptedEncryptionKey)),
});

var encryptionKey = ConvertMemoryStreamToBase64String(decryptionResult.Plaintext);
shaikatz marked this conversation as resolved.
Show resolved Hide resolved

mSymmetricKeyManagement.SetEncryptionKey(encryptionKey);
return await mSymmetricKeyManagement.Decrypt(actualEncryptedData, serviceAccountId);
}

private static string ConvertMemoryStreamToBase64String(MemoryStream ms)
{
return Convert.ToBase64String(ms.ToArray());
}

private async Task<(MemoryStream encryptionKey, string encryptedEncryptionKey)> GenerateEncryptionKey(string keyAlias)
{
GenerateDataKeyResponse generateKeyResponse = null;
try
{
generateKeyResponse = await mAmazonKeyManagementService.GenerateDataKeyAsync(new GenerateDataKeyRequest { KeyId = keyAlias, KeySpec = "AES_256"});

}
catch (NotFoundException)
{
await GenerateMasterKey(keyAlias);
generateKeyResponse = await mAmazonKeyManagementService.GenerateDataKeyAsync(new GenerateDataKeyRequest { KeyId = keyAlias, KeySpec = "AES_256"});
}

if (generateKeyResponse.HttpStatusCode != HttpStatusCode.OK)
{
throw new Exception($"Couldn't generate encryption key for {keyAlias}");
}

return (generateKeyResponse.Plaintext, ConvertMemoryStreamToBase64String(generateKeyResponse.CiphertextBlob));
}

private async Task GenerateMasterKey(string keyAlias)
{
var createKeyResponse = await mAmazonKeyManagementService.CreateKeyAsync(new CreateKeyRequest());
await mAmazonKeyManagementService.CreateAliasAsync(keyAlias, createKeyResponse.KeyMetadata.KeyId);
}
}
}
13 changes: 2 additions & 11 deletions src/key-managment/AzureKeyVaultKeyManagement.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ public AzureKeyVaultKeyManagement(IKeyVaultClient keyVaultClient,

public async Task<string> Decrypt(string encryptedData, string serviceAccountId)
{
var hash = ComputeKeyId(serviceAccountId);
var hash = KeyIdCreator.Create(serviceAccountId);

var keyId = $"https://{mKeyVaultName}.vault.azure.net/keys/{hash}";
try
Expand All @@ -52,7 +52,7 @@ public async Task<string> Decrypt(string encryptedData, string serviceAccountId)

public async Task<string> Encrypt(string data, string serviceAccountId, bool createKeyIfMissing = true)
{
var hash = ComputeKeyId(serviceAccountId);
var hash = KeyIdCreator.Create(serviceAccountId);

var keyId = $"https://{mKeyVaultName}.vault.azure.net/keys/{hash}";

Expand All @@ -73,14 +73,5 @@ public async Task<string> Encrypt(string data, string serviceAccountId, bool cre

return Convert.ToBase64String(encryptionResult.Result);
}

private string ComputeKeyId(string serviceUserName)
{
return
WebEncoders.Base64UrlEncode(
SHA256.Create().ComputeHash(
Encoding.UTF8.GetBytes(serviceUserName)))
.Replace("_", "-");
}
}
}
13 changes: 2 additions & 11 deletions src/key-managment/GoogleCloudKeyManagment.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ public GoogleCloudKeyManagment(

public async Task<string> Decrypt(string encryptedData, string serviceAccountId)
{
var safeId = ComputeKeyId(serviceAccountId);
var safeId = KeyIdCreator.Create(serviceAccountId);
var cryptoKeys = mKmsService.Projects.Locations.KeyRings.CryptoKeys;
var keyringId = $"projects/{mProjectName}/locations/{mKeyringLocation}/keyRings/{mKeyringName}";
var keyId = $"{keyringId}/cryptoKeys/{safeId}";
Expand All @@ -50,7 +50,7 @@ public async Task<string> Decrypt(string encryptedData, string serviceAccountId)

public async Task<string> Encrypt(string data, string serviceAccountId, bool createKeyIfMissing = true)
{
var safeId = ComputeKeyId(serviceAccountId);
var safeId = KeyIdCreator.Create(serviceAccountId);
var cryptoKeys = mKmsService.Projects.Locations.KeyRings.CryptoKeys;
var keyringId = $"projects/{mProjectName}/locations/{mKeyringLocation}/keyRings/{mKeyringName}";
var keyId = $"{keyringId}/cryptoKeys/{safeId}";
Expand Down Expand Up @@ -81,14 +81,5 @@ public async Task<string> Encrypt(string data, string serviceAccountId, bool cre

return encryted.Ciphertext;
}

private string ComputeKeyId(string serviceUserName)
{
return
WebEncoders.Base64UrlEncode(
SHA256.Create().ComputeHash(
Encoding.UTF8.GetBytes(serviceUserName)))
.Replace("_", "-");
}
}
}
18 changes: 18 additions & 0 deletions src/key-managment/KeyIdCreator.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
using System.Security.Cryptography;
using System.Text;
using Microsoft.AspNetCore.WebUtilities;

namespace Kamus.KeyManagement
{
public static class KeyIdCreator
{
public static string Create(string s)
{
return
WebEncoders.Base64UrlEncode(
SHA256.Create().ComputeHash(
Encoding.UTF8.GetBytes(s)))
.Replace("_", "-");
}
}
}
1 change: 1 addition & 0 deletions src/key-managment/key-managment.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="AWSSDK.KeyManagementService" Version="3.3.7.21" />
<PackageReference Include="Google.Apis.CloudKMS.v1" Version="1.37.0.1468" />
<PackageReference Include="Microsoft.AspNetCore.WebUtilities" Version="2.2.0" />
<PackageReference Include="Microsoft.Azure.KeyVault" Version="3.0.3" />
Expand Down
Loading