sskm: minimum changes, minimally smoke tested!
(thanks to fabian@hirschm.net for testing more thoroughly as well)
This commit is contained in:
parent
48f1e7c781
commit
c5e0e929a7
|
@ -145,7 +145,6 @@
|
||||||
<node CREATED="1333327082853" ID="ID_488765250" MODIFIED="1333327089444" TEXT="log file format, LOG_EXTRA"/>
|
<node CREATED="1333327082853" ID="ID_488765250" MODIFIED="1333327089444" TEXT="log file format, LOG_EXTRA"/>
|
||||||
<node CREATED="1333301298504" ID="ID_60946303" MODIFIED="1333301300418" TEXT="smart http"/>
|
<node CREATED="1333301298504" ID="ID_60946303" MODIFIED="1333301300418" TEXT="smart http"/>
|
||||||
<node CREATED="1333301308136" ID="ID_1900285587" MODIFIED="1333301311863" TEXT="hub"/>
|
<node CREATED="1333301308136" ID="ID_1900285587" MODIFIED="1333301311863" TEXT="hub"/>
|
||||||
<node CREATED="1333301312124" ID="ID_843247306" MODIFIED="1333301313052" TEXT="sskm"/>
|
|
||||||
<node CREATED="1333328274461" ID="ID_248606591" MODIFIED="1333328277083" TEXT="mob branches"/>
|
<node CREATED="1333328274461" ID="ID_248606591" MODIFIED="1333328277083" TEXT="mob branches"/>
|
||||||
<node CREATED="1333328277387" ID="ID_1027016949" MODIFIED="1333328280083" TEXT="password access"/>
|
<node CREATED="1333328277387" ID="ID_1027016949" MODIFIED="1333328280083" TEXT="password access"/>
|
||||||
</node>
|
</node>
|
||||||
|
|
242
doc/sskm.mkd
Normal file
242
doc/sskm.mkd
Normal file
|
@ -0,0 +1,242 @@
|
||||||
|
# changing keys -- self service key management
|
||||||
|
|
||||||
|
[Note on g3 version: this has been manually spot-tested; there is no test suite. Changes from g2 version are minimal so it should all work fine but please report errors!]
|
||||||
|
|
||||||
|
Follow this guide to add keys to or remove keys from your account. Note that you cannot use this method to add your *first* key to the account; you must still email your initial key to your admin.
|
||||||
|
|
||||||
|
The key management is done using a command called `sskm`. This command must be enabled for remote use by the admin (see [here][commands] for more on this).
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
[[TOC]]
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
## Important!
|
||||||
|
|
||||||
|
There are a few things that you should know before using the key management system. Please do not ignore this section!
|
||||||
|
|
||||||
|
### Key fingerprints
|
||||||
|
|
||||||
|
Keys are identified in some of these subcommands by their fingerprints. To see the fingerprint for a public key on your computer, use the following syntax:
|
||||||
|
|
||||||
|
ssh-keygen -l -f <path_to_public_key.pub>
|
||||||
|
|
||||||
|
You'll get output like:
|
||||||
|
|
||||||
|
jeff@baklava ~ $ ssh-keygen -l -f .ssh/jeffskey.pub
|
||||||
|
2048 2d:78:d4:2c:b1:6d:9a:dc:d9:0d:94:3c:d8:c2:65:44 .ssh/jeffskey.pub (RSA)
|
||||||
|
|
||||||
|
### Active keys
|
||||||
|
|
||||||
|
Any keys that you can use to interact with the system are active keys. (Inactive keys are keys that are, for instance, scheduled to be added or removed.) Keys are identified with their `keyid`; see the section below on listing keys.
|
||||||
|
|
||||||
|
If you have no current active keys, you will be locked out of the system (in which case email your admin for help). Therefore, be sure that you are never removing your only active key!
|
||||||
|
|
||||||
|
### Selecting which key to use
|
||||||
|
|
||||||
|
Although you can identify yourself to the Gitolite system with any of your active keys on the server, at times it is necessary to specifically pick which key you are identifying with. To pick the key to use, pass the `-i` flag into `ssh`:
|
||||||
|
|
||||||
|
jeff@baklava ~ $ ssh -i .ssh/jeffskey git@git info
|
||||||
|
hello jeff, the gitolite version here is v2.0.1-11-g1cd3414
|
||||||
|
the gitolite config gives you the following access:
|
||||||
|
@C R W [a-zA-Z0-9][a-zA-Z0-9_\-\.]+[a-zA-Z0-9]
|
||||||
|
....
|
||||||
|
|
||||||
|
*N.B.*: If you have any keys loaded into `ssh-agent` (i.e., `ssh-add -l` shows
|
||||||
|
at least one key), then this may not work properly. `ssh` has a bug which
|
||||||
|
makes it ignore `-i` values when that key has not been loaded into the agent.
|
||||||
|
One solution is to add the key you want to use (e.g., `ssh-add
|
||||||
|
.ssh/jeffskey`). The other is to remove *all* the keys from the agent or
|
||||||
|
disable the agent, using one of these commands:
|
||||||
|
|
||||||
|
* Terminate `ssh-agent` or use `ssh-add -D` flag to remove identities from it
|
||||||
|
* If using `keychain`, run `keychain --clear` to remove identities
|
||||||
|
* Unset the `SSH_AUTH_SOCK` and `SSH_AGENT_PID` variables in the current shell
|
||||||
|
|
||||||
|
### Public vs. private keys
|
||||||
|
|
||||||
|
In this guide, all keys are using their full suffix. In other words, if you see a `.pub` at the end of a key, it's the public key; if you don't, it's the private key. For instance, when using the `-i` flag with `ssh`, you are specifying private keys to use. When you are submitting a key for addition to the system, you are using the public key.
|
||||||
|
|
||||||
|
## Listing your existing keys
|
||||||
|
|
||||||
|
To see a list of your existing keys, use the `list` argument to `sskm`:
|
||||||
|
|
||||||
|
jeff@baklava ~ $ ssh git@git sskm list
|
||||||
|
hello jeff, you are currently using a normal ("active") key
|
||||||
|
you have the following keys:
|
||||||
|
== active keys ==
|
||||||
|
1: 72:ef:a3:e0:f5:06:f8:aa:6f:a2:88:9d:50:86:25:4e : jeff@key1.pub
|
||||||
|
2: 61:38:a7:9f:ba:cb:99:81:4f:49:2c:8b:c8:63:8e:33 : jeff@key2.pub
|
||||||
|
3: 2d:78:d4:2c:b1:6d:9a:dc:d9:0d:94:3c:d8:c2:65:44 : jeff@key3.pub
|
||||||
|
|
||||||
|
Notice the `@` sign in each key's name? That sign and the text after that up until the `.pub` is the `keyid`. This is what you will use when identifying keys to the system. Above, for instance, one of my keys has the `keyid` of `@key3`.
|
||||||
|
|
||||||
|
A keyid may be *empty*; in fact to start with you may only have a single
|
||||||
|
`jeff.pub` key, depending on how your admin added your initial key. You can
|
||||||
|
use any keyid you wish when adding keys (like `@home`, `@laptop`, ...); the
|
||||||
|
only rules are that it must start with the `@` character and after that
|
||||||
|
contain only digits, letters, or underscores.
|
||||||
|
|
||||||
|
## Adding or Replacing a key
|
||||||
|
|
||||||
|
### Step 1: Adding the Key
|
||||||
|
|
||||||
|
Adding and replacing a key is the same process. What matters is the `keyid`. When adding a new key, use a new `keyid`; when replacing a key, pass in the `keyid` of the key you want to replace, as found by using the `list` subcommand. Pretty simple!
|
||||||
|
|
||||||
|
To add a key, pipe in the text of your new key using `cat` to the `add` subcommand. In the example below, I explicitly select which existing, active pubkey to identify with for the command (using the `-i` parameter to ssh) for clarity:
|
||||||
|
|
||||||
|
jeff@baklava ~ $ cat .ssh/newkey.pub | ssh -i .ssh/jeffskey git@git sskm add @key4
|
||||||
|
hello jeff, you are currently using a normal ("active") key
|
||||||
|
please supply the new key on STDIN. (I recommend you
|
||||||
|
don't try to do this interactively, but use a pipe)
|
||||||
|
|
||||||
|
If you now run the `list` command you'll see that it's scheduled for addition:
|
||||||
|
|
||||||
|
jeff@baklava ~ $ ssh -i .ssh/jeffskey git@git sskm list
|
||||||
|
hello jeff, you are currently using a normal ("active") key
|
||||||
|
you have the following keys:
|
||||||
|
== active keys ==
|
||||||
|
1: 72:ef:a3:e0:f5:06:f8:aa:6f:a2:88:9d:50:86:25:4e : jeff@key1.pub
|
||||||
|
2: 61:38:a7:9f:ba:cb:99:81:4f:49:2c:8b:c8:63:8e:33 : jeff@key2.pub
|
||||||
|
3: 2d:78:d4:2c:b1:6d:9a:dc:d9:0d:94:3c:d8:c2:65:44 : jeff@key3.pub
|
||||||
|
== keys marked for addition/replacement ==
|
||||||
|
1: ff:92:a2:20:6d:42:6b:cf:20:e8:a2:4a:3b:b0:32:3a : jeff@key4.pub
|
||||||
|
|
||||||
|
### Step 2: Confirming the addition
|
||||||
|
|
||||||
|
Gitolite uses Git internally to store the keys. Just like with Git, where you commit locally before `push`-ing up to the server, you need to confirm the key addition (see the next section if you made a mistake). We use the `confirm-add` subcommand to do this, *but*: to verify that you truly have ownership of the corresponding private key, you *must* use the key you are adding itself to do the confirmation! (Inconvenient like most security, but very necessary from a security perspective.) This is where using the `-i` flag of `ssh` comes in handy:
|
||||||
|
|
||||||
|
jeff@baklava ~ $ ssh -i .ssh/newkey git@git sskm confirm-add @key4
|
||||||
|
hello jeff, you are currently using a key in the 'marked for add' state
|
||||||
|
|
||||||
|
Listing keys again shows that all four keys are now active:
|
||||||
|
|
||||||
|
jeff@baklava ~ $ ssh -i .ssh/newkey git@git sskm list
|
||||||
|
hello jeff, you are currently using a normal ("active") key
|
||||||
|
you have the following keys:
|
||||||
|
== active keys ==
|
||||||
|
1: 72:ef:a3:e0:f5:06:f8:aa:6f:a2:88:9d:50:86:25:4e : jeff@key1.pub
|
||||||
|
2: 61:38:a7:9f:ba:cb:99:81:4f:49:2c:8b:c8:63:8e:33 : jeff@key2.pub
|
||||||
|
3: 2d:78:d4:2c:b1:6d:9a:dc:d9:0d:94:3c:d8:c2:65:44 : jeff@key3.pub
|
||||||
|
4: ff:92:a2:20:6d:42:6b:cf:20:e8:a2:4a:3b:b0:32:3a : jeff@key4.pub
|
||||||
|
|
||||||
|
### Optional: Undoing a mistaken add (before confirmation)
|
||||||
|
|
||||||
|
Another advantage of Gitolite using Git internally is that that if we mistakenly add the wrong key, we can undo it before it's confirmed by passing in the `keyid` we want to remove into the `undo-add` subcommand:
|
||||||
|
|
||||||
|
jeff@baklava ~ $ ssh -i .ssh/jeffskey git@git sskm undo-add @key4
|
||||||
|
hello jeff, you are currently using a normal ("active") key
|
||||||
|
|
||||||
|
Listing the keys shows that that new key has been removed:
|
||||||
|
|
||||||
|
jeff@baklava ~ $ ssh -i .ssh/jeffskey git@git sskm list
|
||||||
|
hello jeff, you are currently using a normal ("active") key
|
||||||
|
you have the following keys:
|
||||||
|
== active keys ==
|
||||||
|
1: 72:ef:a3:e0:f5:06:f8:aa:6f:a2:88:9d:50:86:25:4e : jeff@key1.pub
|
||||||
|
2: 61:38:a7:9f:ba:cb:99:81:4f:49:2c:8b:c8:63:8e:33 : jeff@key2.pub
|
||||||
|
3: 2d:78:d4:2c:b1:6d:9a:dc:d9:0d:94:3c:d8:c2:65:44 : jeff@key3.pub
|
||||||
|
|
||||||
|
## Removing a key
|
||||||
|
|
||||||
|
### Step 1: Mark the key for deletion
|
||||||
|
|
||||||
|
Deleting a key works very similarly to adding a key, with `del` substituted for `add`.
|
||||||
|
|
||||||
|
Let's say that I have my four keys from the example above:
|
||||||
|
|
||||||
|
jeff@baklava ~ $ ssh -i .ssh/newkey git@git sskm list
|
||||||
|
hello jeff, you are currently using a normal ("active") key
|
||||||
|
you have the following keys:
|
||||||
|
== active keys ==
|
||||||
|
1: 72:ef:a3:e0:f5:06:f8:aa:6f:a2:88:9d:50:86:25:4e : jeff@key1.pub
|
||||||
|
2: 61:38:a7:9f:ba:cb:99:81:4f:49:2c:8b:c8:63:8e:33 : jeff@key2.pub
|
||||||
|
3: 2d:78:d4:2c:b1:6d:9a:dc:d9:0d:94:3c:d8:c2:65:44 : jeff@key3.pub
|
||||||
|
4: ff:92:a2:20:6d:42:6b:cf:20:e8:a2:4a:3b:b0:32:3a : jeff@key4.pub
|
||||||
|
|
||||||
|
I would like to remove the key that on my box is called `newkey` and in the Gitolite system is known as `@key4`.
|
||||||
|
|
||||||
|
I simply pass in the identifier to the `del` subcommand of `sskm`:
|
||||||
|
|
||||||
|
jeff@baklava ~ $ ssh -i .ssh/newkey git@git sskm del @key4
|
||||||
|
hello jeff, you are currently using a normal ("active") key
|
||||||
|
|
||||||
|
Listing the keys now shows that it is marked for deletion:
|
||||||
|
|
||||||
|
jeff@baklava ~ $ ssh -i .ssh/newkey git@git sskm list
|
||||||
|
hello jeff, you are currently using a key in the 'marked for del' state
|
||||||
|
you have the following keys:
|
||||||
|
== active keys ==
|
||||||
|
1: 72:ef:a3:e0:f5:06:f8:aa:6f:a2:88:9d:50:86:25:4e : jeff@key1.pub
|
||||||
|
2: 61:38:a7:9f:ba:cb:99:81:4f:49:2c:8b:c8:63:8e:33 : jeff@key2.pub
|
||||||
|
3: 2d:78:d4:2c:b1:6d:9a:dc:d9:0d:94:3c:d8:c2:65:44 : jeff@key3.pub
|
||||||
|
== keys marked for deletion ==
|
||||||
|
1: ff:92:a2:20:6d:42:6b:cf:20:e8:a2:4a:3b:b0:32:3a : jeff@key4.pub
|
||||||
|
|
||||||
|
### Step 2: Confirming the deletion
|
||||||
|
|
||||||
|
Just like with Git, where you commit locally before `push`-ing up to the server, you need to confirm the key addition (see the next section if you made a mistake). We use the `confirm-del` subcommand to do this, *but*: unlike the `confirm-add` subcommand, you *must* use a *different* key than the key you are deleting to do the confirmation! This prevents you from accidentally locking yourself out of the system by removing all active keys:
|
||||||
|
|
||||||
|
jeff@baklava ~ $ ssh -i .ssh/jeffskey git@git sskm confirm-del @key4
|
||||||
|
hello jeff, you are currently using a normal ("active") key
|
||||||
|
|
||||||
|
Listing keys again shows that the fourth key has been removed:
|
||||||
|
|
||||||
|
jeff@baklava ~ $ ssh -i .ssh/jeffskey git@git sskm list
|
||||||
|
hello jeff, you are currently using a normal ("active") key
|
||||||
|
you have the following keys:
|
||||||
|
== active keys ==
|
||||||
|
1: 72:ef:a3:e0:f5:06:f8:aa:6f:a2:88:9d:50:86:25:4e : jeff@key1.pub
|
||||||
|
2: 61:38:a7:9f:ba:cb:99:81:4f:49:2c:8b:c8:63:8e:33 : jeff@key2.pub
|
||||||
|
3: 2d:78:d4:2c:b1:6d:9a:dc:d9:0d:94:3c:d8:c2:65:44 : jeff@key3.pub
|
||||||
|
|
||||||
|
### Optional: Undoing a mistaken delete (before confirmation)
|
||||||
|
|
||||||
|
Another advantage of Gitolite using Git internally is that that if we mistakenly delete the wrong key, we can undo it before it's confirmed by passing in the `keyid` we want to keep into the `undo-del` subcommand. Note that this operation *must* be performed using the private key that corresponds to the key you are trying to keep! (Security reasons, similar to the reason that you must confirm an addition this way; it prevents anyone from undoing a deletion, and therefore keeping in the system, a key that they cannot prove (by having the corresponding private key) should stay in the system):
|
||||||
|
|
||||||
|
jeff@baklava ~ $ ssh -i .ssh/newkey git@git sskm undo-del @key4
|
||||||
|
hello jeff, you are currently using a key in the 'marked for del' state
|
||||||
|
|
||||||
|
You're undeleting a key that is currently marked for deletion.
|
||||||
|
Hit ENTER to undelete this key
|
||||||
|
Hit Ctrl-C to cancel the undelete
|
||||||
|
Please see documentation for caveats on the undelete process as well as how to
|
||||||
|
actually delete it.
|
||||||
|
|
||||||
|
(Go ahead and hit ENTER there; the caveats are really only on the administrative side of things.)
|
||||||
|
|
||||||
|
Listing the keys shows that that new key is now marked active again:
|
||||||
|
|
||||||
|
jeff@baklava ~ $ ssh -i .ssh/newkey git@git sskm list
|
||||||
|
hello jeff, you are currently using a normal ("active") key
|
||||||
|
you have the following keys:
|
||||||
|
== active keys ==
|
||||||
|
1: 72:ef:a3:e0:f5:06:f8:aa:6f:a2:88:9d:50:86:25:4e : jeff@key1.pub
|
||||||
|
2: 61:38:a7:9f:ba:cb:99:81:4f:49:2c:8b:c8:63:8e:33 : jeff@key2.pub
|
||||||
|
3: 2d:78:d4:2c:b1:6d:9a:dc:d9:0d:94:3c:d8:c2:65:44 : jeff@key3.pub
|
||||||
|
4: ff:92:a2:20:6d:42:6b:cf:20:e8:a2:4a:3b:b0:32:3a : jeff@key4.pub
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
## important notes for the admin
|
||||||
|
|
||||||
|
These are the things that can break if you allows your users to use this command:
|
||||||
|
|
||||||
|
* if you, as the gitolite admin, are in the habit of force-pushing changes
|
||||||
|
to the admin repo instead of doing a `git pull` (or, even better, a `git
|
||||||
|
pull --rebase`) then you had better not enable this command. Your users
|
||||||
|
will eventually come after you with pitchforks ;-)
|
||||||
|
|
||||||
|
* there is no way to distinguish `foo/alice.pub` from `bar/alice.pub` using
|
||||||
|
this command. You can distinguish `foo/alice.pub` from
|
||||||
|
`bar/alice@home.pub`, but that's not because of the foo and bar, it's
|
||||||
|
because the two files have different keyids.
|
||||||
|
|
||||||
|
In other words, sskm only works with the older style, not with the
|
||||||
|
"subdirectory" style of [multi-key][] management.
|
||||||
|
|
||||||
|
* keys placed in specific folders (for whatever reasons), will probably not
|
||||||
|
stay in those folders if this command is used. Even a key delete, followed
|
||||||
|
by undoing the delete, will cause the key to effectively move to the root
|
||||||
|
of the key store (i.e., the `keydir` directory in the gitolite-admin repo).
|
280
src/commands/sskm
Executable file
280
src/commands/sskm
Executable file
|
@ -0,0 +1,280 @@
|
||||||
|
#!/usr/bin/perl
|
||||||
|
use strict;
|
||||||
|
use warnings;
|
||||||
|
|
||||||
|
use lib $ENV{GL_LIBDIR};
|
||||||
|
use Gitolite::Rc;
|
||||||
|
use Gitolite::Common;
|
||||||
|
|
||||||
|
=for usage
|
||||||
|
Usage for this command is not that simple. Please read the full documentation
|
||||||
|
in doc/sskm.mkd or online at http://sitaramc.github.com/gitolite/sskm.html.
|
||||||
|
=cut
|
||||||
|
|
||||||
|
usage() if @ARGV and $ARGV[0] eq '-h';
|
||||||
|
|
||||||
|
my $rb = `gitolite query-rc -n GL_REPO_BASE`;
|
||||||
|
my $ab = `gitolite query-rc -n GL_ADMIN_BASE`;
|
||||||
|
# get to the keydir
|
||||||
|
_chdir("$ab/keydir");
|
||||||
|
|
||||||
|
# save arguments for later
|
||||||
|
my $operation = shift || 'list';
|
||||||
|
my $keyid = shift || '';
|
||||||
|
# keyid must fit a very specific pattern
|
||||||
|
$keyid and $keyid !~ /^@[-0-9a-z_]+$/i and die "invalid keyid $keyid\n";
|
||||||
|
|
||||||
|
# get the actual userid and keytype
|
||||||
|
my $gl_user = $ENV{GL_USER};
|
||||||
|
my $keytype = '';
|
||||||
|
$keytype = $1 if $gl_user =~ s/^zzz-marked-for-(...)-//;
|
||||||
|
print STDERR "hello $gl_user, you are currently using "
|
||||||
|
. (
|
||||||
|
$keytype
|
||||||
|
? "a key in the 'marked for $keytype' state\n"
|
||||||
|
: "a normal (\"active\") key\n"
|
||||||
|
);
|
||||||
|
|
||||||
|
# ----
|
||||||
|
# first collect the keys
|
||||||
|
|
||||||
|
my ( @pubkeys, @marked_for_add, @marked_for_del );
|
||||||
|
# get the list of pubkey files for this user, including pubkeys marked for
|
||||||
|
# add/delete
|
||||||
|
|
||||||
|
for my $pubkey (`find . -type f -name "*.pub" | sort`) {
|
||||||
|
chomp($pubkey);
|
||||||
|
$pubkey =~ s(^./)(); # artifact of the find command
|
||||||
|
|
||||||
|
my $user = $pubkey;
|
||||||
|
$user =~ s(.*/)(); # foo/bar/baz.pub -> baz.pub
|
||||||
|
$user =~ s/(\@[^.]+)?\.pub$//; # baz.pub, baz@home.pub -> baz
|
||||||
|
|
||||||
|
next unless $user eq $gl_user or $user =~ /^zzz-marked-for-...-$gl_user/;
|
||||||
|
|
||||||
|
if ( $user =~ m(^zzz-marked-for-add-) ) {
|
||||||
|
push @marked_for_add, $pubkey;
|
||||||
|
} elsif ( $user =~ m(^zzz-marked-for-del-) ) {
|
||||||
|
push @marked_for_del, $pubkey;
|
||||||
|
} else {
|
||||||
|
push @pubkeys, $pubkey;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# ----
|
||||||
|
# list mode; just do it and exit
|
||||||
|
sub print_keylist {
|
||||||
|
my ( $message, @list ) = @_;
|
||||||
|
return unless @list;
|
||||||
|
print "== $message ==\n";
|
||||||
|
my $count = 1;
|
||||||
|
for (@list) {
|
||||||
|
my $fp = fingerprint($_);
|
||||||
|
s/zzz-marked(\/|-for-...-)//g;
|
||||||
|
print $count++ . ": $fp : $_\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ( $operation eq 'list' ) {
|
||||||
|
print "you have the following keys:\n";
|
||||||
|
print_keylist( "active keys", @pubkeys );
|
||||||
|
print_keylist( "keys marked for addition/replacement", @marked_for_add );
|
||||||
|
print_keylist( "keys marked for deletion", @marked_for_del );
|
||||||
|
print "\n\n";
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
# ----
|
||||||
|
# please see docs for details on how a user interacts with this
|
||||||
|
|
||||||
|
if ( $keytype eq '' ) {
|
||||||
|
# user logging in with a normal key
|
||||||
|
die "valid operations: add, del, undo-add, confirm-del\n" unless $operation =~ /^(add|del|confirm-del|undo-add)$/;
|
||||||
|
if ( $operation eq 'add' ) {
|
||||||
|
print STDERR "please supply the new key on STDIN. (I recommend you
|
||||||
|
don't try to do this interactively, but use a pipe)\n";
|
||||||
|
kf_add( $gl_user, $keyid, safe_stdin() );
|
||||||
|
} elsif ( $operation eq 'del' ) {
|
||||||
|
kf_del( $gl_user, $keyid );
|
||||||
|
} elsif ( $operation eq 'confirm-del' ) {
|
||||||
|
die "you dont have any keys marked for deletion\n" unless @marked_for_del;
|
||||||
|
kf_confirm_del( $gl_user, $keyid );
|
||||||
|
} elsif ( $operation eq 'undo-add' ) {
|
||||||
|
die "you dont have any keys marked for addition\n" unless @marked_for_add;
|
||||||
|
kf_undo_add( $gl_user, $keyid );
|
||||||
|
}
|
||||||
|
} elsif ( $keytype eq 'del' ) {
|
||||||
|
# user is using a key that was marked for deletion. The only possible use
|
||||||
|
# for this is that she changed her mind for some reason (maybe she marked
|
||||||
|
# the wrong key for deletion) or is not able to get her client-side sshd
|
||||||
|
# to stop using this key
|
||||||
|
die "valid operations: undo-del\n" unless $operation eq 'undo-del';
|
||||||
|
|
||||||
|
# reinstate the key
|
||||||
|
kf_undo_del( $gl_user, $keyid );
|
||||||
|
} elsif ( $keytype eq 'add' ) {
|
||||||
|
die "valid operations: confirm-add\n" unless $operation eq 'confirm-add';
|
||||||
|
# user is trying to validate a key that has been previously marked for
|
||||||
|
# addition. This isn't interactive, but it *could* be... if someone asked
|
||||||
|
kf_confirm_add( $gl_user, $keyid );
|
||||||
|
}
|
||||||
|
|
||||||
|
exit;
|
||||||
|
|
||||||
|
# ----
|
||||||
|
|
||||||
|
# make a temp clone and switch to it
|
||||||
|
our $TEMPDIR;
|
||||||
|
BEGIN { $TEMPDIR = `mktemp -d -t tmp.XXXXXXXXXX`; }
|
||||||
|
END { `/bin/rm -rf $TEMPDIR`; }
|
||||||
|
|
||||||
|
sub cd_temp_clone {
|
||||||
|
chomp($TEMPDIR);
|
||||||
|
hushed_git( "clone", "$rb/gitolite-admin.git", "$TEMPDIR" );
|
||||||
|
chdir($TEMPDIR);
|
||||||
|
my $hostname = `hostname`; chomp($hostname);
|
||||||
|
hushed_git( "config", "--get", "user.email" ) and hushed_git( "config", "user.email", $ENV{USER} . "@" . $hostname );
|
||||||
|
hushed_git( "config", "--get", "user.name" ) and hushed_git( "config", "user.name", "$ENV{USER} on $hostname" );
|
||||||
|
}
|
||||||
|
|
||||||
|
sub fingerprint {
|
||||||
|
my $fp = `ssh-keygen -l -f $_[0]`;
|
||||||
|
die "does not seem to be a valid pubkey\n" unless $fp =~ /(([0-9a-f]+:)+[0-9a-f]+ )/i;
|
||||||
|
return $1;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub safe_stdin {
|
||||||
|
# read one line from STDIN
|
||||||
|
my $data;
|
||||||
|
my $ret = read STDIN, $data, 4096;
|
||||||
|
# current pubkeys are approx 400 bytes so we go a little overboard
|
||||||
|
die "could not read pubkey data" . ( defined($ret) ? "" : ": $!" ) . "\n" unless $ret;
|
||||||
|
die "pubkey data seems to have more than one line\n" if $data =~ /\n./;
|
||||||
|
return $data;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub hushed_git {
|
||||||
|
local (*STDOUT) = \*STDOUT;
|
||||||
|
local (*STDERR) = \*STDERR;
|
||||||
|
open( STDOUT, ">", "/dev/null" );
|
||||||
|
open( STDERR, ">", "/dev/null" );
|
||||||
|
system( "git", @_ );
|
||||||
|
}
|
||||||
|
|
||||||
|
sub highlander {
|
||||||
|
# there can be only one
|
||||||
|
my ( $keyid, $die_if_empty, @a ) = @_;
|
||||||
|
# too many?
|
||||||
|
if ( @a > 1 ) {
|
||||||
|
print STDERR "
|
||||||
|
more than one key satisfies this condition, and I can't deal with that!
|
||||||
|
The keys are:
|
||||||
|
|
||||||
|
";
|
||||||
|
print STDERR "\t" . join( "\n\t", @a ), "\n\n";
|
||||||
|
exit 1;
|
||||||
|
}
|
||||||
|
# too few?
|
||||||
|
die "no keys with " . ( $keyid || "empty" ) . " keyid found\n" if $die_if_empty and not @a;
|
||||||
|
|
||||||
|
return @a;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub kf_add {
|
||||||
|
my ( $gl_user, $keyid, $keymaterial ) = @_;
|
||||||
|
|
||||||
|
# add a new "marked for addition" key for $gl_user.
|
||||||
|
cd_temp_clone();
|
||||||
|
chdir("keydir");
|
||||||
|
|
||||||
|
mkdir("zzz-marked");
|
||||||
|
_print( "zzz-marked/zzz-marked-for-add-$gl_user$keyid.pub", $keymaterial );
|
||||||
|
hushed_git( "add", "." ) and die "git add failed\n";
|
||||||
|
my $fp = fingerprint("zzz-marked/zzz-marked-for-add-$gl_user$keyid.pub");
|
||||||
|
hushed_git( "commit", "-m", "sskm: add $gl_user$keyid ($fp)" ) and die "git commit failed\n";
|
||||||
|
system("gitolite push >/dev/null 2>/dev/null") and die "git push failed\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
sub kf_confirm_add {
|
||||||
|
my ( $gl_user, $keyid ) = @_;
|
||||||
|
# find entries in both @pubkeys and @marked_for_add whose basename matches $gl_user$keyid
|
||||||
|
my @pk = highlander( $keyid, 0, grep { m(^(.*/)?$gl_user$keyid.pub$) } @pubkeys );
|
||||||
|
my @mfa = highlander( $keyid, 1, grep { m(^zzz-marked/zzz-marked-for-add-$gl_user$keyid.pub$) } @marked_for_add );
|
||||||
|
|
||||||
|
cd_temp_clone();
|
||||||
|
chdir("keydir");
|
||||||
|
|
||||||
|
my $fp = fingerprint( $mfa[0] );
|
||||||
|
if ( $pk[0] ) {
|
||||||
|
hushed_git( "mv", "-f", $mfa[0], $pk[0] );
|
||||||
|
hushed_git( "commit", "-m", "sskm: confirm-add (replace) $pk[0] ($fp)" ) and die "git commit failed\n";
|
||||||
|
} else {
|
||||||
|
hushed_git( "mv", "-f", $mfa[0], "$gl_user$keyid.pub" );
|
||||||
|
hushed_git( "commit", "-m", "sskm: confirm-add $gl_user$keyid ($fp)" ) and die "git commit failed\n";
|
||||||
|
}
|
||||||
|
system("gitolite push >/dev/null 2>/dev/null") and die "git push failed\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
sub kf_undo_add {
|
||||||
|
# XXX some code at start is shared with kf_confirm_add
|
||||||
|
my ( $gl_user, $keyid ) = @_;
|
||||||
|
my @mfa = highlander( $keyid, 1, grep { m(^zzz-marked/zzz-marked-for-add-$gl_user$keyid.pub$) } @marked_for_add );
|
||||||
|
|
||||||
|
cd_temp_clone();
|
||||||
|
chdir("keydir");
|
||||||
|
|
||||||
|
my $fp = fingerprint( $mfa[0] );
|
||||||
|
hushed_git( "rm", $mfa[0] );
|
||||||
|
hushed_git( "commit", "-m", "sskm: undo-add $gl_user$keyid ($fp)" ) and die "git commit failed\n";
|
||||||
|
system("gitolite push >/dev/null 2>/dev/null") and die "git push failed\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
sub kf_del {
|
||||||
|
my ( $gl_user, $keyid ) = @_;
|
||||||
|
|
||||||
|
cd_temp_clone();
|
||||||
|
chdir("keydir");
|
||||||
|
|
||||||
|
mkdir("zzz-marked");
|
||||||
|
my @pk = highlander( $keyid, 1, grep { m(^(.*/)?$gl_user$keyid.pub$) } @pubkeys );
|
||||||
|
|
||||||
|
my $fp = fingerprint( $pk[0] );
|
||||||
|
hushed_git( "mv", $pk[0], "zzz-marked/zzz-marked-for-del-$gl_user$keyid.pub" ) and die "git mv failed\n";
|
||||||
|
hushed_git( "commit", "-m", "sskm: del $pk[0] ($fp)" ) and die "git commit failed\n";
|
||||||
|
system("gitolite push >/dev/null 2>/dev/null") and die "git push failed\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
sub kf_confirm_del {
|
||||||
|
my ( $gl_user, $keyid ) = @_;
|
||||||
|
my @mfd = highlander( $keyid, 1, grep { m(^zzz-marked/zzz-marked-for-del-$gl_user$keyid.pub$) } @marked_for_del );
|
||||||
|
|
||||||
|
cd_temp_clone();
|
||||||
|
chdir("keydir");
|
||||||
|
|
||||||
|
my $fp = fingerprint( $mfd[0] );
|
||||||
|
hushed_git( "rm", $mfd[0] );
|
||||||
|
hushed_git( "commit", "-m", "sskm: confirm-del $gl_user$keyid ($fp)" ) and die "git commit failed\n";
|
||||||
|
system("gitolite push >/dev/null 2>/dev/null") and die "git push failed\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
sub kf_undo_del {
|
||||||
|
my ( $gl_user, $keyid ) = @_;
|
||||||
|
|
||||||
|
my @mfd = highlander( $keyid, 1, grep { m(^zzz-marked/zzz-marked-for-del-$gl_user$keyid.pub$) } @marked_for_del );
|
||||||
|
|
||||||
|
print STDERR "
|
||||||
|
You're undeleting a key that is currently marked for deletion.
|
||||||
|
Hit ENTER to undelete this key
|
||||||
|
Hit Ctrl-C to cancel the undelete
|
||||||
|
Please see documentation for caveats on the undelete process as well as how to
|
||||||
|
actually delete it.
|
||||||
|
";
|
||||||
|
<>; # yeay... always wanted to do that -- throw away user input!
|
||||||
|
|
||||||
|
cd_temp_clone();
|
||||||
|
chdir("keydir");
|
||||||
|
|
||||||
|
my $fp = fingerprint( $mfd[0] );
|
||||||
|
hushed_git( "mv", "-f", $mfd[0], "$gl_user$keyid.pub" );
|
||||||
|
hushed_git( "commit", "-m", "sskm: undo-del $gl_user$keyid ($fp)" ) and die "git commit failed\n";
|
||||||
|
system("gitolite push >/dev/null 2>/dev/null") and die "git push failed\n";
|
||||||
|
}
|
Loading…
Reference in a new issue