| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596 |
- #!/usr/bin/env perl
- use 5.040;
- use re '/aasx';
- use warnings FATAL => 'all';
- use FindBin::libs;
- use Crypt::Misc ();
- use Crypt::PRNG ();
- use Getopt::Long qw{GetOptions};
- use Trog::Vault ();
- =head1 SYNOPSIS
- Write a vault key into config/secrets.key, for a tCMS that is not being handed
- one by the service that starts it.
- =head2 USAGE
- bin/tcms-vault-key --file
- =head2 WHEN YOU WANT THIS
- Developing, mostly. Running tPSGI by hand -- HOME=. bin/tpsgi -p 5001, in a
- tmux, so you can watch it and restart it -- is the ordinary way to work on tCMS,
- and nothing in that path hands over a vault key: there is no unit importing a
- systemd credential, and service/tpsgi.sh, which is what would have put it in the
- environment, never ran. So /secrets does not work until you put a key where
- Trog::Vault can find one, and this is how.
- The other time is a machine with no systemd at all, and there it is a compromise
- rather than a convenience.
- =head2 WHEN YOU DO NOT
- On a production host. tPSGI's bin/build_service makes the key and puts it in
- systemd's credential store, sealed to this machine if it has a TPM to seal it to,
- and its unit hands it to tCMS before it chroots -- so the key is never on a disk
- that tCMS can see, and never in a backup of tCMS.
- A key file in the checkout gives up exactly that. It sits next to the database
- it protects, so whatever copies one copies the other, and the encryption stops
- being worth anything the moment both are somewhere they should not be.
- .gitignore keeps it out of the repository; keeping it out of your backups is on
- you.
- =head2 WHAT THE KEY 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.
- There is one key per installation and it does not need rotating on a schedule.
- It is meant to live and die with the machine -- nothing backs it up, and
- trog-provisioner does not carry it onto a rebuilt guest -- so a rebuild loses
- every stored secret and each user stores theirs again. That is the intended
- cost of the key not being anywhere the database is.
- Replacing it has the same effect. Everything sealed under the old one stops
- opening and says so, on /secrets and wherever it was going to be used, rather
- than presenting as corruption or as a thing worth trying again.
- =cut
- my ( $file, $help );
- GetOptions(
- 'file' => \$file,
- 'help' => \$help,
- ) or exit 2;
- if ( $help || !$file ) {
- print "Usage: bin/tcms-vault-key --file\n\n";
- print "Writes $Trog::Vault::key_file, for a tCMS nothing is handing a vault key to:\n";
- print "running tPSGI by hand while developing, or a machine with no systemd.\n";
- print "On a production host tPSGI's bin/build_service does this properly, and better;\n";
- print "see perldoc bin/tcms-vault-key.\n";
- exit( $help ? 0 : 2 );
- }
- # 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 ) { # 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} Crypt::Misc::encode_b64( Crypt::PRNG::random_bytes(32) ) . "\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 "Fine for development. On a production host, let tPSGI's bin/build_service hand one\n";
- print "over instead, so the key is not sitting next to the database it protects.\n";
|