8000 Restrict secrets management to sodium+filesystem · symfony/symfony@e089bec · GitHub
[go: up one dir, main page]

Skip to content

Commit e089bec

Browse files
Restrict secrets management to sodium+filesystem
1 parent cc3575a commit e089bec

28 files changed

+370
-868
lines changed

composer.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,7 @@
113113
"monolog/monolog": "^1.25.1",
114114
"nyholm/psr7": "^1.0",
115115
"ocramius/proxy-manager": "^2.1",
116+
"paragonie/sodium_compat": "^1.8",
116117
"php-http/httplug": "^1.0|^2.0",
117118
"predis/predis": "~1.1",
118119
"psr/http-client": "^1.0",

src/Symfony/Bundle/FrameworkBundle/CHANGELOG.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ CHANGELOG
1717
* Added new `error_controller` configuration to handle system exceptions
1818
* Added sort option for `translation:update` command.
1919
* [BC Break] The `framework.messenger.routing.senders` config key is not deep merged anymore.
20-
* Added secrets management.
20+
* Added `secrets:*` commands and `%env(secret:...)%` processor to deal with secrets seamlessly.
2121

2222
4.3.0
2323
-----

src/Symfony/Bundle/FrameworkBundle/Command/SecretsGenerateKeyCommand.php

Lines changed: 0 additions & 97 deletions
This file was deleted.
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Bundle\FrameworkBundle\Command;
13+
14+
use Symfony\Bundle\FrameworkBundle\Secrets\SodiumVault;
15+
use Symfony\Component\Console\Command\Command;
16+
use Symfony\Component\Console\Input\InputInterface;
17+
use Symfony\Component\Console\Input\InputOption;
18+
use Symfony\Component\Console\Output\OutputInterface;
19+
use Symfony\Component\Console\Style\SymfonyStyle;
20+
21+
/**
22+
* @author Tobias Schultze <http://tobion.de>
23+
* @author Jérémy Derussé <jeremy@derusse.com>
24+
*/
25+
final class SecretsGenerateKeysCommand extends Command
26+
{
27+
protected static $defaultName = 'secrets:generate-keys';
28+
29+
private $vault;
30+
31+
public function __construct(SodiumVault $vault)
32+
{
33+
$this->vault = $vault;
34+
parent::__construct();
35+
}
36+
37+
protected function configure()
38+
{
39+
$this
40+
->setDefinition([
41+
new InputOption('rotate', 'r', InputOption::VALUE_NONE, 'Re-encrypts existing secrets with the newly generated keys.'),
42+
])
43+
->setDescription('Generates new encryption keys.')
44+
->setHelp(<<<'EOF'
45+
The <info>%command.name%</info> command generates a new encryption key.
46+
47+
%command.full_name%
48+
49+
If encryption keys already exist, the command must be called with
50+
the <info>--rotate</info> option in order to override those keys and re-encrypt
51+
exiting secrets.
52+
53+
%command.full_name% --rotate
54+
EOF
55+
)
56+
;
57+
}
58+
59+
protected function execute(InputInterface $input, OutputInterface $output): int
60+
{
61+
$io = new SymfonyStyle($input, $output);
62+
63+
if (!$input->getOption('rotate')) {
64+
if ($this->vault->generateKeys()) {
65+
$io->success('New keys have been generated.');
66+
67+
return 0;
68+
}
69+
70+
$io->error('Some keys already exist and won\'t be overridden.');
71+
72+
return 1;
73+
}
74+
75+
$secrets = [];
76+
foreach ($this->vault->list(true) as $name => $decryptedSecret) {
77+
$secrets[$name] = $decryptedSecret;
78+
}
79+
80+
$this->vault->generateKeys(true);
81+
$io->success('New keys have been generated.');
82+
83+
if ($secrets) {
84+
foreach ($secrets as $name => &$decryptedSecret) {
85+
$this->vault->seal($name, $decryptedSecret);
86+
}
87+
88+
$io->success('Existing secrets have been rotated to the new keys.');
89+
}
90+
91+
$io->caution('DO NOT COMMIT THE DECRYPTION KEY FOR THE PROD ENVIRONMENT⚠️');
92+
93+
return 0;
94+
}
95+
}

src/Symfony/Bundle/FrameworkBundle/Command/SecretsListCommand.php

Lines changed: 16 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,7 @@
1111

1212
namespace Symfony\Bundle\FrameworkBundle\Command;
1313

14-
use Symfony\Bundle\FrameworkBundle\Exception\EncryptionKeyNotFoundException;
15-
use Symfony\Bundle\FrameworkBundle\Secret\Storage\SecretStorageInterface;
14+
use Symfony\Bundle\FrameworkBundle\Secrets\SodiumVault;
1615
use Symfony\Component\Console\Command\Command;
1716
use Symfony\Component\Console\Input\InputInterface;
1817
use Symfony\Component\Console\Input\InputOption;
@@ -27,11 +26,11 @@ final class SecretsListCommand extends Command
2726
{
2827
protected static $defaultName = 'debug:secrets';
2928

30-
private $secretStorage;
29+
private $vault;
3130

32-
public function __construct(SecretStorageInterface $secretStorage)
31+
public function __construct(SodiumVault $vault)
3332
{
34-
$this->secretStorage = $secretStorage;
33+
$this->vault = $vault;
3534

3635
parent::__construct();
3736
}
@@ -48,7 +47,7 @@ protected function configure()
4847
4948
%command.full_name%
5049
51-
When the the option <info>--reveal</info> is provided, the decrypted secrets are also displayed.
50+
When the option <info>--reveal</info> is provided, the decrypted secrets are also displayed.
5251
5352
%command.full_name% --reveal
5453
EOF
@@ -60,33 +59,26 @@ protected function configure()
6059
->addOption('reveal', 'r', InputOption::VALUE_NONE, 'Display decrypted values alongside names');
6160
}
6261

63-
protected function execute(InputInterface $input, OutputInterface $output)
62+
protected function execute(InputInterface $input, OutputInterface $output): int
6463
{
65-
$reveal = $input->getOption('reveal');
6664
$io = new SymfonyStyle($input, $output);
6765

68-
try {
69-
$secrets = $this->secretStorage->listSecrets($reveal);
70-
} catch (EncryptionKeyNotFoundException $e) {
71-
throw new \LogicException(sprintf('Unable to decrypt secrets, the encryption key "%s" is missing.', $e->getKeyLocation()));
66+
if (!$reveal = $input->getOption('reveal')) {
67+
$io->comment(sprintf('To reveal the secrets run <info>php %s %s --reveal</info>', $_SERVER['PHP_SELF'], $this->getName()));
7268
}
7369

74-
if ($reveal) {
75-
$rows = [];
76-
foreach ($secrets as $name => $value) {
77-
$rows[] = [$name, $value];
78-
}
79-
$io->table(['name', 'secret'], $rows);
70+
$secrets = $this->vault->list($reveal);
8071

81-
return;
72+
$rows = [];
73+
foreach ($secrets as $name => $value) {
74+
$rows[] = [$name, $value ?? '********'];
8275
}
76+
$io->table(['name', 'secret'], $rows);
8377

84-
$rows = [];
85-
foreach ($secrets as $name => $_) {
86-
$rows[] = [$name];
78+
if ($reveal && $secrets && null === $value) {
79+
$io->comment('Secrets could not be revealed as not decryption key has been found.');
8780
}
8881

89-
$io->comment(sprintf('To reveal the values of the secrets use <info>php %s %s --reveal</info>', $_SERVER['PHP_SELF'], $this->getName()));
90-
$io->table(['name'], $rows);
82+
return 0;
9183
}
9284
}

src/Symfony/Bundle/FrameworkBundle/Command/SecretsRemoveCommand.php

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111

1212
namespace Symfony\Bundle\FrameworkBundle\Command;
1313

14-
use Symfony\Bundle\FrameworkBundle\Secret\Storage\MutableSecretStorageInterface;
14+
use Symfony\Bundle\FrameworkBundle\Secrets\SodiumVault;
1515
use Symfony\Component\Console\Command\Command;
1616
use Symfony\Component\Console\Input\InputArgument;
1717
use Symfony\Component\Console\Input\InputInterface;
@@ -25,11 +25,11 @@ final class SecretsRemoveCommand extends Command
2525
{
2626
protected static $defaultName = 'secrets:remove';
2727

28-
private $secretsStorage;
28+
private $vault;
2929

30-
public function __construct(MutableSecretStorageInterface $secretsStorage)
30+
public function __construct(SodiumVault $vault)
3131
{
32-
$this->secretsStorage = $secretsStorage;
32+
$this->vault = $vault;
3333

3434
parent::__construct();
3535
}
@@ -50,12 +50,16 @@ protected function configure()
5050
;
5151
}
5252

53-
protected function execute(InputInterface $input, OutputInterface $output)
53+
protected function execute(InputInterface $input, OutputInterface $output): int
5454
{
5555
$io = new SymfonyStyle($input, $output);
5656

57-
$this->secretsStorage->removeSecret($input->getArgument('name'));
57+
if ($this->vault->remove($input->getArgument('name'))) {
58+
$io->success('Secret was successfully removed.');
59+
} else {
60+
$io->text('No secret was found with that name.');
61+
}
5862

59-
$io->success('Secret was successfully removed.');
63+
return 0;
6064
}
6165
}

0 commit comments

Comments
 (0)
0