Skip to content

Commit

Permalink
Merge branch '4.4'
Browse files Browse the repository at this point in the history
* 4.4:
  [Console] Revert wrong change
  [HttpClient] Add a canceled state to the ResponseInterface
  Fix small typo in Exception message
  Restrict secrets management to sodium+filesystem
  Add secrets management
  Proof of concept for encrypted secrets
  • Loading branch information
nicolas-grekas committed Oct 22, 2019
2 parents df1bb4e + f5cef02 commit 077f7be
Show file tree
Hide file tree
Showing 30 changed files with 1,306 additions and 13 deletions.
1 change: 1 addition & 0 deletions src/Symfony/Bundle/FrameworkBundle/CHANGELOG.md
Expand Up @@ -37,6 +37,7 @@ CHANGELOG
* Added new `error_controller` configuration to handle system exceptions
* Added sort option for `translation:update` command.
* [BC Break] The `framework.messenger.routing.senders` config key is not deep merged anymore.
* Added `secrets:*` commands and `%env(secret:...)%` processor to deal with secrets seamlessly.

4.3.0
-----
Expand Down
@@ -0,0 +1,90 @@
<?php

/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Symfony\Bundle\FrameworkBundle\Command;

use Symfony\Bundle\FrameworkBundle\Secrets\AbstractVault;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\ConsoleOutputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;

/**
* @author Nicolas Grekas <p@tchwork.com>
*
* @internal
*/
final class SecretsDecryptToLocalCommand extends Command
{
protected static $defaultName = 'secrets:decrypt-to-local';

private $vault;
private $localVault;

public function __construct(AbstractVault $vault, AbstractVault $localVault = null)
{
$this->vault = $vault;
$this->localVault = $localVault;

parent::__construct();
}

protected function configure()
{
$this
->setDescription('Decrypts all secrets and stores them in the local vault.')
->addOption('force', 'f', InputOption::VALUE_NONE, 'Forces overriding of secrets that already exist in the local vault')
->setHelp(<<<'EOF'
The <info>%command.name%</info> command list decrypts all secrets and stores them in the local vault..
<info>%command.full_name%</info>
When the option <info>--force</info> is provided, secrets that already exist in the local vault are overriden.
<info>%command.full_name% --force</info>
EOF
)
;
}

protected function execute(InputInterface $input, OutputInterface $output): int
{
$io = new SymfonyStyle($input, $output instanceof ConsoleOutputInterface ? $output->getErrorOutput() : $output);

if (null === $this->localVault) {
$io->error('The local vault is disabled.');

return 1;
}

$secrets = $this->vault->list(true);

if (!$input->getOption('force')) {
foreach ($this->localVault->list() as $k => $v) {
unset($secrets[$k]);
}
}

foreach ($secrets as $k => $v) {
if (null === $v) {
$io->error($this->vault->getLastMessage());

return 1;
}

$this->localVault->seal($k, $v);
}

return 0;
}
}
@@ -0,0 +1,90 @@
<?php

/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Symfony\Bundle\FrameworkBundle\Command;

use Symfony\Bundle\FrameworkBundle\Secrets\AbstractVault;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\ConsoleOutputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;

/**
* @author Nicolas Grekas <p@tchwork.com>
*
* @internal
*/
final class SecretsEncryptFromLocalCommand extends Command
{
protected static $defaultName = 'secrets:encrypt-from-local';

private $vault;
private $localVault;

public function __construct(AbstractVault $vault, AbstractVault $localVault = null)
{
$this->vault = $vault;
$this->localVault = $localVault;

parent::__construct();
}

protected function configure()
{
$this
->setDescription('Encrypts all local secrets to the vault.')
->addOption('force', 'f', InputOption::VALUE_NONE, 'Forces overriding of secrets that already exist in the vault')
->setHelp(<<<'EOF'
The <info>%command.name%</info> command list encrypts all local secrets and stores them in the vault..
<info>%command.full_name%</info>
When the option <info>--force</info> is provided, secrets that already exist in the vault are overriden.
<info>%command.full_name% --force</info>
EOF
)
;
}

protected function execute(InputInterface $input, OutputInterface $output): int
{
$io = new SymfonyStyle($input, $output instanceof ConsoleOutputInterface ? $output->getErrorOutput() : $output);

if (null === $this->localVault) {
$io->error('The local vault is disabled.');

return 1;
}

$secrets = $this->localVault->list(true);

if (!$input->getOption('force')) {
foreach ($this->vault->list() as $k => $v) {
unset($secrets[$k]);
}
}

foreach ($secrets as $k => $v) {
if (null === $v) {
$io->error($this->localVault->getLastMessage());

return 1;
}

$this->vault->seal($k, $v);
}

return 0;
}
}
@@ -0,0 +1,125 @@
<?php

/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Symfony\Bundle\FrameworkBundle\Command;

use Symfony\Bundle\FrameworkBundle\Secrets\AbstractVault;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\ConsoleOutputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;

/**
* @author Tobias Schultze <http://tobion.de>
* @author Jérémy Derussé <jeremy@derusse.com>
* @author Nicolas Grekas <p@tchwork.com>
*
* @internal
*/
final class SecretsGenerateKeysCommand extends Command
{
protected static $defaultName = 'secrets:generate-keys';

private $vault;
private $localVault;

public function __construct(AbstractVault $vault, AbstractVault $localVault = null)
{
$this->vault = $vault;
$this->localVault = $localVault;

parent::__construct();
}

protected function configure()
{
$this
->setDescription('Generates new encryption keys.')
->addOption('local', 'l', InputOption::VALUE_NONE, 'Updates the local vault.')
->addOption('rotate', 'r', InputOption::VALUE_NONE, 'Re-encrypts existing secrets with the newly generated keys.')
->setHelp(<<<'EOF'
The <info>%command.name%</info> command generates a new encryption key.
<info>%command.full_name%</info>
If encryption keys already exist, the command must be called with
the <info>--rotate</info> option in order to override those keys and re-encrypt
existing secrets.
<info>%command.full_name% --rotate</info>
EOF
)
;
}

protected function execute(InputInterface $input, OutputInterface $output): int
{
$io = new SymfonyStyle($input, $output instanceof ConsoleOutputInterface ? $output->getErrorOutput() : $output);
$vault = $input->getOption('local') ? $this->localVault : $this->vault;

if (null === $vault) {
$io->success('The local vault is disabled.');

return 1;
}

if (!$input->getOption('rotate')) {
if ($vault->generateKeys()) {
$io->success($vault->getLastMessage());

if ($this->vault === $vault) {
$io->caution('DO NOT COMMIT THE DECRYPTION KEY FOR THE PROD ENVIRONMENT⚠️');
}

return 0;
}

$io->warning($vault->getLastMessage());

return 1;
}

$secrets = [];
foreach ($vault->list(true) as $name => $value) {
if (null === $value) {
$io->error($vault->getLastMessage());

return 1;
}

$secrets[$name] = $value;
}

if (!$vault->generateKeys(true)) {
$io->warning($vault->getLastMessage());

return 1;
}

$io->success($vault->getLastMessage());

if ($secrets) {
foreach ($secrets as $name => $value) {
$vault->seal($name, $value);
}

$io->comment('Existing secrets have been rotated to the new keys.');
}

if ($this->vault === $vault) {
$io->caution('DO NOT COMMIT THE DECRYPTION KEY FOR THE PROD ENVIRONMENT⚠️');
}

return 0;
}
}

0 comments on commit 077f7be

Please sign in to comment.