diff options
Diffstat (limited to 'src/lib/Gitolite/Triggers/TProxy.pm')
-rw-r--r-- | src/lib/Gitolite/Triggers/TProxy.pm | 98 |
1 files changed, 98 insertions, 0 deletions
diff --git a/src/lib/Gitolite/Triggers/TProxy.pm b/src/lib/Gitolite/Triggers/TProxy.pm new file mode 100644 index 0000000..9c42918 --- /dev/null +++ b/src/lib/Gitolite/Triggers/TProxy.pm @@ -0,0 +1,98 @@ +package Gitolite::Triggers::TProxy; + +# ---------------------------------------------------------------------- +# transparent proxy for git repos, hosted on a gitolite server + +# ---------------------------------------------------------------------- +# WHAT + +# 1. user runs a git command (clone, fetch, push) against a gitolite +# server. +# 2. if that server has the repo, it will serve it up. Else it will +# *transparently* forward the git operation to a designated upstream +# server. The user does not have to do anything, and in fact may not +# even know this has happened. + +# can be combined with, but does not *require*, gitolite mirroring. + +# ---------------------------------------------------------------------- +# SECURITY +# +# 1. Most of the issues that apply to "redirected push" in mirroring.html +# also apply here. In particular, you had best make sure the two +# servers use the same authentication data (i.e., "alice" here should be +# "alice" there!) +# +# 2. Also, do not add keys for servers you don't trust! + +# ---------------------------------------------------------------------- +# HOW + +# on transparent proxy server (the one that is doing the redirect): +# 1. add +# INPUT => ['TProxy::input'], +# just before the ENABLE list in the rc file +# 2. add an RC variable to tell gitolite where to go; this is also just +# before the ENABLE list: +# TPROXY_FORWARDS_TO => 'git@upstream', + +# on upstream server (the one redirected TO): +# 1. add +# INPUT => ['TProxy::input'], +# just before the ENABLE list in the rc file +# 2. add the pubkey of the proxy server (the one that will be redirecting +# to us) to this server's gitolite-admin "keydir" as +# "server-<something>.pub", and push the change. + +# to use in combination with gitolite mirroring +# 1. just follow the same instructions as above. Server names and +# corresponding pub keys would already be set ok so step 2 in the +# upstream server setup (above) will not be needed. +# 2. needless to say, **don't** declare the repos you want to be +# transparently proxied in the gitolite.conf for the copy. + +use Gitolite::Rc; +use Gitolite::Common; +use Gitolite::Conf::Load; + +use strict; +use warnings; + +my $soc = $ENV{SSH_ORIGINAL_COMMAND}; + +# ---------------------------------------------------------------------- + +sub input { + # are we the upstream, getting something from a tproxy server? + my $git_commands = "git-upload-pack|git-receive-pack|git-upload-archive"; + if ( $ARGV[0] =~ /^server-/ and $soc =~ /^TPROXY_FOR=(\S+) SOC=(($git_commands) '\S+')$/ ) { + @ARGV = ($1); + # you better make sure you read the security warnings up there! + + $ENV{SSH_ORIGINAL_COMMAND} = $2; + delete $ENV{GL_BYPASS_ACCESS_CHECKS}; + # just in case we somehow end up running before Mirroring::input! + + return; + } + + # well we're not upstream; are we a tproxy? + return unless $rc{TPROXY_FORWARDS_TO}; + + # is it a normal git command? + return unless $ENV{SSH_ORIGINAL_COMMAND} =~ m(^($git_commands) '/?(.*?)(?:\.git(\d)?)?'$); + + # ...get the repo name from $ENV{SSH_ORIGINAL_COMMAND} + my ( $verb, $repo, $trace_level ) = ( $1, $2, $3 ); + $ENV{D} = $trace_level if $trace_level; + _die "invalid repo name: '$repo'" if $repo !~ $REPONAME_PATT; + + # nothing to do if the repo exists locally + return if -d "$ENV{GL_REPO_BASE}/$repo.git"; + + my $user = shift @ARGV; + # redirect to upstream + exec( "ssh", $rc{TPROXY_FORWARDS_TO}, "TPROXY_FOR=$user", "SOC=$ENV{SSH_ORIGINAL_COMMAND}" ); +} + +1; |