auth: new subcommand "htpasswd"
great idea by Robin Smidsrød: since users are already capable of authenticating themselves to gitolite via ssh keys, use that to let them set or change their own HTTP passwords (ie, run the "htpasswd" command with the correct parameters on behalf of the "git" user on the server) code, rc para, and documentation. In fact everything except... ahem... testing ;-) and while we're about it, we also reorganised the way these helper commands (including the venerable "info" are called)
This commit is contained in:
parent
0a7fa6c6b5
commit
67c10a34fe
|
@ -108,6 +108,15 @@ $GIT_PATH="";
|
|||
|
||||
# --------------------------------------
|
||||
|
||||
# if you want to enable the "htpasswd" command, give this the absolute path to
|
||||
# whatever file apache (etc) expect to find the passwords in.
|
||||
|
||||
$HTPASSWD_FILE = "";
|
||||
|
||||
# Look in doc/3 ("easier to link gitweb authorisation with gitolite" section)
|
||||
# for more details on using this feature.
|
||||
|
||||
# --------------------------------------
|
||||
# EXTERNAL COMMAND HELPER -- RSYNC
|
||||
#
|
||||
# base path of all the files that are accessible via rsync. Must be an
|
||||
|
|
|
@ -331,9 +331,25 @@ This requires that:
|
|||
* the HTTP auth should use the same username (like "sitaram") as used in the
|
||||
gitolite config (for the corresponding user)
|
||||
|
||||
Once that is done, it's easy. Gitweb allows you to specify a subroutine to
|
||||
decide on access. We use that feature and tie it to gitolite. Sample code
|
||||
(untested, munged from something I saw [here][leho]) is given below.
|
||||
Normally a superuser sets up passwords for users using the "htpasswd" command,
|
||||
but this is an administrative chore.
|
||||
|
||||
Robin Smidsrød had the *great* idea that, since each user already has pubkey
|
||||
access to `git@server`, this gives us a very neat way of using gitolite to let
|
||||
the users *manage their own HTTP passwords*. Here's how:
|
||||
|
||||
* setup apache so that the htaccess file it looks for is owned by the "git"
|
||||
user
|
||||
* in the `~/.gitolite.rc` file, look for the variable `$HTPASSWD_FILE` and
|
||||
point it to this file
|
||||
* tell your users to type in `ssh git@server htpasswd` to set or change
|
||||
their HTTP passwords
|
||||
|
||||
Here's the rest of how it hangs together.
|
||||
|
||||
Gitweb allows you to specify a subroutine to decide on access. We use that
|
||||
feature and tie it to gitolite. Sample code (untested by me, but others do
|
||||
use it, munged from something I saw [here][leho]) is given below.
|
||||
|
||||
Note the **utter simplicity** of the actual check (just 1 line!). This is an
|
||||
unexpected piece of luck coming from the decision to keep the config parse
|
||||
|
@ -349,7 +365,7 @@ already done and we just use it!
|
|||
$projectroot = '/home/git/repositories/';
|
||||
my $gl_conf_compiled = '/home/git/.gitolite/conf/gitolite.conf-compiled.pm';
|
||||
|
||||
# I assume this gives us the HTTP auth username
|
||||
# I am told this gives us the HTTP auth username
|
||||
my $username = $cgi->remote_user;
|
||||
|
||||
# ----------
|
||||
|
@ -359,10 +375,11 @@ already done and we just use it!
|
|||
die "parse $gl_conf_compiled failed: " . ($! or $@) unless do $gl_conf_compiled;
|
||||
|
||||
# this is gitweb's mechanism; it calls whatever sub is pointed at by this
|
||||
# variable to decide access yes/no
|
||||
# variable to decide access yes/no. Gitweb calls it with one argument
|
||||
# containing the full path of the repo being accessed
|
||||
$export_auth_hook = sub {
|
||||
my $reponame = shift;
|
||||
# gitweb passes us the full repo path; so we strip the beginning...
|
||||
# take the full path provided, strip the beginning...
|
||||
$reponame =~ s/\Q$projectroot\E\/?//;
|
||||
# ...and the end, to get the repo name as it is specified in gitolite conf
|
||||
$reponame =~ s/\.git$//;
|
||||
|
|
|
@ -158,17 +158,28 @@ sub report_basic
|
|||
}
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
# E X T E R N A L C O M M A N D H E L P E R S
|
||||
# S P E C I A L C O M M A N D S
|
||||
# ----------------------------------------------------------------------------
|
||||
|
||||
sub ext_cmd
|
||||
sub special_cmd
|
||||
{
|
||||
my ($GL_CONF_COMPILED, $RSYNC_BASE, $cmd) = @_;
|
||||
my ($GL_ADMINDIR, $GL_CONF_COMPILED, $RSYNC_BASE, $HTPASSWD_FILE) = @_;
|
||||
|
||||
# check each external command we know about and call it if enabled
|
||||
if ($RSYNC_BASE and $cmd =~ /^rsync /) {
|
||||
my $cmd = $ENV{SSH_ORIGINAL_COMMAND};
|
||||
my $user = $ENV{GL_USER};
|
||||
|
||||
# check each special command we know about and call it if enabled
|
||||
if ($cmd eq 'info') {
|
||||
&report_basic($GL_ADMINDIR, $GL_CONF_COMPILED, $user);
|
||||
print "you also have shell access\n\r" if $shell_allowed;
|
||||
} elsif ($HTPASSWD_FILE and $cmd eq 'htpasswd') {
|
||||
&ext_cmd_htpasswd($HTPASSWD_FILE);
|
||||
} elsif ($RSYNC_BASE and $cmd =~ /^rsync /) {
|
||||
&ext_cmd_rsync($GL_CONF_COMPILED, $RSYNC_BASE, $cmd);
|
||||
} else {
|
||||
# if the user is allowed a shell, just run the command
|
||||
exec $ENV{SHELL}, "-c", $cmd if $shell_allowed;
|
||||
|
||||
die "bad command: $cmd\n";
|
||||
}
|
||||
}
|
||||
|
@ -234,5 +245,30 @@ sub ext_cmd_rsync
|
|||
exec $ENV{SHELL}, "-c", $ENV{SSH_ORIGINAL_COMMAND};
|
||||
}
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
# external command helper: htpasswd
|
||||
# ----------------------------------------------------------------------------
|
||||
|
||||
sub ext_cmd_htpasswd
|
||||
{
|
||||
my $HTPASSWD_FILE = shift;
|
||||
|
||||
die "$HTPASSWD_FILE doesn't exist or is not writable\n" unless -w $HTPASSWD_FILE;
|
||||
$|++;
|
||||
print <<EOFhtp;
|
||||
Please type in your new htpasswd at the prompt. You only have to type it once.
|
||||
|
||||
NOTE THAT THE PASSWORD WILL BE ECHOED, so please make sure no one is
|
||||
shoulder-surfing, and make sure you clear your screen as well as scrollback
|
||||
history after you're done (or close your terminal instance).
|
||||
|
||||
EOFhtp
|
||||
print "new htpasswd:";
|
||||
|
||||
my $password = <>;
|
||||
$password =~ s/[\n\r]*$//;
|
||||
my $rc = system("htpasswd", "-b", $HTPASSWD_FILE, $ENV{GL_USER}, $password);
|
||||
die "htpasswd command seems to have failed with $rc return code...\n" if $rc;
|
||||
}
|
||||
|
||||
1;
|
||||
|
|
|
@ -24,7 +24,7 @@ use warnings;
|
|||
# ----------------------------------------------------------------------------
|
||||
|
||||
# these are set by the "rc" file
|
||||
our ($GL_LOGT, $GL_CONF_COMPILED, $REPO_BASE, $GIT_PATH, $REPO_UMASK, $GL_ADMINDIR, $RSYNC_BASE);
|
||||
our ($GL_LOGT, $GL_CONF_COMPILED, $REPO_BASE, $GIT_PATH, $REPO_UMASK, $GL_ADMINDIR, $RSYNC_BASE, $HTPASSWD_FILE);
|
||||
# and these are set by gitolite.pm
|
||||
our ($R_COMMANDS, $W_COMMANDS, $REPONAME_PATT);
|
||||
our %repos;
|
||||
|
@ -97,31 +97,31 @@ unless ($ENV{SSH_ORIGINAL_COMMAND}) {
|
|||
$ENV{SSH_ORIGINAL_COMMAND} = 'info';
|
||||
}
|
||||
|
||||
my $cmd = $ENV{SSH_ORIGINAL_COMMAND};
|
||||
# people allowed to get a shell can get basic access info by asking nicely
|
||||
if ($cmd eq 'info') {
|
||||
&report_basic($GL_ADMINDIR, $GL_CONF_COMPILED, $user);
|
||||
print "you also have shell access\n\r" if $shell_allowed;
|
||||
exit 1;
|
||||
}
|
||||
# ----------------------------------------------------------------------------
|
||||
# non-git commands
|
||||
# ----------------------------------------------------------------------------
|
||||
|
||||
# split into command and arguments; the pattern allows old style as well as
|
||||
# new style: "git-subcommand arg" or "git subcommand arg", just like gitosis
|
||||
# does, although I'm not sure how necessary that is
|
||||
#
|
||||
# keep in mind this is how git sends across the command:
|
||||
# if the command does NOT fit the pattern of a normal git command, send it off
|
||||
# somewhere else...
|
||||
|
||||
# side notes on detecting a normal git command: the pattern we check allows
|
||||
# old style as well as new style ("git-subcommand arg" or "git subcommand
|
||||
# arg"), just like gitosis does, although I'm not sure how necessary that is.
|
||||
# Currently, this is how git sends across the command (including the single
|
||||
# quotes):
|
||||
# git-receive-pack 'reponame.git'
|
||||
# including the single quotes
|
||||
|
||||
my ($verb, $repo) = ($cmd =~ /^\s*(git\s+\S+|\S+)\s+'\/?(.*?)(?:\.git)?'/);
|
||||
my ($verb, $repo) = ($ENV{SSH_ORIGINAL_COMMAND} =~ /^\s*(git\s+\S+|\S+)\s+'\/?(.*?)(?:\.git)?'/);
|
||||
unless ( $verb and ( $verb =~ $R_COMMANDS or $verb =~ $W_COMMANDS ) and $repo and $repo =~ $REPONAME_PATT ) {
|
||||
# if the user is allowed a shell, just run the command
|
||||
exec $ENV{SHELL}, "-c", $ENV{SSH_ORIGINAL_COMMAND} if $shell_allowed;
|
||||
# otherwise, call the external command helper
|
||||
&ext_cmd($GL_CONF_COMPILED, $RSYNC_BASE, $cmd);
|
||||
exit; # in case the external command helper forgot :-)
|
||||
# ok, it's not a normal git command; call the special command helper
|
||||
&special_cmd ($GL_ADMINDIR, $GL_CONF_COMPILED, $RSYNC_BASE, $HTPASSWD_FILE);
|
||||
exit;
|
||||
}
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
# the real git commands (git-receive-pack, etc...)
|
||||
# ----------------------------------------------------------------------------
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
# first level permissions check
|
||||
# ----------------------------------------------------------------------------
|
||||
|
|
Loading…
Reference in a new issue