1 package App
::GroupSecret
;
2 # ABSTRACT: A simple tool for maintaining a shared group secret
6 This module is part of the command-line interface for managing keyfiles.
8 See L<groupsecret> for documentation.
15 our $VERSION = '9999.999'; # VERSION
17 use App
::GroupSecret
::Crypt
qw(generate_secure_random_bytes read_openssh_key_fingerprint);
18 use App
::GroupSecret
::File
;
19 use Getopt
::Long
qw(GetOptionsFromArray);
26 return bless {}, $class;
39 # Parse options using pass_through so that we can pick out the global
40 # options, wherever they are in the arg list, and leave the rest to be
41 # parsed by each individual command.
42 Getopt
::Long
::Configure
('pass_through');
45 'file|f=s' => \
$filepath,
47 'manual|man' => \
$man,
48 'private-key|k=s' => \
$private_key,
49 'version|v' => \
$version,
51 Getopt
::Long
::Configure
('default');
53 pod2usage
(-exitval
=> 1, -verbose
=> 99, -sections
=> [qw(SYNOPSIS OPTIONS COMMANDS)]) if $help;
54 pod2usage
(-verbose
=> 2) if $man;
55 return print "groupsecret ${VERSION}\n" if $version;
57 $self->{private_key
} = $private_key if $private_key;
58 $self->{filepath
} = $filepath if $filepath;
62 add_keys
=> 'add_key',
63 change_secret
=> 'set_secret',
64 delete_key
=> 'delete_key',
65 delete_keys
=> 'delete_key',
66 list_keys
=> 'list_keys',
67 print => 'print_secret',
68 print_secret
=> 'print_secret',
69 remove_key
=> 'delete_key',
70 remove_keys
=> 'delete_key',
71 set_secret
=> 'set_secret',
72 show_secret
=> 'print_secret',
73 update_secret
=> 'set_secret',
76 unshift @args, 'print' if !@args || $args[0] =~ /^-/;
78 my $command = shift @args;
79 my $lookup = $command;
81 my $method = 'action_' . ($commands{$lookup} || '');
83 if (!$self->can($method)) {
84 warn "Unknown command: $command\n";
88 $self->$method(@args);
92 shift-
>{filepath
} ||= $ENV{GROUPSECRET_KEYFILE
} || 'groupsecret.yml';
98 return $self->{file
} ||= App
::GroupSecret
::File-
>new($self->filepath);
102 shift-
>{private_key
} ||= $ENV{GROUPSECRET_PRIVATE_KEY
} || "$ENV{HOME}/.ssh/id_rsa";
105 sub action_print_secret
{
111 'decrypt!' => \
$decrypt,
114 my $file = $self->file;
115 die "No secret in file -- use the \`set-secret' command to set one.\n" if !$file->secret;
118 my $private_key = $self->private_key;
119 my $secret = $file->decrypt_secret(private_key
=> $private_key) or die "No secret.\n";
127 sub action_set_secret
{
130 my $keep_passphrase = 0;
133 'keep-passphrase!' => \
$keep_passphrase,
136 my $secret_spec = shift;
138 warn "You must specify a secret to set.\n";
145 if ($secret_spec =~ /^rand:(\d+)$/i) {
146 my $rand = encode_base64
(generate_secure_random_bytes
($1), '');
149 elsif ($secret_spec eq '-') {
150 my $in = do { local $/; <STDIN
> };
153 elsif ($secret_spec =~ /^file:(.*)$/i) {
157 $secret = $secret_spec;
160 my $file = $self->file;
162 if ($keep_passphrase) {
163 my $private_key = $self->private_key;
164 $passphrase = $file->decrypt_secret_passphrase($private_key);
165 $file->encrypt_secret($secret, $passphrase);
168 $passphrase = generate_secure_random_bytes
(32);
169 $file->encrypt_secret($secret, $passphrase);
170 $file->encrypt_secret_passphrase($passphrase);
184 'update|u' => \
$update,
187 my $file = $self->file;
188 my $keys = $file->keys;
190 my $opts = {embed
=> $embed};
192 for my $public_key (@_) {
193 my $info = read_openssh_key_fingerprint
($public_key);
195 if ($keys->{$info->{fingerprint
}} && !$update) {
196 my $formatted_key = $file->format_key($info);
197 print "SKIP\t$formatted_key\n";
201 if ($file->secret && !$opts->{passphrase
}) {
202 my $private_key = $self->private_key;
203 my $passphrase = $file->decrypt_secret_passphrase($private_key);
204 $opts->{passphrase
} = $passphrase;
207 local $opts->{fingerprint_info
} = $info;
208 my ($fingerprint, $key) = $file->add_key($public_key, $opts);
210 local $key->{fingerprint
} = $fingerprint;
211 my $formatted_key = $file->format_key($key);
212 print "ADD\t$formatted_key\n";
218 sub action_delete_key
{
221 my $file = $self->file;
223 for my $fingerprint (@_) {
224 if ($fingerprint =~ s/^(?:MD5|SHA1|SHA256)://) {
225 $fingerprint =~ s/://g;
228 my $info = read_openssh_key_fingerprint
($fingerprint);
229 $fingerprint = $info->{fingerprint
};
232 my $key = $file->keys->{$fingerprint};
233 $file->delete_key($fingerprint) if $key;
235 local $key->{fingerprint
} = $fingerprint;
236 my $formatted_key = $file->format_key($key);
237 print "DELETE\t$formatted_key\n";
243 sub action_list_keys
{
246 my $file = $self->file;
247 my $keys = $file->keys;
249 while (my ($fingerprint, $key) = each %$keys) {
250 local $key->{fingerprint
} = $fingerprint;
251 my $formatted_key = $file->format_key($key);
252 print "$formatted_key\n";