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
|
# EXTERNAL COMMAND HELPER -- RSYNC
|
||||||
#
|
#
|
||||||
# base path of all the files that are accessible via rsync. Must be an
|
# 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
|
* the HTTP auth should use the same username (like "sitaram") as used in the
|
||||||
gitolite config (for the corresponding user)
|
gitolite config (for the corresponding user)
|
||||||
|
|
||||||
Once that is done, it's easy. Gitweb allows you to specify a subroutine to
|
Normally a superuser sets up passwords for users using the "htpasswd" command,
|
||||||
decide on access. We use that feature and tie it to gitolite. Sample code
|
but this is an administrative chore.
|
||||||
(untested, munged from something I saw [here][leho]) is given below.
|
|
||||||
|
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
|
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
|
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/';
|
$projectroot = '/home/git/repositories/';
|
||||||
my $gl_conf_compiled = '/home/git/.gitolite/conf/gitolite.conf-compiled.pm';
|
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;
|
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;
|
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
|
# 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 {
|
$export_auth_hook = sub {
|
||||||
my $reponame = shift;
|
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\/?//;
|
$reponame =~ s/\Q$projectroot\E\/?//;
|
||||||
# ...and the end, to get the repo name as it is specified in gitolite conf
|
# ...and the end, to get the repo name as it is specified in gitolite conf
|
||||||
$reponame =~ s/\.git$//;
|
$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
|
my $cmd = $ENV{SSH_ORIGINAL_COMMAND};
|
||||||
if ($RSYNC_BASE and $cmd =~ /^rsync /) {
|
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);
|
&ext_cmd_rsync($GL_CONF_COMPILED, $RSYNC_BASE, $cmd);
|
||||||
} else {
|
} 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";
|
die "bad command: $cmd\n";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -234,5 +245,30 @@ sub ext_cmd_rsync
|
||||||
exec $ENV{SHELL}, "-c", $ENV{SSH_ORIGINAL_COMMAND};
|
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;
|
1;
|
||||||
|
|
|
@ -24,7 +24,7 @@ use warnings;
|
||||||
# ----------------------------------------------------------------------------
|
# ----------------------------------------------------------------------------
|
||||||
|
|
||||||
# these are set by the "rc" file
|
# 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
|
# and these are set by gitolite.pm
|
||||||
our ($R_COMMANDS, $W_COMMANDS, $REPONAME_PATT);
|
our ($R_COMMANDS, $W_COMMANDS, $REPONAME_PATT);
|
||||||
our %repos;
|
our %repos;
|
||||||
|
@ -97,31 +97,31 @@ unless ($ENV{SSH_ORIGINAL_COMMAND}) {
|
||||||
$ENV{SSH_ORIGINAL_COMMAND} = 'info';
|
$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
|
# non-git commands
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
# split into command and arguments; the pattern allows old style as well as
|
# if the command does NOT fit the pattern of a normal git command, send it off
|
||||||
# new style: "git-subcommand arg" or "git subcommand arg", just like gitosis
|
# somewhere else...
|
||||||
# does, although I'm not sure how necessary that is
|
|
||||||
#
|
|
||||||
# keep in mind this is how git sends across the command:
|
|
||||||
# git-receive-pack 'reponame.git'
|
|
||||||
# including the single quotes
|
|
||||||
|
|
||||||
my ($verb, $repo) = ($cmd =~ /^\s*(git\s+\S+|\S+)\s+'\/?(.*?)(?:\.git)?'/);
|
# 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'
|
||||||
|
|
||||||
|
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 ) {
|
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
|
# ok, it's not a normal git command; call the special command helper
|
||||||
exec $ENV{SHELL}, "-c", $ENV{SSH_ORIGINAL_COMMAND} if $shell_allowed;
|
&special_cmd ($GL_ADMINDIR, $GL_CONF_COMPILED, $RSYNC_BASE, $HTPASSWD_FILE);
|
||||||
# otherwise, call the external command helper
|
exit;
|
||||||
&ext_cmd($GL_CONF_COMPILED, $RSYNC_BASE, $cmd);
|
|
||||||
exit; # in case the external command helper forgot :-)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# ----------------------------------------------------------------------------
|
||||||
|
# the real git commands (git-receive-pack, etc...)
|
||||||
|
# ----------------------------------------------------------------------------
|
||||||
|
|
||||||
# ----------------------------------------------------------------------------
|
# ----------------------------------------------------------------------------
|
||||||
# first level permissions check
|
# first level permissions check
|
||||||
# ----------------------------------------------------------------------------
|
# ----------------------------------------------------------------------------
|
||||||
|
|
Loading…
Reference in a new issue