#!/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 Write a vault key into config/secrets.key, for an installation that is not run by systemd. =head2 USAGE bin/tcms-vault-key --file =head2 YOU PROBABLY DO NOT WANT THIS 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. That is the way to do this, and it needs nothing from here. This is the fallback for a machine with no systemd, and it is genuinely weaker: a key file beside the database it protects is a key file in the same backup as the database it protects. Take the trade knowingly or not at all. =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 an installation with no systemd.\n"; print "Under systemd, tPSGI's bin/build_service does this properly; 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 ) { ## 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} 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 "Back it up somewhere that is not where you back up config/auth.db, or do not back it up at all.\n";