Skip to content

Instantly share code, notes, and snippets.

@kennwhite
Last active March 23, 2022 13:01
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save kennwhite/c1b598f8433ee1e662b55a06dc69161a to your computer and use it in GitHub Desktop.
Save kennwhite/c1b598f8433ee1e662b55a06dc69161a to your computer and use it in GitHub Desktop.
Simple MongoDB client-side field level encryption (libmongocrypt) demo on Alpine Linux with .NET
# WARNING: This is a demonstration only, not any kind of official build - use at your own risk
# Launched standard Alpine Linux AMI on an t2.micro instance configured w/ 8GB:
# alpine-3.15.1-x86_64-bios-cloudinit-r0 - ami-0421638898b821bff
#
# ssh -i mykey.pem alpine@[instance address]
# This demo runs as default non-root "alpine" user
cd /home/alpine
# .NET installer dependencies
sudo apk add bash icu-dev
wget https://dot.net/v1/dotnet-install.sh
# Installed via MS .NET docs https://docs.microsoft.com/en-us/dotnet/core/install/linux-scripted-manual#scripted-install
# defaults to current LTS version (6)
chmod +x ./dotnet-install.sh
./dotnet-install.sh
# Add .NET tools & locally-built libraries
export PATH=$PATH:/home/alpine/.dotnet:/usr/local/lib
# Sanity check
dotnet --info
uname -a
cat /etc/os-release
sudo apk add git make cmake g++ libbson-static musl-dev libc-dev openssl openssl-dev py3-pip
git clone https://github.com/mongodb/mongo-c-driver
cd /home/alpine/mongo-c-driver/
cmake -DENABLE_MONGOC=OFF .
# Note: warning issued for: Policy CMP0075 is not set: Include file check macros honor
sudo make -j8 install
cd /home/alpine
git clone https://github.com/mongodb/libmongocrypt
cd /home/alpine/libmongocrypt
cmake .
# Throws similar warning: CMake Warning at CMakeLists.txt:416 (find_package):
# By not providing "Findmongoc-1.0.cmake" in CMAKE_MODULE_PATH this project
# has asked CMake to find a package configuration file provided by
# "mongoc-1.0", but CMake did not find one.
sudo make install
mkdir /home/alpine/app
cd /home/alpine/app
cat << EOF > app.csproj
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="MongoDB.Bson" Version="2.15.0" />
<PackageReference Include="MongoDB.Driver" Version="2.15.0" />
<PackageReference Include="MongoDB.Driver.Core" Version="2.15.0" />
</ItemGroup>
</Project>
EOF
# cat app.csproj
cat << EOF > app.cs
[contents of https://mongodb.github.io/mongo-csharp-driver/2.15/reference/driver/crud/client_side_encryption/#explicit-encryption-and-auto-decryption]
EOF
# NOTE: Change the 2 localhost references in app.cs to a real connection string, e.g:
# var keyVaultClient = new MongoClient("mongodb+srv://appuser:XXXX@cluster0.xxx.mongodb.net/test?retryWrites=true&w=majority");
cd /home/alpine/app
dotnet publish -c release -o publish -r linux-musl-x64 --self-contained
cp /usr/local/lib/libmongocrypt.so ./bin/release/net6.0/linux-musl-x64/
cp /usr/local/lib/libmongocrypt.so ./publish/
# Confirm this instance's IP has network permissions to the database
wget -qO- icanhazip.com
# Alternatively: ./publish/app
./bin/release/net6.0/linux-musl-x64/app
$ dotnet --info
.NET SDK (reflecting any global.json):
Version: 6.0.103
Commit: 2d7bc7059f
Runtime Environment:
OS Name: alpine
OS Version: 3.15
OS Platform: Linux
RID: alpine.3.15-x64
Base Path: /home/alpine/.dotnet/sdk/6.0.103/
Host (useful for support):
Version: 6.0.3
Commit: c24d9a9c91
.NET SDKs installed:
6.0.103 [/home/alpine/.dotnet/sdk]
.NET runtimes installed:
Microsoft.AspNetCore.App 6.0.3 [/home/alpine/.dotnet/shared/Microsoft.AspNetCore.App]
Microsoft.NETCore.App 6.0.3 [/home/alpine/.dotnet/shared/Microsoft.NETCore.App]
To install additional .NET runtimes or SDKs:
https://aka.ms/dotnet-download
$ uname -a
Linux ip-172-30-0-227 5.15.29-0-virt #1-Alpine SMP Wed, 16 Mar 2022 15:02:59 +0000 x86_64 Linux
$ cat /etc/os-release
NAME="Alpine Linux"
ID=alpine
VERSION_ID=3.15.1
PRETTY_NAME="Alpine Linux v3.15"
HOME_URL="https://alpinelinux.org/"
BUG_REPORT_URL="https://bugs.alpinelinux.org/"
$ ./bin/release/net6.0/linux-musl-x64/app
Original string 123456789.
Encrypted value Encrypted:0x013ee87f33fc384c6ea50ed6c1a7090eb002f2129159c32ed7890935f1fea69f6ddfd82b798b927bb885e2f3a1ee21f8e2fd4507433c39c57085f711812c9ba3332065a68d52894c6dcb7567e528d5d7a8b5.
Decrypted document { "_id" : ObjectId("623a9901331480269e996fc6"), "encryptedField" : "123456789" }.
$
// From: https://mongodb.github.io/mongo-csharp-driver/2.15/reference/driver/crud/client_side_encryption/#explicit-encryption-and-auto-decryption
// NOTE: You MUST update the 2 connection strings "xxxx" for your database
using System;
using System.Collections.Generic;
using System.Threading;
using MongoDB.Bson;
using MongoDB.Driver.Encryption;
using MongoDB.Libmongocrypt;
namespace MongoDB.Driver.Examples
{
public class ExplicitEncryptionAndAutoDecryptionExamples
{
private const string LocalMasterKey = "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk";
public static void Main(string[] args)
{
var localMasterKey = Convert.FromBase64String(LocalMasterKey);
var kmsProviders = new Dictionary<string, IReadOnlyDictionary<string, object>>();
var localKey = new Dictionary<string, object>
{
{ "key", localMasterKey }
};
kmsProviders.Add("local", localKey);
var keyVaultNamespace = CollectionNamespace.FromFullName("keyvault.datakeys");
var collectionNamespace = CollectionNamespace.FromFullName("test.coll");
var autoEncryptionOptions = new AutoEncryptionOptions(
keyVaultNamespace,
kmsProviders,
bypassAutoEncryption: true);
var clientSettings = MongoClientSettings.FromConnectionString("mongodb+srv://appuser:XXXX@cluster0.xxxxx.mongodb.net/test?retryWrites=true&w=majority");
clientSettings.AutoEncryptionOptions = autoEncryptionOptions;
var mongoClient = new MongoClient(clientSettings);
var database = mongoClient.GetDatabase(collectionNamespace.DatabaseNamespace.DatabaseName);
database.DropCollection(collectionNamespace.CollectionName);
var collection = database.GetCollection<BsonDocument>(collectionNamespace.CollectionName);
var keyVaultClient = new MongoClient("mongodb+srv://appuser:XXXX@cluster0.xxxxx.mongodb.net/test?retryWrites=true&w=majority");
var keyVaultDatabase = keyVaultClient.GetDatabase(keyVaultNamespace.DatabaseNamespace.DatabaseName);
keyVaultDatabase.DropCollection(keyVaultNamespace.CollectionName);
// Create the ClientEncryption instance
var clientEncryptionSettings = new ClientEncryptionOptions(
keyVaultClient,
keyVaultNamespace,
kmsProviders);
using (var clientEncryption = new ClientEncryption(clientEncryptionSettings))
{
var dataKeyId = clientEncryption.CreateDataKey(
"local",
new DataKeyOptions(),
CancellationToken.None);
var originalString = "123456789";
Console.WriteLine($"Original string {originalString}.");
// Explicitly encrypt a field
var encryptOptions = new EncryptOptions(
EncryptionAlgorithm.AEAD_AES_256_CBC_HMAC_SHA_512_Deterministic.ToString(),
keyId: dataKeyId);
var encryptedFieldValue = clientEncryption.Encrypt(
originalString,
encryptOptions,
CancellationToken.None);
Console.WriteLine($"Encrypted value {encryptedFieldValue}.");
collection.InsertOne(new BsonDocument("encryptedField", encryptedFieldValue));
// Automatically decrypts the encrypted field.
var decryptedValue = collection.Find(FilterDefinition<BsonDocument>.Empty).First();
Console.WriteLine($"Decrypted document {decryptedValue.ToJson()}.");
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment