-
Notifications
You must be signed in to change notification settings - Fork 230
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
How to pass driver_option "autoEncryption" to MongoDB\Client #742
Comments
Helo, I created a public gist with this workaround ( perhaps not the most "fancy" way but it works) but still needs testing. I post this here to help anyone who has a similar problem. # services.yaml
# Resolver
App\DependencyInjection\MongoBinaryEnvVarProcessor:
tags:
- { name: container.env_var_processor, priority: -1 } # config.yaml
app:
autoEncryption:
keyVaultNamespace: "%env(MONGODB_DB)%.keyVault"
kmsProviders:
aws:
accessKeyId: "%env(AWS_KEY)%"
secretAccessKey: "%env(AWS_SECRET)%"
schemaMap:
"%env(MONGODB_DB)%.clients":
bsonType: "object"
encryptMetadata:
keyId: ["%env(mongoBinary:base64:MONGO_KEY_ID)%"]
algorithm: "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic"
properties:
ssn:
encrypt: true <?php
namespace App;
use App\DependencyInjection\AppExtension;
use App\DependencyInjection\Compiler\MongoODMConfigurePass;
use Symfony\Bundle\FrameworkBundle\Kernel\MicroKernelTrait;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\HttpKernel\Kernel as BaseKernel;
class Kernel extends BaseKernel
{
use MicroKernelTrait;
protected function build(ContainerBuilder $container)
{
$container->registerExtension(new AppExtension());
$container->addCompilerPass(new MongoODMConfigurePass());
}
} <?php
namespace App\DependencyInjection;
use MongoDB\BSON\Binary;
use Symfony\Component\DependencyInjection\EnvVarProcessorInterface;
class MongoBinaryEnvVarProcessor implements EnvVarProcessorInterface
{
public function getEnv(string $prefix, string $name, \Closure $getEnv)
{
$binary = $getEnv( $name);
if ( $binary === null) {
return null;
}
return new Binary($binary, Binary::TYPE_UUID);
}
public static function getProvidedTypes()
{
return [
'mongoBinary' => "string",
];
}
} <?php
namespace App\DependencyInjection;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\HttpKernel\DependencyInjection\Extension;
class AppExtension extends Extension
{
protected array $config = [];
public function load(array $configs, ContainerBuilder $container)
{
$configuration = new Configuration();
$this->config = $this->processConfiguration($configuration, $configs);
}
public function getConfig(): array
{
return $this->config;
}
} <?php
namespace App\DependencyInjection;
use Symfony\Component\Config\Definition\Builder\TreeBuilder;
use Symfony\Component\Config\Definition\ConfigurationInterface;
class Configuration implements ConfigurationInterface
{
public function getConfigTreeBuilder()
{
$bsonTypes = [
'string',
'int',
'float',
'bool',
'object',
'array',
'binary',
'date',
'timestamp',
'regex',
'dbPointer',
'javascript',
'symbol',
'javascriptWithScope',
'int64',
'minKey',
'maxKey',
'numberLong',
];
$algorithms = ['AEAD_AES_256_CBC_HMAC_SHA_512-Random','AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic'];
$tree = new TreeBuilder('app');
$tree->getRootNode()
->children()
->arrayNode('autoEncryption')
->children()
->scalarNode('keyVaultNamespace')->isRequired()->end()
->arrayNode('kmsProviders')
->isRequired()
->requiresAtLeastOneElement()
->useAttributeAsKey('name')
->arrayPrototype()
->children()
->scalarNode('accessKeyId')->isRequired()->end()
->scalarNode('secretAccessKey')->isRequired()->end()
->end() // children
->end() // arrayPrototype
->end() // kmsProviders
->arrayNode('schemaMap')
->isRequired()
->useAttributeAsKey('name')
->arrayPrototype()
->children()
->enumNode('bsonType')->defaultValue('object')->values($bsonTypes)->end()
->arrayNode('encryptMetadata')
->children()
->arrayNode('keyId')->scalarPrototype()->end()->isRequired()->end()
->enumNode('algorithm')->values($algorithms)->defaultValue('AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic')->end()
->end() // children
->end() // encryptMetadata
->arrayNode('properties')
->isRequired()
->requiresAtLeastOneElement()
->useAttributeAsKey('name')
->arrayPrototype()
->children()
->arrayNode('encrypt')
->treatTrueLike(['keyId' => null])
->children()
->enumNode('bsonType')->defaultValue('string')->values($bsonTypes)->end()
->enumNode('algorithm')->values($algorithms)->defaultValue('AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic')->end()
->arrayNode('keyId')
->validate()
->ifEmpty()->thenUnset()
->end() // validate
->scalarPrototype()->end() // scalarPrototype
->end() // keyId
->end() // children
->end() // encrypt
->end() // children
->end() // arrayPrototype
->end() // array node
->end() // children
->end() // arrayPrototype
->end() // schemaMap
->end() // end of mongoAutoEncryption
->end() // mongoAutoEncryption
->end() // app
;
return $tree;
}
} <?php
namespace App\DependencyInjection\Compiler;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
class MongoODMConfigurePass implements CompilerPassInterface
{
public function process(ContainerBuilder $container)
{
$config = $container->getExtension('app')->getConfig();
$defaultConnection = $container->getDefinition("doctrine_mongodb.odm.default_connection");
$arg = $defaultConnection->getArgument(2);
if (array_key_exists('autoEncryption', $config)) {
$arg['autoEncryption'] = $config['autoEncryption'];
}
$defaultConnection->setArgument(2, $arg);
}
} This is an optional step but required when using AWS KMS, this command register the master key from AWS KMS into the MongoDB Vault collection<?php
namespace App\Command\System;
use MongoDB\Client;
use Symfony\Bundle\FrameworkBundle\Secrets\AbstractVault;
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;
#[AsCommand(name: "app:system:create-mongo-encrypt-key")]
class CreateMongoEncryptKeyCommand extends Command
{
public function __construct(protected AbstractVault $vault)
{
parent::__construct();
}
protected function configure()
{
$this->addArgument('key-id', InputArgument::REQUIRED, 'Key ID');
$this->addArgument('aws-region', InputArgument::OPTIONAL, 'AWS Region', 'us-east-1');
}
protected function execute(InputInterface $input, OutputInterface $output)
{
$io = new SymfonyStyle($input, $output);
$kms = [
"aws" => [
"accessKeyId" => $_ENV['AWS_KEY'],
"secretAccessKey" => $_ENV['AWS_SECRET'],
]
];
$masterKey = [
"key" => $input->getArgument("key-id"),
"region" => $input->getArgument('aws-region'),
];
$mongoClient = new Client($_ENV['MONGODB_URL']);
$encryptClient = $mongoClient->getManager()->createClientEncryption([
"keyVaultNamespace" => $_ENV['MONGODB_DB'].".keyVault",
"kmsProviders" => $kms
]);
$keyId = $encryptClient->createDataKey('aws', [
"masterKey" => $masterKey
]);
if ($this->vault->generateKeys()) {
$io->success($this->vault->getLastMessage());
}
$this->vault->seal("MONGO_KEY_ID", base64_encode($keyId->getData()));
$io->success($this->vault->getLastMessage() ?? 'Secret was successfully stored in the vault.');
return self::SUCCESS;
}
} |
@carlossosa thanks for posting your solution, I'm glad you're figured this out! If you have some time on your hands I'd love to have this ironed out and incorporated into the bundle. Feel free to hop on our Slack and ask if you'd need help :) |
The last few days I have been looking a way (or workaround) to pass the "autoEncryption" but nothing. The only option accepted by the Bundle under "driver_options" is "context" which is considered deprecated. I'll try to use a CompilerPass to override the "doctrine_mongodb.odm.default_connection" with this option.
My request is ( if the resources and the time allows it) to support this driver option. I'll try to learn more about the Bundle and create Pull request to support this option.
The text was updated successfully, but these errors were encountered: