| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104 |
- #!/usr/bin/env perl
- use v5.36;
- use re '/aa';
- use FindBin::libs;
- use Crypt::Misc ();
- use Crypt::PRNG ();
- use Getopt::Long qw{GetOptions};
- use Trog::Vault ();
- =head1 SYNOPSIS
- Make the key that seals this installation's stored secrets, and say where to put
- it.
- =head2 USAGE
- bin/tcms-vault-key # print a key and the commands to install it
- bin/tcms-vault-key --file # write config/secrets.key instead
- =head2 WHAT THIS IS
- Every secret a user stores through /secrets is encrypted under a key derived
- from this one. It is not in the database, which is the whole point: without it
- config/auth.db is ciphertext. Losing it loses every stored secret, and there is
- no way around that -- they go back to typing the password, which is what they
- did before.
- There is one key per installation, and it does not need rotating on a schedule.
- If you do replace it, everything sealed under the old one stops opening and says
- so rather than presenting as corruption; the fix is for each user to store
- theirs again.
- =head2 WHERE TO PUT IT
- In systemd's credential store, which is what the default output tells you how to
- do. tPSGI's unit imports it from there and hands it to tCMS in the environment,
- tCMS takes it back out of the environment as it loads, and it never touches a
- disk anywhere on the way. Encrypted with systemd-creds it is also sealed to
- this machine, so the file is useless if it walks.
- --file is the fallback for a machine that is not running systemd. It writes
- config/secrets.key, mode 600, and that is genuinely weaker: a key beside the
- database it protects is a key in the same backup as the database it protects.
- =head2 CAVEATS
- Prints a key to your terminal, which is a place keys end up in scrollback. The
- default output is meant to be piped rather than read.
- =cut
- my ( $file, $help );
- GetOptions(
- 'file' => \$file,
- 'help' => \$help,
- ) or exit 2;
- if ($help) {
- print "Usage: bin/tcms-vault-key [--file]\n";
- exit 0;
- }
- my $key = Crypt::Misc::encode_b64( Crypt::PRNG::random_bytes(32) );
- if ($file) {
- # Printed and exited rather than died: Carp::Always is in the dependency
- # chain, and refusing to clobber a key is not a crash.
- if ( -e $Trog::Vault::key_file ) { ## no critic (ProhibitFiletest_e) -- refusing to overwrite the one thing that cannot be regenerated
- print STDERR "There is already a key at $Trog::Vault::key_file. Move it out of the way if you really mean to replace it, and read what that costs first: perldoc bin/tcms-vault-key\n";
- exit 2;
- }
- open( my $fh, '>', $Trog::Vault::key_file ) or die "Could not write $Trog::Vault::key_file: $!\n";
- print {$fh} "$key\n";
- close($fh);
- chmod( oct('600'), $Trog::Vault::key_file ) or die "Could not chmod $Trog::Vault::key_file: $!\n";
- print "Wrote $Trog::Vault::key_file. Restart tCMS to pick it up.\n";
- print "This is the weaker of the two places to keep it: see perldoc bin/tcms-vault-key.\n";
- exit 0;
- }
- print <<"COMMANDS";
- A new vault key. Put it in systemd's credential store, sealed to this machine:
- echo '$key' | sudo systemd-creds encrypt --name=tcms-vault - /etc/credstore.encrypted/tcms-vault
- ...or unsealed, if this machine has no TPM:
- echo '$key' | sudo tee /etc/credstore/tcms-vault > /dev/null
- sudo chmod 600 /etc/credstore/tcms-vault
- Then restart the service. tPSGI's unit imports it and hands it over before it
- chroots; see the Service configuration section of its Readme.
- Losing this key loses every secret stored under it, and it is not in any backup
- you take of tCMS, which is the point. Keep it wherever you keep the things you
- cannot regenerate.
- COMMANDS
|