]> Dogcows Code - chaz/groupsecret/blob - bin/groupsecret
0701d69216ea2f94199392a15c16260b8d536918
[chaz/groupsecret] / bin / groupsecret
1 #!perl
2 # PODNAME: groupsecret
3 # ABSTRACT: A simple tool for maintaining a shared group secret
4
5 =head1 SYNOPSIS
6
7 groupsecret [--version] [--help] [-f <filepath>] [-k <privatekey_path>]
8 <command> [<args>]
9
10 groupsecret add-key [--embed] [--update] <publickey_path> ...
11
12 groupsecret delete-key <fingerprint>|<publickey_path> ...
13
14 groupsecret list-keys
15
16 groupsecret set-secret [--keep-passphrase] <path>|-|rand:<num_bytes>
17
18 groupsecret [print-secret] [--no-decrypt]
19
20 =head1 DESCRIPTION
21
22 L<groupsecret> is a program that makes it easy for groups to share a secret between themselves
23 without exposing the secret to anyone else. It could be used, for example, by a team to share an
24 L<ansible-vault(1)> password; see L</ansible-vault> for more about this particular use case.
25
26 The goal of this program is to be easy to use and have few dependencies (or only have dependencies
27 users are likely to already have installed).
28
29 groupsecret works by encrypting a secret with a symmetric cipher protected by a secure random
30 passphrase which is itself encrypted by one or more SSH2 RSA public keys. Only those who have access
31 to one of the corresponding private keys are able to decrypt the passphrase and access the secret.
32
33 The encrypted secret and passphrase are stored in a single keyfile. You can even commit the keyfile
34 in a public repo or in a private repo where some untrusted users may have read access; the secret is
35 locked away to all except those with a private key to a corresponding public key that has been added
36 to the keyfile.
37
38 The keyfile is just a YAML file, so it's human-readable (except of course for the encrypted parts).
39 This make it easy to add to version control and work with diffs. You can edit the keyfile by hand if
40 you learn its very simple structure, but this program makes it even easier to manage the keyfile.
41
42 =head1 OPTIONS
43
44 =head2 --version
45
46 Print the program name and version to C<STDOUT>, and exit.
47
48 Alias: C<-v>
49
50 =head2 --help
51
52 Print the synopsis to C<STDOUT>, and exit.
53
54 Alias: C<-h>
55
56 =head2 --file=path
57
58 Specify a path to a keyfile which stores a secret and keys.
59
60 Defaults to the value of the environment variable L</GROUPSECRET_KEYFILE> or F<groupsecret.yml>.
61
62 Alias: C<-f>
63
64 =head2 --private-key=path
65
66 Specify a path to a PEM private key. This is used by some commands to decrypt the passphrase that
67 protects the secret and is ignored by commands that don't need it.
68
69 Defaults to the value of the environment variable L</GROUPSECRET_PRIVATE_KEY> or F<~/.ssh/id_rsa>.
70
71 Alias: C<-k>
72
73 =head1 COMMANDS
74
75 =head2 add-key
76
77 groupsecret add-key path/to/mykey_rsa.pub
78
79 Adds one or more SSH2 RSA public keys to a keyfile. This allows the secret contained within the
80 keyfile to be accessed by whoever has the corresponding private key.
81
82 If the C<--embed> option is used, the public keys will be embeded in the keyfile. This may be
83 a useful way to make sure the actual keys are available in the future since they could be needed to
84 encrypt a new passphrase if it ever needs to be changed. Keys that are not embedded will be searched
85 for in the filesystem; see L</GROUPSECRET_PATH>.
86
87 If the C<--update> option is used and a key with the same fingerprint is added, the new key will
88 replace the existing key. The default behavior is to skip existing keys.
89
90 If the keyfile is storing a secret, the passphrase protecting the secret will need to be decrypted
91 so that access to the secret can be shared with the new key(s).
92
93 Alias: C<add-keys>
94
95 =head2 delete-key
96
97 groupsecret delete-key MD5:89:b3:fb:76:6c:f9:56:8e:a8:1a:df:ba:1c:ba:7d:05
98 groupsecret delete-key path/to/mykey_rsa.pub
99
100 Deletes one or more keys from a keyfile. This prevents the secret contained within the keyfile from
101 being accessed by whoever has the corresponding private key.
102
103 Of course, if the owners of the key(s) being removed have already had access to the keyfile prior to
104 their keys being removed, the secret is already exposed to them. It usually makes sense to follow up
105 this command with a L</set-secret> command in order to change the secret.
106
107 Aliases: C<delete-keys>, C<remove-key>, C<remove-keys>
108
109 =head2 list-keys
110
111 groupsecret list-keys
112
113 Prints the keys that have access to the secret contained in the keyfile to C<STDOUT>, one per line
114 in the following format:
115
116 <fingerprint> <comment>
117
118 =head2 set-secret
119
120 groupsecret set-secret path/to/secretfile.txt
121 groupsecret set-secret - <<END
122 > it's a secret to everybody
123 > END
124 groupsecret set-secret rand:48
125
126 Set or update the secret contained in a keyfile. The argument allows you to add a secret from
127 a file, from <STDIN>, or from a stream of secure random bytes.
128
129 If the keyfile already contains a secret, it will be replaced by the new secret. A keyfile can only
130 contain one secret at a time. If you think you want to store more than one secret at a time, store
131 a tarball instead.
132
133 By default, this will also change the passphrase protecting the secret and re-encrypt the passphrase
134 for each key currently in the keyfile. This requires all of the public keys to be available (see
135 L</GROUPSECRET_PATH>). If for some reason you want to protect the new secret with the current
136 passphrase, use the C<--keep-passphrase> option; this can be done without the public keys being
137 available, but it will require a private key to decrypt the passphrase.
138
139 Aliases: C<change-secret>, C<update-secret>
140
141 =head2 print-secret
142
143 groupsecret print-secret
144 groupsecret print-secret --no-decrypt
145
146 Print the secret contained in the keyfile to C<STDOUT>.
147
148 If the C<--no-decrypt> option is used, the secret will be printed in its encrypted form.
149
150 This requires a private key.
151
152 Aliases: (no command), C<show-secret>
153
154 =head1 REQUIREMENTS
155
156 =for :list
157 * L<OpenSSH|https://www.openssh.com> (commands: L<ssh-keygen(1)>)
158 * L<OpenSSL|https://www.openssl.org> (commands: L<openssl(1)>)
159
160 =head1 INSTALL
161
162 There are a few ways to install groupsecret to your system. First, make sure you first have the
163 L</REQUIREMENTS> installed.
164
165 =head2 Using cpanm
166
167 You can install groupsecret using L<cpanm>. If you have a local perl (plenv, perlbrew, etc.), you
168 can just do this:
169
170 cpanm App::GroupSecret
171
172 to install the F<groupsecret> executable and its Perl module dependencies. The executable will be
173 installed to your perl's bin path, like F<~/perl5/perlbrew/bin/groupsecret>.
174
175 If you're installing to your system perl, you can do:
176
177 cpanm --sudo App::GroupSecret
178
179 to install the F<groupsecret> executable to a system directory, like F</usr/local/bin/groupsecret>
180 (depending on your perl).
181
182 =head2 For developers
183
184 If you're a developer and want to hack on the source, clone the repository and pull the
185 dependencies:
186
187 git clone https://github.com/chazmcgarvey/groupsecret.git
188 cd groupsecret
189 cpanm Dist::Zilla
190 dzil authordeps --missing | cpanm
191 dzil listdeps --author --develop --missing | cpanm
192
193 =head1 ENVIRONMENT
194
195 =head2 GROUPSECRET_KEYFILE
196
197 If set, this program will use the value as a path to the keyfile. The L</--file=path> option takes
198 precedence if it is used.
199
200 =head2 GROUPSECRET_PRIVATE_KEY
201
202 If set, this program will use the value as a path to the keyfile. The L</--private-key=path> option
203 takes precedence if it is used.
204
205 =head2 GROUPSECRET_PATH
206
207 The value of this variable should be a colon-separated list of directories in which to search for
208 public keys. By default, the actual keys are not embedded in keyfiles, but they may be needed to
209 encrypt a new passphrase if it ever needs to be changed. Keys that are not embedded will be searched
210 for in the filesystem based on the value of this environment variable.
211
212 Defaults to C<.:keys:$HOME/.ssh>.
213
214 =head1 EXAMPLES
215
216 =head2 ansible-vault
217
218 L<Ansible Vault|http://docs.ansible.com/ansible/latest/vault.html> is a great way to securely store
219 secret configuration variables for use in your playbooks. Vaults are secured using a password, which
220 is okay if you're the only one who will need to unlock the Vault, but as soon as you add team
221 members who also need to access the Vault you are then faced with how to manage knowledge of the
222 password. When a team member leaves, you'll also need to change the Vault password which means
223 you'll need a way to communicate the change to other team members who also have access. This becomes
224 a burden to manage.
225
226 You can use groupsecret to manage this very easily by storing the Vault password in a groupsecret
227 keyfile. That way, you can add or remove keys and change the secret (the Vault password) at any time
228 without affecting the team members that still have access. Team members always use their own SSH2
229 RSA keys to unlock the Vault, so no new password ever needs to be communicated out.
230
231 To set this up, first create a keyfile with the public keys of everyone on your team:
232
233 groupsecret -f vault-password.yml add-keys keys/*_rsa.pub
234
235 Then set the secret in the keyfile to a long random number:
236
237 groupsecret -f vault-password.yml set-secret rand:48
238
239 This will be the Ansible Vault password. You can see it if you want using the L</print-secret>
240 command, but you don't need to.
241
242 Finally, we'll take advantage of the fact that a Ansible Vault password file can be an executable
243 program that prints the Vault password to C<STDOUT>. Create a file named F<vault-password> with the
244 following script, and make it executable (C<chmod +x vault-password>):
245
246 #!/bin/sh
247 # Use groupsecret <https://github.com/chazmcgarvey/groupsecret> to access the Vault password
248 exec ${GROUPSECRET:-groupsecret} -f vault-password.yml print-secret
249
250 Commit both F<vault-password> and F<vault-password.yml> to your repository.
251
252 Now use L<ansible-vault(1)> to add files to the Vault:
253
254 ansible-vault --vault-id=vault-password encrypt foo.yml bar.yml baz.yml
255
256 These examples show the Ansible 2.4+ syntax, but it can be adapted for earlier versions. The
257 significant part of this command is C<--vault-id=vault-password> which refers to the executable
258 script we created earlier. You can use that argument with other ansible-vault commands to view or
259 edit the encrypted files.
260
261 You can also pass that same argument to C<ansible-playbook(1)> in order to use the Vault in
262 playbooks that refer to the encrypted variables:
263
264 ansible-playbook -i myinventory --vault-id=vault-password site.yml
265
266 What this does is execute F<vault-password> which executes groupsecret to print the secret contained
267 in the F<vault-password.yml> file (which is actually the Vault password) to <STDOUT>. In order to do
268 this, groupsecret will decrypt the keyfile passphrase using any one of the private keys that have
269 associated public keys added to the keyfile.
270
271 That's it! Pretty easy.
272
273 If and when you need to change the Vault password (such as when a team member leaves), you can
274 follow this procedure which is probably mostly self-explanatory:
275
276 groupsecret -f vault-password.yml delete-key keys/revoked/jdoe_rsa.pub
277 groupsecret -f vault-password.yml print-secret >old-vault-password.txt
278 groupsecret -f vault-password.yml set-secret rand:48
279 echo "New Vault password: $(groupsecret -f vault-password.yml)"
280 ansible-vault --vault-id=old-vault-password.txt rekey foo.yml bar.yml baz.yml
281 # You will be prompted for the new Vault password which you can copy from the output above.
282 rm -f old-vault-password.txt
283
284 This removes access to the keyfile secret and to the Ansible Vault. Don't forget that you may also
285 want to change the variables being protected by the Vault. After all, those secrets are the actual
286 things we're protecting by doing all of this, and an exiting team member may have decided to take
287 a copy of those variables for himself before leaving.
288
289 =cut
290
291 use warnings FATAL => 'all';
292 use strict;
293
294 our $VERSION = '9999.999'; # VERSION
295
296 use App::GroupSecret;
297
298 App::GroupSecret->new->main(@ARGV);
299 exit;
This page took 0.049025 seconds and 3 git commands to generate.