Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save carlossosa/43d1d4b1702c5ec0368ca7bbb518ce35 to your computer and use it in GitHub Desktop.
Save carlossosa/43d1d4b1702c5ec0368ca7bbb518ce35 to your computer and use it in GitHub Desktop.
Symfony 5.4/DoctrineMongoDBBundle 4.4: Workaround to pass $driver_options["autoEncryption" ] to MongoDB\Client
<?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;
}
}
<?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);
}
}
<?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;
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 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;
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());
}
}
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
# Resolver
App\DependencyInjection\MongoBinaryEnvVarProcessor:
tags:
- { name: container.env_var_processor, priority: -1 }
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment