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

Skip to content

Commit 1c0fbb7

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

33 files changed

+451
-961
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/Bridge/Twig/Command/LintCommand.php

Lines changed: 2 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -81,15 +81,15 @@ protected function execute(InputInterface $input, OutputInterface $output)
8181
$showDeprecations = $input->getOption('show-deprecations');
8282

8383
if (['-'] === $filenames) {
84-
return $this->display($input, $output, $io, [$this->validate($this->getStdin(), uniqid('sf_', true))]);
84+
return $this->display($input, $output, $io, [$this->validate(file_get_contents('php://stdin'), uniqid('sf_', true))]);
8585
}
8686

8787
if (!$filenames) {
8888
// @deprecated to be removed in 5.0
8989
if (0 === ftell(STDIN)) {
9090
@trigger_error('Piping content from STDIN to the "lint:twig" command without passing the dash symbol "-" as argument is deprecated since Symfony 4.4.', E_USER_DEPRECATED);
9191

92-
return $this->display($input, $output, $io, [$this->validate($this->getStdin(), uniqid('sf_', true))]);
92+
return $this->display($input, $output, $io, [$this->validate(file_get_contents('php://stdin'), uniqid('sf_', true))]);
9393
}
9494

9595
$loader = $this->twig->getLoader();
@@ -132,16 +132,6 @@ protected function execute(InputInterface $input, OutputInterface $output)
132132
return $this->display($input, $output, $io, $filesInfo);
133133
}
134134

135-
private function getStdin(): string
136-
{
137-
$template = '';
138-
while (!feof(STDIN)) {
139-
$template .= fread(STDIN, 1024);
140-
}
141-
142-
return $template;
143-
}
144-
145135
private function getFilesInfo(array $filenames): array
146136
{
147137
$filesInfo = [];

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/SecretsAddCommand.php

Lines changed: 0 additions & 70 deletions
This file was deleted.

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

Lines changed: 0 additions & 97 deletions
This file was deleted.
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
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+
$io->caution('DO NOT COMMIT THE DECRYPTION KEY FOR THE PROD ENVIRONMENT⚠️');
67+
68+
return 0;
69+
}
70+
71+
$io->error('Some keys already exist and won\'t be overridden.');
72+
73+
return 1;
74+
}
75+
76+
$secrets = [];
77+
foreach ($this->vault->list(true) as $name => $value) {
78+
$secrets[$name] = $value;
79+
}
80+
81+
$this->vault->generateKeys(true);
82+
$io->success('New keys have been generated.');
83+
84+
if ($secrets) {
85+
foreach ($secrets as $name => $value) {
86+
$this->vault->seal($name, $value);
87+
}
88+
89+
$io->success('Existing secrets have been rotated to the new keys.');
90+
}
91+
92+
$io->caution('DO NOT COMMIT THE DECRYPTION KEY FOR THE PROD ENVIRONMENT⚠️');
93+
94+
return 0;
95+
}
96+
}

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+
publi F438 c 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', 'Value'], $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
}

0 commit comments

Comments
 (0)
0