Compare commits

...

No commits in common. "master" and "g2" have entirely different histories.
master ... g2

313 changed files with 22272 additions and 13061 deletions

4
.gitattributes vendored Normal file
View file

@ -0,0 +1,4 @@
conf/* crlf=input
src/* crlf=input
hooks/common/* crlf=input
hooks/gitolite-admin/* crlf=input

5
.gitignore vendored Normal file
View file

@ -0,0 +1,5 @@
*.tar
*.tgz
*.tar.gz
*.tar.bz2
conf/VERSION

View file

@ -1,86 +0,0 @@
2012-12-29 v3.3 bug fix: gl-perms propagation to slaves broke sometime
after v3.2 (so if you're only picking up tagged releases
you're OK)
the "D" command now allows rm/unlock to be totally
disabled
new trigger: update-gitweb-daemon-from-options; another
way to update gitweb and daemon access lists
new 'create' command for explicit wild repo creation, and
new AutoCreate trigger to control auto-creation
allow simple macros in conf file
2012-11-14 v3.2 major efficiency boost for large setups
optional support for multi-line pubkeys; see
src/triggers/post-compile/ssh-authkeys-split
bug fix for not creating gl-conf when repo para has only
config lines and no access rules
new 'bg' trigger command to put long jobs started from a
trigger into background
%GL_REPO and %GL_CREATOR now work for 'option's also
test suite now much more BSD friendly
2012-10-05 v3.1 (security) fix path traversal on wild repos
new %GL_CREATOR variable for git-config lines
rsync command to create and send bundles automagically
migrated 'who-pushed'
logical expressions on refexes!!!
2012-06-27 v3.04 documentation graduated and moved out of parents house :)
new trigger for 'repo specific umask'
new 'list-dangling-repos' command
new LOCAL_CODE rc var; allow admin specified programs to
override system-installed ones
new 'upstream' trigger-cum-command to maintain local
copies of external repos
new 'sudo' command
minor backward compat breakage in 'gitolite query-rc'
'perms' command can now create repo if needed
migrated 'symbolic-ref' command
'gitolite setup --hooks-only'
2012-05-23 v3.03 fix major bug that allowed an admin to get a shell
2012-05-20 v3.02 packaging instructions fixed up and smoke tested
make it easier to give some users a full shell
allow aliasing a repo to another name
simulate POST_CREATE for new normal (non-wild) repos
(just for kicks) a VREF that allows for voting on changes
to a branch
bug fix: smart http was not running PRE_ and POST_GIT
triggers
htpasswd migrated
2012-04-29 v3.01 mostly BSD and Solaris compat
also fork command added
2012-04-18 v3.0 first release to "master"
This is a compete rewrite of gitolite; please see
documentation before upgrading.

19
Makefile Normal file
View file

@ -0,0 +1,19 @@
# this is a simple wrapper around "git archive" using make
# "make [refname].tar" produces a tar of refname, then adds a file containing
# the "git describe" output for that refname to the tar. This lets you say
# "cat .GITOLITE-VERSION" to find out which ref produced this tar
branch := $(shell git rev-parse --abbrev-ref HEAD)
$(branch): $(branch).tar
.GITOLITE-VERSION:
@touch conf/VERSION
%.tar: .GITOLITE-VERSION
git describe --tags --long $* > conf/VERSION
git archive $* > $@
tar -r -f $@ conf/VERSION
rm conf/VERSION
cp -v $@ /tmp

359
README.md
View file

@ -1,359 +0,0 @@
Github-users: click the 'wiki' link before sending me anything via github.
Existing users: this is gitolite v3.x. If you are upgrading from v2.x this
file will not suffice; you *must* check the online docs (see below for URL).
------------------------------------------------------------------------
This file contains BASIC DOCUMENTATION ONLY.
* It is suitable for a fresh, ssh-based, installation of gitolite and basic
usage of its most important features.
* It is NOT meant to be exhaustive or detailed.
The COMPLETE DOCUMENTATION is at:
http://sitaramc.github.com/gitolite/master-toc.html
Please go there for what/why/how, concepts, background, troubleshooting, more
details on what is covered here, or advanced features not covered here.
------------------------------------------------------------------------
BASIC DOCUMENTATION FOR GITOLITE
================================
This file contains the following sections:
* INSTALLATION AND SETUP
* ADDING USERS AND REPOS
* HELP FOR YOUR USERS
* BASIC SYNTAX
* ACCESS RULES
* GROUPS
* COMMANDS
* THE 'rc' FILE
* GIT-CONFIG
* GIT-DAEMON
* GITWEB
* CONTACT AND SUPPORT
* LICENSE
------------------------------------------------------------------------
INSTALLATION AND SETUP
----------------------
Server requirements:
* any unix system
* sh
* git 1.6.6+
* perl 5.8.8+
* openssh 5.0+
* a dedicated userid to host the repos (in this document, we assume it
is 'git'), with shell access ONLY by 'su - git' from some other userid
on the same server.
Steps to install:
* login as 'git' as described above
* make sure ~/.ssh/authorized_keys is empty or non-existent
* make sure your ssh public key from your workstation is available at $HOME/YourName.pub
* run the following commands:
git clone git://github.com/sitaramc/gitolite
mkdir -p $HOME/bin
gitolite/install -to $HOME/bin
gitolite setup -pk YourName.pub
If the last command doesn't run perhaps 'bin' in not in your 'PATH'.
You can either add it, or just run:
$HOME/bin/gitolite setup -pk YourName.pub
ADDING USERS AND REPOS
----------------------
Do NOT add new repos or users manually on the server. Gitolite users,
repos, and access rules are maintained by making changes to a special repo
called 'gitolite-admin' and pushing those changes to the server.
----
To administer your gitolite installation, start by doing this on your
workstation (if you have not already done so):
git clone git@host:gitolite-admin
**NOTE**: if you are asked for a password, something has gone wrong.
Now if you 'cd gitolite-admin', you will see two subdirectories in it:
'conf' and 'keydir'.
To add new users alice, bob, and carol, obtain their public keys and add
them to 'keydir' as alice.pub, bob.pub, and carol.pub respectively.
To add a new repo 'foo' and give different levels of access to these
users, edit the file 'conf/gitolite.conf' and add lines like this:
repo foo
RW+ = alice
RW = bob
R = carol
See the 'ACCESS RULES' section later for more details.
Once you have made these changes, do something like this:
git add conf
git add keydir
git commit -m 'added foo, gave access to alice, bob, carol'
git push
When the push completes, gitolite will add the new users to
~/.ssh/authorized_keys on the server, as well as create a new, empty, repo
called 'foo'.
HELP FOR YOUR USERS
-------------------
Once a user has sent you their public key and you have added them as
specified above and given them access, you have to tell them what URL to
access their repos at. This is usually 'git clone git@host:reponame'; see
man git-clone for other forms.
**NOTE**: again, if they are asked for a password, something is wrong.
If they need to know what repos they have access to, they just have to run
'ssh git@host info'; see 'COMMANDS' section later for more on this.
BASIC SYNTAX
------------
The basic syntax of the conf file is very simple.
* Everything is space separated; there are no commas, semicolons, etc.,
in the syntax.
* Comments are in the usual perl/shell style.
* User and repo names are as simple as possible; they must start with an
alphanumeric, but after that they can also contain '.', '_', or '-'.
Usernames can optionally be followed by an '@' and a domainname
containing at least one '.'; this allows you to use an email address
as someone's username.
Reponames can contain '/' characters; this allows you to put your
repos in a tree-structure for convenience.
* There are no continuation lines.
ACCESS RULES
------------
This section is mostly 'by example'.
Gitolite's access rules are very powerful. The simplest use was already
shown above. Here is a slightly more detailed example:
repo foo
RW+ = alice
- master = bob
- refs/tags/v[0-9] = bob
RW = bob
RW refs/tags/v[0-9] = carol
R = dave
For clones and fetches, as long as the user is listed with an R, RW
or RW+ in at least one rule, he is allowed to read the repo.
For pushes, rules are processed in sequence until a rule is found
where the user, the permission (see note 1), and the refex (note 2)
*all* match. At that point, if the permission on the matched rule
was '-', the push is denied, otherwise it is allowed. If no rule
matches, the push is denied.
Note 1: permission matching:
* a permission of RW matches only a fast-forward push or create
* a permission of RW+ matches any type of push
* a permission of '-' matches any type of push
Note 2: refex matching:
(refex = optional regex to match the ref being pushed)
* an empty refex is treated as 'refs/.*'
* a refex that does not start with 'refs/' is prefixed with 'refs/heads/'
* finally, a '^' is prefixed
* the ref being pushed is matched against this resulting refex
With all that background, here's what the example rules say:
* alice can do anything to any branch or tag -- create, push, delete, rewind/overwrite etc.
* bob can create or fast-forward push any branch whose name does
not start with 'master' and create any tag whose name does not
start with 'v'+digit.
* carol can create tags whose names start with 'v'+digit.
* dave can clone/fetch.
GROUPS
------
Gitolite allows you to group users or repos for convenience. Here's an
example that creates two groups of users:
@staff = alice bob carol
@interns = ashok
repo secret
RW = @staff
repo foss
RW+ = @staff
RW = @interns
Group lists accumulate. The following two lines have the same effect as
the earlier definition of @staff above:
@staff = alice bob
@staff = carol
You can also use group names in other group names:
@all-devs = @staff @interns
Finally, @all is a special group name that is often convenient to use if
you really mean 'all repos' or 'all users'.
COMMANDS
--------
Users can run certain commands remotely, using ssh. For example:
ssh git@host help
prints a list of available commands.
The most commonly used command is 'info'. All commands respond to a
single argument of '-h' with suitable information.
If you have shell on the server, you have a lot more commands available to
you; try running 'gitolite help'.
THE 'rc' FILE
--------------
Some of the instructions below may require you to edit the rc file
(~/.gitolite.rc on the server).
The rc file is perl code, but you do NOT need to know perl to edit it.
Just mind the commas, use single quotes unless you know what you're doing,
and make sure the brackets and braces stay matched up.
GIT-CONFIG
----------
Gitolite lets you set git-config values for individual repos without
having to log on to the server and run 'git config' commands:
repo foo
config hooks.mailinglist = foo-commits@example.tld
config hooks.emailprefix = '[foo] '
config foo.bar = ''
config foo.baz =
**WARNING**
The last syntax shown above is the *only* way to *delete* a config
variable once you have added it. Merely removing it from the conf
file will *not* delete it from the repo.git/config file.
**SECURITY NOTE**
Some git-config keys allow arbitrary code to be run on the server.
If all of your gitolite admins already have shell access to the server
account hosting it, you can edit the rc file (~/.gitolite.rc) on the
server, and change the GIT_CONFIG_KEYS line to look like this:
GIT_CONFIG_KEYS => '.*',
Otherwise, give it a space-separated list of regular expressions that
define what git-config keys are allowed. For example, this one allows
only variables whose names start with 'gitweb' or with 'gc' to be
defined:
GIT_CONFIG_KEYS => 'gitweb\..* gc\..*',
GIT-DAEMON
----------
Gitolite creates the 'git-daemon-export-ok' file for any repo that is
readable by a special user called 'daemon', like so:
repo foo
R = daemon
GITWEB
------
Any repo that is readable by a special user called 'gitweb' will be added
to the projects.list file.
repo foo
R = gitweb
Or you can set one or more of the following config variables instead:
repo foo
config gitweb.owner = some person's name
config gitweb.description = some description
config gitweb.category = some category
**NOTE**
You will probably need to change the UMASK in the rc file from the
default (0077) to 0027 and add whatever user your gitweb is running as
to the 'git' group. After that, you need to run a one-time 'chmod -R'
on the already created files and directories.
------------------------------------------------------------------------
CONTACT AND SUPPORT
-------------------
Mailing list for support and general discussion:
gitolite@googlegroups.com
subscribe address: gitolite+subscribe@googlegroups.com
Mailing list for announcements and notices:
subscribe address: gitolite-announce+subscribe@googlegroups.com
IRC: #git and #gitolite on freenode. Note that I live in India (UTC+0530
time zone).
Author: sitaramc@gmail.com, but please DO NOT use this for general support
questions. Subscribe to the list and ask there instead.
LICENSE
-------
The gitolite *code* is released under GPL v2. See COPYING for details.
This documentation, which is part of the source code repository, is
provided under a Creative Commons Attribution-ShareAlike 3.0 Unported
License -- see http://creativecommons.org/licenses/by-sa/3.0/

41
README.mkd Normal file
View file

@ -0,0 +1,41 @@
# Gitolite README
**2012-03-25: there is a completely re-written version of gitolite in the "g3"
branch. The old version will now be supported only for existing users, and
for people who need the features that have not yet made it to the new one. To
reflect this, the `pu` branch has been merged into master and deleted.**
The steps to use the new version are:
git clone git://github.com/sitaramc/gitolite
cd gitolite
git checkout g3
Documentation is currently available at [g3di]
[g3di]: http://sitaramc.github.com/gitolite/g3/
----
**Github users: please read the "wiki" link at the top of the page before
submitting issues or pull requests**.
If you're really impatient, and you're familiar with Unix and ssh, follow the
[quick install](http://sitaramc.github.com/gitolite/index.html#qi)
instructions.
But if you want to do anything meaningful with gitolite you have to spend some
time cuddling up to the docs. **The complete online documentation starts
[here](http://sitaramc.github.com/gitolite)** -- this is the best starting
point for general questions about git, such as what it is, why you would need
it, features, contact/mailing list info, and so on.
For convenience, here is a link to the [master table of
contents](http://sitaramc.github.com/gitolite/master-toc.html), which is very
useful to search using your browser's search function.
----
License information for code and documentation is at the end of doc/index.mkd
(or you can read it online [here][license]).

View file

@ -1,99 +0,0 @@
#!/usr/bin/perl
use Cwd;
my $h = $ENV{HOME};
my $rc = "$h/.gitolite.rc";
my %count;
intro();
msg( FATAL => "no rc file found; do you even *have* g2 running?" ) if not -f $rc;
do $rc;
unless ( $return = do $rc ) {
msg( FATAL => "couldn't parse $rc: $@" ) if $@;
msg( FATAL => "couldn't do $rc: $!" ) unless defined $return;
msg( WARNING => "couldn't run $rc" ) unless $return;
}
print "checking rc file...\n";
rc_basic();
rest_of_rc();
print "\n";
print "checking conf file(s)...\n";
conf();
print "\n";
print "checking repos...\n";
repo();
print "\n";
print "...all done...\n";
# ----------------------------------------------------------------------
sub intro {
msg( INFO => "This program only checks for uses that make the new g3 completely unusable" );
msg( '' => "or that might end up giving *more* access to someone if migrated as-is." );
msg( '' => "It does NOT attempt to catch all the differences described in the docs." );
msg( '', '' );
msg( INFO => "'see docs' usually means the pre-migration checklist in" );
msg( '', => "'g2migr.html'; to get there, start from the main migration" );
msg( '', => "page at http://sitaramc.github.com/gitolite/install.html#migr" );
msg( '', '' );
}
sub rc_basic {
msg( FATAL => "GL_ADMINDIR in the wrong place -- aborting; see docs" ) if $GL_ADMINDIR ne "$h/.gitolite";
msg( NOTE => "GL_ADMINDIR is in the right place; assuming you did not mess with" );
msg( '', "GL_CONF, GL_LOGT, GL_KEYDIR, and GL_CONF_COMPILED" );
msg( FATAL => "REPO_BASE in the wrong place -- aborting; see docs" ) if $REPO_BASE ne "$h/repositories" and $REPO_BASE ne "repositories";
# ( abs or rel both ok)
}
sub rest_of_rc {
msg( SEVERE => "GIT_PATH found; see docs" ) if $GIT_PATH;
msg( SEVERE => "GL_ALL_INCLUDES_SPECIAL found; see docs" ) if $GL_ALL_INCLUDES_SPECIAL;
msg( SEVERE => "GL_NO_CREATE_REPOS not yet implemented" ) if $GL_NO_CREATE_REPOS;
msg( SEVERE => "rsync not yet implemented" ) if $RSYNC_BASE;
msg( WARNING => "ADMIN_POST_UPDATE_CHAINS_TO found; see docs" ) if $ADMIN_POST_UPDATE_CHAINS_TO;
msg( WARNING => "GL_NO_DAEMON_NO_GITWEB found; see docs" ) if $GL_NO_DAEMON_NO_GITWEB;
msg( WARNING => "GL_NO_SETUP_AUTHKEYS found; see docs" ) if $GL_NO_SETUP_AUTHKEYS;
msg( WARNING => "UPDATE_CHAINS_TO found; see docs" ) if $UPDATE_CHAINS_TO;
msg( WARNING => "GL_ADC_PATH found; see docs" ) if $GL_ADC_PATH;
msg( WARNING => "non-default GL_WILDREPOS_PERM_CATS found" ) if $GL_WILDREPOS_PERM_CATS ne 'READERS WRITERS';
}
sub conf {
chdir($h);
chdir($GL_ADMINDIR);
my $conf = `find . -name "*.conf" | xargs cat`;
msg( "SEVERE", "NAME rules; see docs" ) if $conf =~ m(NAME/);
msg( "SEVERE", "subconf command in admin repo; see docs" ) if $conf =~ m(NAME/conf/fragments);
msg( "SEVERE", "mirroring used; see docs" ) if $conf =~ m(config +gitolite\.mirror\.);
}
sub repo {
chdir($h);
chdir($REPO_BASE);
my @creater = `find . -name gl-creater`;
if (@creater) {
msg( WARNING => "found " . scalar(@creater) . " gl-creater files; see docs" );
}
my @perms = `find . -name gl-perms | xargs egrep -l -w R\\|RW`;
if (@perms) {
msg( WARNING => "found " . scalar(@perms) . " gl-perms files with R or RW; see docs" );
}
}
sub msg {
my ( $type, $text ) = @_;
print "$type" if $type;
print "\t$text\n";
exit 1 if $type eq 'FATAL';
$count{$type}++ if $type;
}

2
conf/example.conf Normal file
View file

@ -0,0 +1,2 @@
# see doc/gitolite.conf.mkd for help on the syntax and semantics of this file
# online at http://sitaramc.github.com/gitolite/doc/gitolite.conf.html

96
conf/example.gitolite.rc Normal file
View file

@ -0,0 +1,96 @@
# configuration variables for gitolite
# PLEASE READ THE DOCUMENTATION BEFORE EDITING OR ASKING QUESTIONS
# ( http://github.com/sitaramc/gitolite/blob/pu/doc/gitolite.rc.mkd )
# ( or http://sitaramc.github.com/gitolite/doc/gitolite.rc.html )
# this file is in perl syntax. However, you do NOT need to know perl to edit
# it; it should be fairly self-explanatory and easy to maintain
# ------------------------------------------------------------------------------
# DO NOT TOUCH THIS SECTION!
# ------------------------------------------------------------------------------
$GL_ADMINDIR=$ENV{HOME} . "/.gitolite";
$GL_CONF="$GL_ADMINDIR/conf/gitolite.conf";
$GL_KEYDIR="$GL_ADMINDIR/keydir";
$GL_CONF_COMPILED="$GL_ADMINDIR/conf/gitolite.conf-compiled.pm";
# DO NOT CHANGE THE NEXT FOUR LINES UNLESS YOU REALLY KNOW WHAT YOU'RE DOING.
# These variables are set automatically by the install method you choose.
# (PACKAGE MAINTAINERS: PLEASE READ doc/packaging.mkd)
$GL_PACKAGE_CONF = "/tmp/share/gitolite/conf";
$GL_PACKAGE_HOOKS = "/tmp/share/gitolite/hooks";
# ------------------------------------------------------------------------------
# most often used/changed variables
# ------------------------------------------------------------------------------
$GL_WILDREPOS = 0;
$PROJECTS_LIST = $ENV{HOME} . "/projects.list";
# $WEB_INTERFACE = "gitweb";
# $GITWEB_URI_ESCAPE = 0;
$REPO_UMASK = 0077;
# ------------------------------------------------------------------------------
# variables with an efficiency/performance impact
# ------------------------------------------------------------------------------
$GL_BIG_CONFIG = 0;
$GL_NO_DAEMON_NO_GITWEB = 0;
# $GL_NICE_VALUE = 0;
# $BIG_INFO_CAP = 20;
# ------------------------------------------------------------------------------
# VARIABLES WITH A SECURITY IMPACT. READ DOCS BEFORE CHANGING THESE!
# http://github.com/sitaramc/gitolite/blob/pu/doc/gitolite.rc.mkd#_variables_with_a_security_impact
# (or http://sitaramc.github.com/gitolite/doc/gitolite.rc.html#_variables_with_a_security_impact)
# ------------------------------------------------------------------------------
# $GL_ALL_READ_ALL = 0;
$GIT_PATH="";
$GL_GITCONFIG_KEYS = "";
$GL_NO_CREATE_REPOS = 0;
$GL_NO_SETUP_AUTHKEYS = 0;
# $GL_WILDREPOS_DEFPERMS = 'R @all';
$HTPASSWD_FILE = "";
$RSYNC_BASE = "";
$SVNSERVE = "";
# $UPDATE_CHAINS_TO = "hooks/update.secondary";
# $ADMIN_POST_UPDATE_CHAINS_TO = "hooks/post-update.secondary";
# $GL_ADC_PATH = "";
# $GL_GET_MEMBERSHIPS_PGM = "/usr/local/bin/expand-ldap-user-to-groups"
# $GL_HTTP_ANON_USER = "mob";
# $GL_REF_OR_FILENAME_PATT=qr(^[0-9a-zA-Z][0-9a-zA-Z._\@/+ :,-]*$);
# ------------------------------------------------------------------------------
# less used/changed variables
# ------------------------------------------------------------------------------
# $GL_ALL_INCLUDES_SPECIAL = 0;
# $GL_SLAVE_MODE = 0;
# $ENV{GL_SLAVES} = 'gitolite@server2 gitolite@server3';
# PLEASE USE SINGLE QUOTES ABOVE, NOT DOUBLE QUOTES
$GL_WILDREPOS_PERM_CATS = "READERS WRITERS";
# $GL_SITE_INFO = "XYZ.COM DEVELOPERS: PLEASE SEE http://xyz.com/gitolite/help first";
# $GL_HOSTNAME = "frodo"; # read doc/mirroring.mkd COMPLETELY before setting this
# ------------------------------------------------------------------------------
# rarely changed variables
# ------------------------------------------------------------------------------
$GL_LOGT="$GL_ADMINDIR/logs/gitolite-%y-%m.log";
# $GL_PERFLOGT="$GL_ADMINDIR/logs/perf-gitolite-%y-%m.log";
# ------------------------------------------------------------------------------
# variables that should NOT be changed after the install step completes
# ------------------------------------------------------------------------------
$REPO_BASE="repositories";
# ------------------------------------------------------------------------------
# DO NOT TOUCH ANY THING AFTER THIS LINE
# ------------------------------------------------------------------------------
# ------------------------------------------------------------------------------
# per perl rules, this should be the last line in such a file:
1;
# Local variables:
# mode: perl
# End:
# vim: set syn=perl:

View file

@ -1,4 +1,4 @@
#!/bin/sh
#!/bin/bash
# gitolite VREF to count number of changed/new files in a push
@ -31,9 +31,9 @@ nf=
# count files against all the other commits in the system not just $oldsha
# (why? consider what is $oldtree when you create a new branch, or what is
# $oldsha when you update an old feature branch from master and then push it
count=`git log --name-only $nf --format=%n $newtree --not --all | grep . | sort -u | perl -ne '}{print "$."'`
count=`git log --name-only $nf --format=%n $newtree --not --all | grep . | sort -u | wc -l`
[ $count -gt $max ] && {
[[ $count -gt $max ]] && {
# count has been exceeded. If $9 was NO_SIGNOFF there's still a chance
# for redemption -- if the top commit has a proper signed-off by line
[ "$9" = "NO_SIGNOFF" ] && {

45
contrib/VREF/gl-VREF-DUPKEYS Executable file
View file

@ -0,0 +1,45 @@
#!/bin/bash
# gitolite VREF to detect duplicate public keys
# see gitolite doc/virtual-refs.mkd for what the arguments are
sha=$3
# git sets this; and we don't want it at this point...
unset GIT_DIR
# paranoia
set -e
# setup the temp area
export TMPDIR=$GL_REPO_BASE_ABS
export tmp=$(mktemp -d -t gl-internal-temp-repo.XXXXXXXXXX);
trap "rm -rf $tmp" EXIT;
git archive $sha keydir | tar -C $tmp -xf -
# DO NOT try, say, 'GIT_WORK_TREE=$tmp git checkout $sha'. It'll screw up
# both the 'index' and 'HEAD' of the repo.git. Screwing up the index is
# BAD because now it goes out of sync with $GL_ADMINDIR. Think of a push
# that had a deleted pubkey but failed a hooklet for some reason. A
# subsequent push that fixes the error will now result in a $GL_ADMINDIR
# that still *has* that deleted pubkey!!
# And this is equally applicable to cases where you're using a
# post-receive or similar hook to live update a web site or something,
# which is a pretty common usage, I am given to understand.
cd $tmp
for f in `find keydir -name "*.pub"`
do
ssh-keygen -l -f "$f"
done | perl -ane '
die "FATAL: $F[2] is a duplicate of $seen{$F[1]}\n" if $seen{$F[1]};
$seen{$F[1]} = $F[2];
'
# as you can see, a vref can also 'die' if it wishes to, and it'll take the
# whole update with it if it does. No messing around with sending back a
# vref, having it run through the matches, and printing the DENIED message,
# etc. However, if your push is running from a script, and that script is
# looking for the word "DENIED" or something, then this won't work...

View file

@ -1,4 +1,4 @@
#!/bin/sh
#!/bin/bash
# gitolite VREF to find autogenerated files

View file

@ -5,7 +5,7 @@ use warnings;
# gitolite VREF to check if there are any merge commits in the current push.
# THIS IS DEMO CODE; please read all comments below as well as
# doc/vref.mkd before trying to use this.
# doc/virtual-refs.mkd before trying to use this.
# usage in conf/gitolite.conf goes like this:

View file

@ -0,0 +1,52 @@
# F=shipped_ADCs brief descriptions of the shipped ADCs (admin-defined commands)
(...with pointers to further information where needed)
----
**able**: enable/disable push access temporarily (such as for taking backups
or other admin chores); details [here][able]. This ADC is meant only for
admins.
**delete-branch**: allow someone to delete a branch that they themselves
created. (i.e., when the user had RWC, but not RWCD, permissions). Details on
this ADC are [here][dbsha]; details on RWC/RWD/RWCD etc are [here][rwcd].
[dbsha]: https://github.com/sitaramc/gitolite/commit/89b68bf5ca99508caaa768c60ce910d7e0a29ccf
**fork**: Think of it as a server-side clone; details [here][fork].
**get-rights-and-owner.in-perl**: Most of the ADCs are in shell, so this is a
sample of how to write an ADC in perl.
**git-annex-shell**: allows git-annex to store and retrieve annexed file content in
repositories. To use, install in `$GL_ADC_PATH/ua/git-annex-shell`
**gl-reflog**: show a fake "reflog" from the server, and allow recovery from
deleted branches and bad force pushes; details in source.
**help**: not all shipped ADCs may be enabled by the site admin. Conversely
the admin may create and install his own ADCs that dont ship with gitolite
itself. This ADC displays site-local help, if the site admin enabled it.
**hub**: allow "pull requests" a la github; details [here][hub].
**rm**, **lock**, and **unlock**:<br>
**trash**, **list-trash**, and **restore**:
> two families of repo deletion commands; details [here][wild_repodel]
**sudo**: allow admin to run ADCs on behalf of a user. Useful in support
situations I guess. Details in source.
Note: the Unix "sudo" and "su" programs are most often used to acquire
*higher* privileges, although they're actually designed to go the other way
also. In gitolite we do not do the former, only the latter (i.e., a normal
gitolite user cannot do admin-stuff using this ADC).
**su-expand**, **su-getperms**, **su-setperms**: as above, but for the
internal commands 'expand', 'getperms', and 'setperms'. (These commands are
not ADCs so you cannot simply use the 'sudo' ADC described above).
**who-pushed**: find the last person to push a given commit; details in
source.

56
contrib/adc/able Executable file
View file

@ -0,0 +1,56 @@
#!/bin/sh
. $(dirname $0)/adc.common-functions
is_admin || die "just *what* are you trying to pull, young man?"
op=$1
shift
locs=
while [ -n "$1" ]
do
case $1 in
'@all' )
locs="$locs $HOME"
;;
* )
loc="$GL_REPO_BASE_ABS/$1.git"
[ -d $loc ] && locs="$locs $GL_REPO_BASE_ABS/$1.git"
[ -d $loc ] || echo "ignoring $1..."
;;
esac
shift
done
[ -z "$locs" ] && die "give me '@all' or some reponame"
case $op in
en|enable )
for l in $locs
do
rm -fv $l/.gitolite.down
done
;;
dis|disable )
TEMPDIR=$(mktemp -d -t tmp.XXXXXXXXXX)
export TEMPDIR
trap "/bin/rm -rf $TEMPDIR" 0
echo 'type the message to be shown to users when they try to push; end with Ctrl-D:'
echo > $TEMPDIR/msg
cat >> $TEMPDIR/msg
echo disabling following locations with message:
cat $TEMPDIR/msg
echo
for l in $locs
do
cat $TEMPDIR/msg > $l/.gitolite.down
echo $l
done
;;
* )
die "argument 1 must be 'en' or 'dis'"
;;
esac

View file

@ -0,0 +1,132 @@
#!/bin/sh
# please make sure this file is NOT chmod +x
# this file contains settings for all ADCs at the top, then functions that you
# can call from shell scripts. Other files in this directory have examples.
# all uses require you to "source" this file, like so:
# # at the top of your ADC
# . $(dirname $0)/adc.common-functions
# then you use one of the following functions, like so:
# can_create reponame || die "you can't create reponame"
# can_write reponame || die "you can't write reponame"
# can_read reponame || die "you can't read reponame"
# is_admin || die "you're not an admin"
# IMPORTANT NOTE: all the can_* functions set $repo to the normalised reponame
# (i.e., with '.git' extension removed if it was supplied).
# ------------------------------------------------------------------------------
# settings for various ADCs, collected in one place for ease of keeping local
# settings intact during upgrades (you only have to worry about this file
# now). Documentation for the variables, however, is in the respective ADC
# settings for 'rm' ADC
ARE_YOU_SURE=1
USE_LOCK_UNLOCK=1
# settings for 'trash' ADC
TRASH_CAN=$GL_REPO_BASE_ABS/deleted
TRASH_SUFFIX=`date +%Y-%m-%d_%H:%M:%S`
# settings for 'hub' ADC
BASE_FETCH_URL="git://gl.example.com"
GL_FORKED_FROM="gl-forked-from"
# KDE may set this to kde-cloned-from for historical reasons
# Change to 1 to make -list the default action for the 'help' command
HELP_LIST_DEFAULT=0
# name of "admin" group (see is_admin() below before uncommenting)
# ADMIN_GROUPNAME=admins
# ------------------------------------------------------------------------------
die() { echo "$@"; exit 1; }
# test an option value more concisely
opt() {
[ "$1" = "1" ] && return 0
return 1
}
valid_owned_repo() {
# check that an arg passed is a valid repo and the current user owns it
[ -z "$1" ] && die need a repo name
get_rights_and_owner $1
[ "$owner" = "$GL_USER" ] || die "$repo does not exist or is not yours!"
# and we sneak this in too, quietly :)
cd $GL_REPO_BASE_ABS
}
# NOTE: this also sets $repo to the normalised (without .git suffix) reponame
get_rights_and_owner() {
local ans
repo=${1%.git}
ans=$(perl -I$GL_BINDIR -Mgitolite -e "cli_repo_rights('"$repo"')")
# set shell variables as needed
owner=${ans#* }
rights=${ans% *}
echo $rights | grep C >/dev/null 2>&1 && perm_create=yes || perm_create=
echo $rights | grep R >/dev/null 2>&1 && perm_read=yes || perm_read=
echo $rights | grep W >/dev/null 2>&1 && perm_write=yes || perm_write=
}
can_create() {
get_rights_and_owner ${1%.git}
[ -z "$perm_create" ] && return 1
return 0
}
can_write() {
get_rights_and_owner ${1%.git}
[ -z "$perm_write" ] && return 1
return 0
}
can_read() {
get_rights_and_owner ${1%.git}
[ -z "$perm_read" ] && return 1
return 0
}
# ------------------------------------------------------------------------------
# check if current user is an admin
is_admin() {
# there are two ways to check if someone is an admin. The default (if
# ADMIN_GROUPNAME is not defined) is to check if they have write access to
# the admin repo
if [ -z "$ADMIN_GROUPNAME" ]
then
can_write gitolite-admin || return 1
return 0
fi
# the alternative way is to check membership in $ADMIN_GROUPNAME; please
# remember this method requires GL_BIG_CONFIG to be set
# TODO, pending the code to allow an external query of a user's "group"
# affiliations
in_group $ADMIN_GROUPNAME
}
# ------------------------------------------------------------------------------
grouplist() {
perl -I$GL_BINDIR -Mgitolite -e "cli_grouplist()"
}
in_group() {
local g=$1
grouplist | egrep "(^| )$g( |$)" >/dev/null && return 0
return 1
}

84
contrib/adc/delete-branch Executable file
View file

@ -0,0 +1,84 @@
#!/usr/bin/perl
use strict;
use warnings;
# allow a user to delete a ref if the last create of the ref was done by the
# same user, *and* it was done within a certain time limie
# change this to suit your needs
my $oldest = 60*60*24*7; # in seconds
# use a generic error message to avoid information leak
my $error = "didn't find repo/ref, or the ref is too old, or you did not create it\n";
# ----
die "ENV GL_RC not set\n" unless $ENV{GL_RC};
die "ENV GL_BINDIR not set\n" unless $ENV{GL_BINDIR};
unshift @INC, $ENV{GL_BINDIR};
require gitolite or die "parse gitolite.pm failed\n";
gitolite->import;
# arg check
die "need two arguments, a reponame and a refname\n" unless @ARGV == 2;
# get the repo name
my $repo = shift;
$repo =~ s/\.git$//;
# get the ref name to be deleted, and allow the same convenience shortcut
# (prefix "refs/heads/" if it doesn't start with "refs/") as in the main
# config file
my $ref = shift;
$ref =~ m(^refs/) or $ref =~ s(^)(refs/heads/);
# XXX WARNING: we do not do any access control checking -- we just go by the
# fact that if *you* created a branch within the last $limit seconds (default
# value is 1 week), you are allowed to delete the branch.
# find the earliest log entry we're willing to look at
my $limit = `date -d '$oldest seconds ago' '+%F.%T'`;
# NOTE: this is the format that gitolite uses in its log entries (see sub
# 'get_logfilename in one of the pm files). The logic also depends on the
# fact that this is sortable, because we read backwards and stop when we
# reach something older than $limit
chomp($limit);
# find the last 2 log files; here also we depend on the fact that the file
# *names* are time ordered when sorted
my ($lf1, $lf2) = reverse sort glob("$ENV{GL_ADMINDIR}/logs/gitolite*log");
my $found = 0;
my($ts, $user, $ip, $cmd, $op, $oldsha, $newsha, $logrepo, $logref, $refrule);
for my $lf ($lf1, $lf2) {
next unless $lf;
open(LF, "-|", "tac", $lf) or die "tac $lf failed: $!\n";
while (<LF>) {
($ts, $user, $ip, $cmd, $op, $oldsha, $newsha, $logrepo, $logref, $refrule) = split /\t/;
next unless $refrule;
if ($ts le $limit) {
# we don't look at entries earlier than this
$found = -1;
last;
}
if ($op eq 'C' and $oldsha =~ /^0+$/ and $logrepo eq $repo and $logref eq $ref) {
# creation record found; no need to look at any more entries
$found = 1;
last;
}
}
last if $found;
}
# check user in creation record to make sure it is the same one
if ($found == 1 and $user eq $ENV{GL_USER}) {
chdir("$ENV{GL_REPO_BASE_ABS}/$repo.git") or die "chdir $ENV{GL_REPO_BASE_ABS}/$repo.git failed: $!\n";
system("git", "update-ref", "-d", $ref, $newsha) and die "ref deletion failed\n";
warn "deleted $ref from $repo (created on $ts)\n";
# NOTE: we use warn so this gets into the log in some way; perhaps
# later we can adjust the format to more closely resemble a normal
# remote delete operation
exit 0;
}
print STDERR $error;
exit 1;

45
contrib/adc/fork Executable file
View file

@ -0,0 +1,45 @@
#!/bin/sh
. $(dirname $0)/adc.common-functions
[ -z "$GL_RC" ] && die "ENV GL_RC not set"
[ -z "$2" ] && die "Usage: fork source_repo target_repo"
# all the can_* functions set $repo
can_read $1 || die "no read permissions on $repo"
from=$repo
can_create $2 || die "no create permissions on $repo"
to=$repo
# clone $from to $to
git clone --bare -l $GL_REPO_BASE_ABS/$from.git $GL_REPO_BASE_ABS/$to.git
[ $? -ne 0 ] && exit 1
echo "$from forked to $to"
# fix up creator, gitweb owner, and hooks
cd $GL_REPO_BASE_ABS/$to.git
echo $GL_USER > gl-creater
git config gitweb.owner "$GL_USER"
( $GL_BINDIR/gl-query-rc GL_WILDREPOS_DEFPERMS ) |
SSH_ORIGINAL_COMMAND="setperms $to" $GL_BINDIR/gl-auth-command $GL_USER
# symlink hooks
shopt -s nullglob
# the order is important; "package" hooks must override same-named "user" hooks
for i in `$GL_BINDIR/gl-query-rc GL_ADMINDIR`/hooks/common/* \
`$GL_BINDIR/gl-query-rc GL_PACKAGE_HOOKS `/common/*
do
ln -sf $i $GL_REPO_BASE_ABS/$to.git/hooks
done
if [ -n "$GL_WILDREPOS_DEFPERMS" ]; then
echo "$GL_WILDREPOS_DEFPERMS" > gl-perms
fi
echo "$from" > gl-forked-from
# run gitolite's post-init hook if you can (hook code expects GL_REPO to be set)
export GL_REPO; GL_REPO="$to"
[ -x hooks/gl-post-init ] && hooks/gl-post-init

View file

@ -0,0 +1,52 @@
#!/usr/bin/perl
use strict;
use warnings;
die "ENV GL_RC not set\n" unless $ENV{GL_RC};
die "ENV GL_BINDIR not set\n" unless $ENV{GL_BINDIR};
unshift @INC, $ENV{GL_BINDIR};
require gitolite or die "parse gitolite.pm failed\n";
gitolite->import;
# get the repo name
my $repo = shift;
$repo =~ s/\.git$//;
# IMPORTANT NOTE: to do any of this inside a hook, you should just use
# $ENV{GL_REPO}, since it's guaranteed to be set to the right value
# to do a "level 1" check (repo level -- not branch level), do this:
my ($perm, $creator) = check_access($repo);
# you can pass in any repo name you wish instead of the active repo
# the first return value looks like one of these, so you can just check for
# the presence of "R" or "W" and be done:
# _____R___W_
# _____R_____
# ___________
# The second value is "<gitolite>" for a normal repo, an actual username for
# a wildrepo, or "<notfound>" for a non-existent repo.
# to do a "level 2" check (branches), do something like this
my $ret = check_access($repo, 'refs/heads/foo', 'W', 1);
# the 2nd argument must be a *full* refname (i.e., not "master", but
# "refs/heads/master"). The 3rd argument is one of W, +, C, or D. The 4th
# argument should be any non-false perl value, like 1.
# the return value may look like this:
# refs/.*
# or perhaps this, if you were denied
# DENIED by fallthru
# NOTE: do NOT pass "R" as the 3rd argument. It will seem to work because
# you're merely testing the permissions in this code, but an *actual* "git
# fetch" for even a DENIED ref will succeed if the user has read access to at
# least one branch. This is because the information on what ref is being read
# is not made available externally in any useful way (the way the "update"
# hook gets its arguments when a push happens).

23
contrib/adc/getdesc Normal file
View file

@ -0,0 +1,23 @@
#!/usr/bin/perl
use strict;
use warnings;
die "ENV GL_RC not set\n" unless $ENV{GL_RC};
die "ENV GL_BINDIR not set\n" unless $ENV{GL_BINDIR};
unshift @INC, $ENV{GL_BINDIR};
require gitolite or die "parse gitolite.pm failed\n";
gitolite->import;
my $repo = shift;
die "need a reponame\n" unless $repo;
my $ret = check_access($repo, 'refs/heads/master', '+', 1);
die "sorry you don't have rights to do this\n" if $ret =~ /DENIED/;
wrap_chdir($ENV{GL_REPO_BASE_ABS});
wrap_chdir("$repo.git");
print slurp("description") if -f "description";

123
contrib/adc/git Executable file
View file

@ -0,0 +1,123 @@
#!/usr/bin/perl
# READ ALL INSTRUCTIONS **AND** SOURCE CODE BEFORE DEPLOYING.
# run arbitrary git commands on the server
# ----
# WARNING: HIGHLY INFLAMMABLE. FISSILE MATERIAL, RADIATION HAZARD. HANDLE
# WITH CARE. DO NOT REMOVE MANUFACTURER LABEL. NOT TO BE USED WHILE DRIVING
# OR UNDER THE INFLUENCE OF ALCOHOL. PATIENTS WITH HEART PROBLEMS MUST SEE
# THEIR CARDIOLOGIST BEFORE USING.
# ----
# ok, warnings done, here's the saner description.
#
# This ADC lets you run arbirtrary git commands on any repo on the server.
# The first argument will be the repo name, the second and subsequent
# arguments will be the rest of the git command. For example, to run `git
# describe --tags` on repo `foo`, you would run:
#
# ssh git@server git foo describe --tags
#
# If that looks weird to you, you can use
#
# ssh git@server git --repo=foo describe --tags
#
# (the position remains the same: between 'git' and '<command>')
# SECURITY AND SAFETY NOTES:
#
# - ADC arguments are checked (in `sub try_adc`) to fit `ADC_CMD_ARGS_PATT`
# and the only special characters allowed by that pattern are ".", "_", "@",
# "/", "+", ":", and "-". Thus, *this* adc does not check arguments
# anymore. ANY RISK IN THIS LAXITY IS YOURS, NOT MINE, although I believe
# it is safe enough.
#
# - Most commands don't make sense to allow, even among those that do not
# require a work-tree. Avoid commands that can be done using normal git
# remote access (ls-remote, clone, archive, push, etc). Also, avoid
# commands that *write* to the repo if possible, or at least think/test
# thoroughly before enabling them.
#
# - You have to deal with issues like stdin/out, output files created etc.,
# which is another reason to avoid most of the more complex commands.
#
# - Do not enable prune, gc, etc., if your repos are on NFS/CIFS/etc. See
# http://permalink.gmane.org/gmane.comp.version-control.git/122670 for why.
#
# - The list of commands allowed to be executed, and the permissions required
# to do so, are defined here. Feel free to uncomment any of this to make
# things more relaxed. If you add new ones, note that the permissions can
# only be 'R', 'W', or 'A'. The meanings of R and W are obvious; "A" means
# the user must have write access to the *gitolite-admin* repo to run this
# command -- yeah that's a nice twist innit? ;-)
my %GIT_COMMANDS = (
# annotate => 'R',
# blame => 'R',
'count-objects' => 'R',
describe => 'R',
# diff => 'R',
# 'fast-export' => 'R',
# grep => 'R',
# log => 'R',
# shortlog => 'R',
# 'show-branch' => 'R',
# show => 'R',
# whatchanged => 'R',
# config => 'A', # I strongly discourage un-commenting this
# fsck => 'W', # write access required
# gc => 'W', # write access required
# prune => 'A', # admin access required
# repack => 'A', # admin access required
);
# preliminary stuff; indented just to visually get it out of the way
use strict;
use warnings;
die "ENV GL_RC not set\n" unless $ENV{GL_RC};
die "ENV GL_BINDIR not set\n" unless $ENV{GL_BINDIR};
unshift @INC, $ENV{GL_BINDIR};
require gitolite or die "parse gitolite.pm failed\n";
gitolite->import;
my $no_help = "this command is too dangerous to just show a help message; we don't want anyone\nrunning it without reading the source and understanding the implications!\n";
# get the repo name
my $repo = shift or die $no_help;
$repo =~ s/^--repo=//;
$repo =~ s/\.git$//;
# get the command
my $cmd = shift or die $no_help;
# is it a valid command at all?
exists $GIT_COMMANDS{$cmd} or die "invalid git command\n";
# check access
my $aa = $GIT_COMMANDS{$cmd}; # aa == attempted access
if ($aa eq 'A') {
my ($perm, $creator) = check_access('gitolite-admin');
$perm =~ /W/ or die "no admin access\n";
} else {
my ($perm, $creator) = check_access($repo);
$perm =~ /$aa/ or die "no $aa access to $repo\n";
}
# cd to the repo dir
chdir("$ENV{GL_REPO_BASE_ABS}/$repo.git") or die "chdir failed: $!\n";
# remove or comment the below line to signify you have read and understood all this
die $no_help;
# now run the git command... fingers crossed
unshift @ARGV, "git", $cmd;
print STDERR "+ ", join(" ", @ARGV), "\n";
exec @ARGV;

63
contrib/adc/git-annex-shell Executable file
View file

@ -0,0 +1,63 @@
#!/usr/bin/perl
# This ADC requires unrestricted arguments, so you need to
# install it into $GL_ADC_PATH/ua/git-annex-shell, instead of
# directly into $GL_ADC_PATH/
#
# This requires git-annex version 20111016 or newer. Older versions won't
# be secure.
use strict;
use warnings;
# pull in modules we need
BEGIN {
die "ENV GL_RC not set\n" unless $ENV{GL_RC};
die "ENV GL_BINDIR not set\n" unless $ENV{GL_BINDIR};
unshift @INC, $ENV{GL_BINDIR};
}
use gitolite_rc;
use gitolite;
# ignore @ARGV and look at the original unmodified command
my $cmd=$ENV{SSH_ORIGINAL_COMMAND};
# Expect commands like:
# git-annex-shell 'configlist' '/~/repo'
# git-annex-shell 'sendkey' '/~/repo' 'key'
# The parameters are always single quoted, and the repo path is always
# the second parameter.
# Further parameters are not validated here (see below).
die "bad git-annex-shell command: $cmd"
unless $cmd =~ m#^(git-annex-shell '\w+' ')/\~/([0-9a-zA-Z][0-9a-zA-Z._\@/+-]*)('( .*|))$#;
my $start = $1;
my $repo = $2;
my $end = $3;
die "I dont like some of the characters in $repo\n" unless $repo =~ $REPONAME_PATT;
die "I dont like absolute paths in $cmd\n" if $repo =~ /^\//;
die "I dont like '..' paths in $cmd\n" if $repo =~ /\.\./;
# Modify $cmd, fixing up the path to the repo to include REPO_BASE.
my $newcmd="$start$REPO_BASE/$repo$end";
# Rather than keeping track of which git-annex-shell commands
# require write access and which are readonly, we tell it
# when readonly access is needed.
my ($perm, $creator) = check_access($repo);
if ($perm =~ /W/) {
}
elsif ($perm =~ /R/) {
$ENV{GIT_ANNEX_SHELL_READONLY}=1;
}
else {
die "$perm $repo $ENV{GL_USER} DENIED\n";
}
# Further limit git-annex-shell to safe commands (avoid it passing
# unknown commands on to git-shell)
$ENV{GIT_ANNEX_SHELL_LIMITED}=1;
# Note that $newcmd does *not* get evaluated by the unix shell.
# Instead it is passed as a single parameter to git-annex-shell for
# it to parse and handle the command. This is why we do not need to
# fully validate $cmd above.
log_it();
exec "git-annex-shell", "-c", $newcmd;

101
contrib/adc/gl-reflog Executable file
View file

@ -0,0 +1,101 @@
#!/usr/bin/perl -w
use strict;
use warnings;
die "ENV GL_RC not set\n" unless $ENV{GL_RC};
die "ENV GL_BINDIR not set\n" unless $ENV{GL_BINDIR};
# - show fake "reflog" from gitolite server
# - recover deleted branches
# - recover from bad force pushes
# --------------------
# WARNING
# - heavily dependent on the gitolite log file format (duh!)
# - cannot recover if some other commits were made after the force push
sub usage {
print STDERR <<'EOF';
USAGE
ssh git@server gl-reflog show r1 refs/heads/b1
# shows last 10 updates to branch b1 in repo r1
ssh git@server gl-reflog show r1 refs/heads/b1 20
# shows last 20 entries...
ssh git@server gl-reflog recover r1 refs/heads/b1
# recovers the last update to b1 in r1 if it was a "+"
EOF
exit 1;
}
usage unless (@ARGV >= 3);
# NOTES
# - the verb "recover" is used because this is expected to be used most often
# to recover deleted branches. Plus there's enough confusion in git land
# caused by "reset" and "revert" I thought I should add my bit to it ;-)
# - git's internal reflog is NOT recovered, even if you recover the branch.
# I'm good but not *that* good ;-)
# - since this program produces a log entry that satisfies it's own criteria,
# it acts as a "toggle" for its own action for rewinds (but not for deletes)
my($cmd, $repo, $ref, $limit) = @ARGV;
$limit ||= 10;
unshift @INC, $ENV{GL_BINDIR};
require gitolite or die "parse gitolite.pm failed\n";
gitolite->import;
my ($perm, $creator) = check_access($repo);
die "you don't have read access to $repo\n" unless $perm =~ /R/;
my @logfiles = sort glob("$ENV{GL_ADMINDIR}/logs/*");
# TODO figure out how to avoid reading *all* the log files when you really
# only need the last few
our @loglines;
{
my @f;
local(@ARGV) = @logfiles;
while (<>) {
chomp;
@f = split /\t/;
# field 2 is the userid, 5 is W or +, 6/7 are old/new SHAs
# 8 is reponame, 9 is refname (but all those are 1-based)
next unless $f[3] =~ /^(git-receive-pack|gl-reflog recover) /;
next unless $f[8];
next unless $f[7] eq $repo;
next unless $f[8] eq $ref;
push @loglines, $_;
}
}
if ( $cmd eq 'show' ) {
my $start = @loglines - $limit;
$start = 0 if $start < 0;
map { print "$loglines[$_]\n" } $start .. $#loglines;
exit 0;
}
if ( $cmd eq 'recover' ) {
my @f = split /\t/, $loglines[$#loglines];
die "the last push was not yours\n" unless $f[1] eq $ENV{GL_USER};
die "the last push was not a rewind or delete\n" unless $f[4] eq '+';
my($oldsha, $newsha) = @f[5,6];
if ($newsha =~ /^0+$/) {
print "recovering $repo $ref at $oldsha (was deleted)\n";
} else {
print "recovering $repo $ref at $oldsha (was forced to $newsha)\n";
}
chdir("$ENV{GL_REPO_BASE_ABS}/$repo.git");
my $newsha2 = $newsha;
$newsha2 = '' if $newsha =~ /^0+$/;
system("git", "update-ref", $ref, $oldsha, $newsha2) and
die "repo $repo, update-ref $ref $oldsha $newsha failed...\n";
log_it("", "+\t$newsha\t$oldsha\t$repo\t$ref");
}

63
contrib/adc/help Executable file
View file

@ -0,0 +1,63 @@
#!/bin/sh
. $(dirname $0)/adc.common-functions
# the help adc now takes some options; we need to process them first
[ "$1" = "-list" -o "$HELP_LIST_DEFAULT" = "1" ] && {
# the GL_ADC_PATH directory has files other than ADCs also, notably the
# include file for shell ADCs, and maybe a README or two. Those should be
# chmod -x.
# if you want to temporarily hide any ADC from being listed, do the same
# thing: chmod -x
cd $($GL_BINDIR/gl-query-rc GL_ADC_PATH)
for i in *
do
[ -x $i ] && echo $i
done
exit 0
}
# the local site can have a file called gl-adc-help.txt, which will be used as
# the *entire* help text for this site...
[ -f $HOME/gl-adc-help.txt ] && {
cat $HOME/gl-adc-help.txt
exit 0
}
# or the local site will use the default help text in this file, with an
# optional pre- and post- text that is site local (like maybe the admin's
# contact details)
# pre
[ -f $HOME/gl-adc-pre-help.txt ] && cat $HOME/gl-adc-pre-help.txt
# default help text
echo "
The following adc's (admin-defined commands) are available at this site.
creating a 'fork' of a repo:
the 'fork' adc forks a repo that you have read access to, to a repo that
you have create rights to
deleting/trashing repos:
You can permanently remove a repo using 'rm'. By default, repos are
protected ('lock'ed) from being 'rm'-ed. You have to first 'unlock' a
repo before you can 'rm' it.
A different scheme of handling this is to use 'trash' to move the repo to
a 'trashcan' area. You can then 'list-trash' to see what you have, and
you can then 'restore' whichever repo you need to bring back.
More details can be found in contrib/adc/repo-deletion.mkd (or online at
http://sitaramc.github.com/gitolite/wild_repodel.html)
"
# post
[ -f $HOME/gl-adc-post-help.txt ] && cat $HOME/gl-adc-post-help.txt

30
contrib/adc/htpasswd Normal file
View file

@ -0,0 +1,30 @@
#!/usr/bin/perl
use strict;
use warnings;
BEGIN {
die "ENV GL_RC not set\n" unless $ENV{GL_RC};
die "ENV GL_BINDIR not set\n" unless $ENV{GL_BINDIR};
unshift @INC, $ENV{GL_BINDIR};
}
use gitolite_rc;
use gitolite;
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]*$//;
die "empty passwords are not allowed\n" unless $password;
my $rc = system("htpasswd", "-mb", $HTPASSWD_FILE, $ENV{GL_USER}, $password);
die "htpasswd command seems to have failed with $rc return code...\n" if $rc;

470
contrib/adc/hub Executable file
View file

@ -0,0 +1,470 @@
#!/usr/bin/perl -w
# SECURITY: look for the word SECURITY below and decide...
# handle pull-requests and related stuff
# developer notes:
# - 'requestor' is too long, so I use "bob"; if you see the documentation
# you'll realise this isn't as crazy as you think :-)
use strict;
use warnings;
die "ENV GL_RC not set\n" unless $ENV{GL_RC};
die "ENV GL_BINDIR not set\n" unless $ENV{GL_BINDIR};
sub usage {
print STDERR <<'EOF';
GENERAL USAGE: ssh git@server hub <hub-command> <args>
See docs for concepts; this usage message is only a refresher!
Requestor's commands (repo child):
request-pull child b1 [parent]
request-status child [parent] [request-number]
Parent repo owner's commands (repo parent):
list-requests parent
view-request parent request-number
view-log parent request-number <git log options>
view-diff parent request-number <git diff options>
reject parent request-number
fetch parent request-number
accept parent request-number
EOF
exit 1;
}
our $tempdir;
END {
wrap_chdir($ENV{GL_REPO_BASE_ABS});
system("rm", "-rf", "$tempdir.git") if $tempdir and $tempdir =~ /gl-internal-temp-repo/;
}
my %dispatch = (
rp => \&rp,
'request-pull' => \&rp,
rs => \&rs,
'request-status' => \&rs,
lr => \&lr,
'list-requests' => \&lr,
vr => \&vr,
'view-request' => \&vr,
vl => \&vl,
'view-log' => \&vl,
vd => \&vd,
'view-diff' => \&vd,
reject => \&reject,
fetch => \&fetch,
accept => \&accept,
);
my $cmd = shift || '';
usage() unless ($dispatch{$cmd});
unshift @INC, $ENV{GL_BINDIR};
require gitolite or die "parse gitolite.pm failed\n";
gitolite->import;
# find what is effectively GL_ADC_PATH, then get the config var we're interested in
use FindBin;
my $BASE_FETCH_URL = `. $FindBin::Bin/adc.common-functions; echo \$BASE_FETCH_URL`;
chomp($BASE_FETCH_URL);
my $GL_FORKED_FROM = `. $FindBin::Bin/adc.common-functions; echo \$GL_FORKED_FROM`;
chomp($GL_FORKED_FROM);
my @args = @ARGV; @ARGV = ();
$dispatch{$cmd}->(@args);
# -------------------- bob's commands
sub rp {
# request-pull child b1 [parent]
usage() unless @_ == 2 or @_ == 3;
# implicitly gives owner-parent read access to part of child, so requestor
# should already have read access to child (to prevent someone gaining
# access to child by faking a pull request against it!)
# XXX would it be better to ensure it is writable by Bob, because how/why
# would he make a pull request if he didn't just write to it?
my ($repo, $creator) = readable_repo(shift);
my $ref = valid_ref($repo, shift);
# the parent is either explicitly given, or the name of the parent
# recorded by the 'fork' ADC is used
my $repo_to = shift || parent_repo($repo);
# requestor need not have any access to parent; it is quite possible he
# gets this via git-daemon or something, so we just need to make sure it's
# a valid repo
$repo_to = valid_repo($repo_to);
# the 'cover letter' message comes from STDIN
my $cover = join("", <>);
# now create/update the pull request file
cd2repo($repo_to);
my %hub = get_hub();
$hub{$repo}{$ref}{BOB} = $ENV{GL_USER};
$hub{$repo}{$ref}{COVER} = $cover;
$hub{$repo}{$ref}{TIME} = time();
$hub{$repo}{$ref}{STATUS} = 'pending';
dump_hub(%hub);
}
sub rs {
# request-status child [parent] [request-number]
usage() unless @_ > 0 and @_ < 4; # 1 or 2 or 3
# same checks as in 'rp' above
my ($repo_from, $creator) = readable_repo(shift);
my $repo;
if ($_[0] and $_[0] !~ /^\d+$/) {
# next arg is not a number, so it should be 'parent'
$repo = shift;
} else {
$repo = parent_repo($repo_from);
}
$repo = valid_repo($repo);
my $rqno = 0;
$rqno = shift if ($_[0] and $_[0] =~ /^\d+$/);
# there should not be any arguments left over
usage() if @_;
unless ($rqno) {
cd2repo($repo);
my %hub_full = get_hub();
return unless $hub_full{$repo_from};
my %hub; $hub{$repo_from} = $hub_full{$repo_from};
list_hub('', %hub);
return;
}
my ($child, $ref, %hub) = get_request_N($repo, $rqno);
# this also does a chdir to $repo, by the way
my %hub1; $hub1{$child}{$ref} = $hub{$child}{$ref};
list_hub('', %hub1);
print "\nMessage:\n$hub1{$child}{$ref}{COVER}\n";
}
# -------------------- alice's commands
sub lr {
# list-requests parent [optional search strings]
usage() unless @_ >= 1;
# Alice must have write access to parent, otherwise she can't really
# accept a pull request eventually right?
my ($repo, $creator) = writable_repo(shift);
cd2repo($repo);
my %hub = get_hub();
return unless %hub;
# create the search pattern. ADC arg checking is very strict; it doesn't
# allow &, | etc., so we just generate an OR condition out of the pieces
my $patt = join("|", @_);
list_hub($patt, %hub);
}
sub vr {
# view-request parent request-number
usage() unless @_ == 2;
my ($repo, $n) = @_;
my ($child, $ref, %hub) = get_request_N($repo, $n);
# this also does a chdir to $repo, by the way
my %hub1; $hub1{$child}{$ref} = $hub{$child}{$ref};
list_hub('', %hub1);
print "\nMessage:\n$hub1{$child}{$ref}{COVER}\n";
}
sub vl {
# view-log parent request-number <git log options>
usage() unless @_ >= 2;
my ($repo, $n) = (shift, shift);
my ($child, $ref, %hub) = get_request_N($repo, $n);
# so now we can find the set of SHAs that we already have
# XXX should we include tags also?
my @known_shas = grep { chomp; } `git for-each-ref refs/heads --format='%(objectname)'`;
# make a copy of the child repo (Bob's repo) containing only the ref being
# offered for fetch, then cd to it. This is easier to do than to sanitise
# all possible git-log arguments. We're doing this to prevent Alice from
# seeing anything more than the ref offered.
temp_clone($child, $ref);
# verify the list of "known_shas" because what's known in Alice's repo may
# not be known here. While you're about it, negate them. (We don't want
# to use "--not" because we're not sure what arguments the user will want
# to add and we don't want to negate some of them by mistake
@known_shas = grep { $_ = `git rev-parse --verify -q $_`; chomp && s/^/^/ } @known_shas;
# run the log command
# XXX SECURITY XXX do we need to check these arguments? Don't forget they
# are restricted by $ADC_CMD_ARGS_PATT (defined in gitolite_rc.pm), which
# is pretty tight to start with, so we know this cannot be used to run
# external programs. The question is, are any of git-log's arguments
# dangerous in their own right?
my @args = ('git', 'log', $ref);
push @args, @known_shas if @known_shas;
check_SHAs($ref, @_);
# each SHA in @_ must be a parent of $ref. Non-shas are not allowed
# since all refs other than $ref have been deleted in the temp clone
push @args, @_ if @_;
system @args;
}
sub vd {
# view-diff parent request-number <git diff options>
usage() unless @_ >= 4;
# we just check for 4 arguments; I guess later on we could also check
# to make sure at least 2 of them are SHAs or something but unless
# there's a security risk it's not needed
my ($repo, $n) = (shift, shift);
my ($child, $ref, %hub) = get_request_N($repo, $n);
# this also does a chdir to $repo, by the way
# now go to the child repo (Bob's repo)
temp_clone($child, $ref);
# run the diff command
# XXX SECURITY XXX do we need to check these arguments? Don't forget they
# are restricted by $ADC_CMD_ARGS_PATT (defined in gitolite_rc.pm), which
# is pretty tight to start with, so we know this cannot be used to run
# external programs. The question is, are any of git-diff's arguments
# dangerous in their own right?
my @args = ('git', 'diff');
check_SHAs($ref, @_);
push @args, @_ if @_;
system @args;
}
sub reject {
# reject parent request-number
usage() unless @_ == 2;
my ($repo, $n) = @_;
writable_repo($repo); # yeah we're throwing away the return values
my ($child, $ref, %hub) = get_request_N($repo, $n);
map { die "request status is already '$_'\n" if $_ ne 'pending' } $hub{$child}{$ref}{STATUS};
# the 'cover letter' message comes from STDIN
my $cover = join("", <>);
$hub{$child}{$ref}{STATUS} = "rejected by $ENV{GL_USER}";
$hub{$child}{$ref}{COVER} .= "\n\nRejected. Message to requestor:\n$cover";
dump_hub(%hub);
}
sub fetch {
# fetch parent request-number
usage() unless @_ == 2;
my ($repo, $n) = @_;
writable_repo($repo); # yeah we're throwing away the return values
my ($child, $ref, %hub) = get_request_N($repo, $n);
map { die "request status is already '$_'\n" if $_ ne 'pending' } $hub{$child}{$ref}{STATUS};
print "user $hub{$child}{$ref}{BOB} asked you to\n\tgit fetch $BASE_FETCH_URL/$child $ref\n";
print "hit enter to accept the fetch request or Ctrl-C to cancel...";
<>;
my $fetched_ref = "refs/heads/requests/child/$ref";
# you're already chdir'd to parent, by get_request_N
system("git", "update-ref", "-d", "refs/heads/$fetched_ref");
system("git", "fetch", "$ENV{GL_REPO_BASE_ABS}/$child.git", "$ref:$fetched_ref");
$hub{$child}{$ref}{STATUS} = "fetched by $ENV{GL_USER}";
dump_hub(%hub);
}
sub accept {
# accept parent request-number
usage() unless @_ == 2;
my ($repo, $n) = @_;
writable_repo($repo); # yeah we're throwing away the return values
my ($child, $ref, %hub) = get_request_N($repo, $n);
map { die "request status is '$_'; must be 'fetched'\n" if $_ !~ /^fetched by / } $hub{$child}{$ref}{STATUS};
# the 'cover letter' message comes from STDIN
my $cover = join("", <>);
$hub{$child}{$ref}{STATUS} = "accepted by $ENV{GL_USER}";
$hub{$child}{$ref}{COVER} .= "\n\nAccepted. Message to requestor:\n$cover";
dump_hub(%hub);
}
# -------------------- service subs
sub assert {
my ($expr, $message) = @_;
eval $expr or die ($message ? "$message\n" : "assert '$expr' failed\n");
}
sub cd2repo {
my $repo = shift;
wrap_chdir("$ENV{GL_REPO_BASE_ABS}/$repo.git");
}
sub dump_hub {
# pwd assumed to git repo.git; dump a file called "gl-adc-hub-requests"
use Data::Dumper;
$Data::Dumper::Indent = 1;
$Data::Dumper::Sortkeys = 1;
my %hub = @_;
my $fh = wrap_open(">", "gl-adc-hub-requests");
print $fh Data::Dumper->Dump([\%hub], [qw(*hub)]);
close $fh;
}
sub get_hub {
# pwd assumed to git repo.git; "do" a file called "gl-adc-hub-requests"
return () unless -w "gl-adc-hub-requests";
our %hub = ();
do "gl-adc-hub-requests" or die "error parsing gl-adc-hub-requests\n";
return %hub;
}
sub get_request_N {
# given a repo and an N, return "child", "ref", and %hub (or die trying!)
# you can't look at pull requests for repos you don't have at least read access to
my ($repo, $creator) = readable_repo(shift);
cd2repo($repo);
my %hub = get_hub();
die "you have no pending requests\n" unless %hub;
my $n = shift || '';
usage() unless ($n =~ /^\d+$/);
my @hub = hub_sort(%hub);
die "you have only " . scalar(@hub) . " requests\n" if @hub < $n;
$n--; # make it 0-relative
return ($hub[$n]->{REPO}, $hub[$n]->{REF}, %hub);
}
sub hub_sort {
my %hub = @_;
my %sorted_hub = ();
for my $child (sort keys %hub) {
for my $ref (sort keys %{ $hub{$child} }) {
my $key = $hub{$child}{$ref}{TIME} . "-$child-$ref";
$sorted_hub{$key} = { REPO=>$child, REF=>$ref };
}
}
my @hub = ();
for my $key (sort keys %sorted_hub) {
push @hub, $sorted_hub{$key};
}
return @hub;
}
sub list_hub {
my ($status, %hub) = @_;
my $header = "#\tchild-repository-name\t(requestor)\tbranch-or-tag-to-pull\tstatus\n----\n";
my @hub = hub_sort(%hub);
my $sn = 0;
for my $pr (@hub) {
$sn++;
my $child = $pr->{REPO};
my $ref = $pr->{REF};
my $pr_status = $hub{$child}{$ref}{STATUS};
next if $status and $pr_status !~ /$status/;
print $header if $header; $header = '';
print "$sn\t$child\t($hub{$child}{$ref}{BOB})\t$ref\t$pr_status\n";
}
}
sub parent_repo {
my ($repo) = shift;
cd2repo($repo);
die "parent repo was not recorded, sorry!\n" unless -f $GL_FORKED_FROM;
my $gff = `cat $GL_FORKED_FROM`;
chomp($gff);
return $gff;
}
sub readable_repo {
my $repo = valid_repo(shift);
my ($perm, $creator) = check_access($repo);
die "$repo does not exist or you have no read access\n" unless $perm =~ /R/;
return ($repo, $creator);
}
sub valid_log_options {
}
sub valid_ref {
my ($repo, $ref) = @_;
cd2repo($repo);
die "invalid ref $ref\n" unless `git cat-file -t $ref` =~ /^commit$/;
die "invalid ref $ref\n" unless `git rev-parse $ref` =~ /^[0-9a-f]{40}$/;
return $ref;
}
sub valid_repo {
my $repo = shift;
$repo =~ s/\.git$//;
die "$repo does not exist or you have no read access\n" unless -d "$ENV{GL_REPO_BASE_ABS}/$repo.git";
return $repo;
}
sub writable_repo {
my $repo = valid_repo(shift);
my ($perm, $creator) = check_access($repo);
die "$repo does not exist or you have no write access\n" unless $perm =~ /W/;
return ($repo, $creator);
}
sub temp_clone {
my ($repo, $ref) = @_;
die "internal error; temp_clone called twice?\n" if $tempdir;
# some of this code is also in "rrr" branch
# first make a temp directory within $REPO_BASE
$ENV{TMPDIR} = $ENV{GL_REPO_BASE_ABS};
$tempdir = `mktemp -d -t gl-internal-temp-repo.XXXXXXXXXX`;
chomp($tempdir);
rename $tempdir, "$tempdir.git";
# make the clone
wrap_chdir("$ENV{GL_REPO_BASE_ABS}");
system("git clone --mirror -l $repo.git $tempdir.git >/dev/null 2>&1");
# go to the clone and delete refs he's not allowed to read
wrap_chdir("$ENV{GL_REPO_BASE_ABS}");
wrap_chdir("$tempdir.git");
# for each available ref
for my $ar (`git for-each-ref refs '--format=%(refname)'`) {
chomp($ar);
system('git', 'update-ref', '-d', $ar) unless $ar eq "refs/heads/$ref";
}
# you've already cd-d to the temp repo, just set the name up properly
$tempdir =~ s/^\Q$ENV{GL_REPO_BASE_ABS}\///;
}
sub check_SHAs {
my $ref = shift;
for (@_) {
next unless /^[0-9a-f]+$/i;
my $fullsha = `git rev-parse $_`;
chomp($fullsha);
die "invalid SHA: $_\n" unless $fullsha =~ /^[0-9a-f]{40}$/;
my $mergebase = `git merge-base $fullsha $ref`;
chomp($mergebase);
die "invalid SHA: $_\n" unless $mergebase eq $fullsha;
}
}

184
contrib/adc/hub.mkd Normal file
View file

@ -0,0 +1,184 @@
# F=hub the 'hub' ADC
## a home grown 'hub' for git repos
This ADC (admin-defined command) helps collaboration among repos. The name is
in honor of github, which is the primary host for gitolite itself.
[Note that github is a web-based service, and does a lot more, like comments,
code reviews, etc., none of which are possible here. We're only talking about
some basic stuff to make the most common operations easier. In particular,
this system is not a replacement for normal project communications like
email!]
**Conventions used**: all through the following description, we will assume
that **Alice** has a repo **parent**, **Bob** has a fork of parent called
**child**, and he is asking Alice to pull a branch he made called **b1** from
child to parent.
In plain git (without using github or similar), the pull request process
starts with an email from Bob to Alice, followed by Alice running a `git fetch
<Bob's URL> b1` (optionally preceded by a `git remote add` for convenience of
long term use). Until this happens she can't see much detail about the
commits to be pulled.
**What this ADC does** is (a) collect all the pull requests she needs to look
at in one place, and (b) allow her to examine the changes with any combination
of `git log` and `git diff` options *without having to fetch the content
first*, and (c) act as a trusted intermediary to allow Alice to fetch *just
one branch* from Bob even if she does not have any access to Bob's repository!
In a situation where there may be lots of requests, being able to take a quick
look at them (and possibly reject some), without having to pull down anything
at all, could be very useful.
Once past that level, she could get the changes down to her workstation using
`git fetch` as normal. With this ADC, however, she has another alternative:
get them over to `parent` (her repo) on the same gitolite server, then later
do a normal `git fetch [origin]` to get it to her workstation. This has the
added advantage that other people, who may be watching her repo but not Bob's,
now get to see what Bob sent her and send comments etc.
## general syntax
The general syntax is
ssh git@server hub <hub-command> <args>
### Bob's commands
The following commands do not cause a fetch, and should be quite fast:
* Bob sends a pull request for branch b1 to repo parent. Notice he does not
mention Alice by name. This command expects a message to be piped/typed
in via STDIN [this message is meant to be transient and is not stored long
term; use email for more "permanent" communications].
echo "hi Alice, please pull" | ssh git@server hub request-pull child b1 [parent]
If `child` was created by a recent version of the 'fork' ADC (or the KDE
'clone' ADC), which records the name of the parent repo on a fork, and it
is *that* repo to which Bob wishes to send the pull request, the third
argument is optional.
* Bob lists the status (fetched/rejected/pending) of pull requests he has
made from his repo child to repo parent. (Note we don't say "accepted" but
"fetched"; see later for why):
ssh git@server hub request-status child [parent]
The second argument is optional the same way as the 3rd argument in the
previous command.
Requests that have been accepted or rejected will usually have some
additional text, supplied by the user who did the reject/accept. Bob can
ask for those details by request number:
ssh git@server hub request-status child [parent] request-number
### Alice's "just looking" commands
* Alice lists requests waiting for her to check and possibly pull into
parent. For each waiting pull request, she will see a serial number, the
originating repo name (child, in our example), the requestor (Bob, here),
and the branch/tag-name (b1) being pulled:
ssh git@server hub list-requests parent
This command also takes an optional list of search strings that are OR-d
together and matched against the 'status' field. So saying
ssh git@server hub list-requests parent fetched pending
would list only items that were 'fetched' or 'pending' (meaning 'accepted'
and 'rejected' would not show up).
* Alice views request # 1 waiting to be pulled into parent. Shows the same
details as above for that request, followed by the message that Bob typed
in when he ran `request-pull`:
ssh git@server hub view-request parent 1
* Alice views the log of the branch she is being asked to pull. Note that
this does NOT involve a fetch, so it will be pretty fast. The log starts
from b1, and stops at a SHA that represents any of the branches in parent.
Alice can use any git-log options she wants to; for instance `--graph`,
`--decorate`, `--boundary`, etc., could be quite useful. However, she
can't use any GUI; it has to be 'git log':
ssh git@server hub view-log parent 1 <git log options>
Notice that the repo name Alice supplies is still her own, although the
log comes from the child repo that Bob wants Alice to pull from. It's
best if you think of this as "view the commits from pull request #1 in
'parent'".
* Alice views the diff between arbitrary commits on child:
ssh git@server hub view-diff parent 1 <git diff options>
Again, she mentions *her* reponame but the diff's come from `child`. Also
note that, due to restrictions on what characters are allowed in arguments
to ADCs, you probably can't do things like `pu^` or `master~3`, and have
to use SHAs instead.
### Alice's "action" commands
* Alice doesn't like what she sees and decides to reject it. This command
expects some text on STDIN as the rejection message:
echo "hi Bob, your patch needs work; see email" | ssh git@server hub reject parent 1
* Alice likes what she sees so far and wants to fetch the branch Bob is
asking her to pull. Note that we are intentionally not using the word
"accept", because this command does not (and cannot, since it is running
on a bare repo on the server) do a pull. What it does is it fetches into
a branch whose name will be `requests/child/b1`.
Note that when multiple requests from the same repo (child) for the same
branch (b1) happen, each "fetch" overwrites the branch. This allows Bob
to continually refine the branch he is requesting for a pull based on
(presumably emailed) comments from Alice. In a way, this is a "remote
tracking branch", just like `refs/remotes/origin/b1`.
ssh git@server hub fetch parent 1
This command will actually fetch from child into parent, and may take time
when the changes are large. However all this is on the server so it does
not involve network traffic:
* Alice has fetched the stuff she wants, looked at it/tested it, and decides
to merge it into `parent`. Once that is done, she runs:
echo "thanks for the frobnitz patch Bob" | ssh git@server hub accept parent 1
to let Bob know next time he checks 'request-status'. Like the `reject`
sub-command, this is also just a status update; no actual 'git' changes
happen.
Notice the sequence of Alice's action commands: it's either 'reject', or a
'fetch' then 'accept'.
## what next?
At this point, you're done with the `hub` ADC. However, all this is on the
bare `parent.git` on the server, and nothing has hit Alice's workstation yet!
Alice will still have to run a fetch or a pull on her workstation if she wants
to check the code in detail, use a tool like gitk, and especially to
compile/test it. *Then* she decides whether to accept or reject the request,
which will have to be communicated via email, of course; see the second para
of this document ;-)
Finally, note that Alice does not actually need to use the `fetch` subcommand.
She can do the traditional thing and fetch Bob's repo/branch directly to her
*workstation*.
## note to the admin: configuration variables
There are 2 configuration variables. `BASE_FETCH_URL` should be set to a
simple "read" URL (so it doesn't even have to be ssh) that almost anyone using
this server can use. It's only used in producing the `git fetch` command
mentioned just above.
`GL_FORKED_FROM` is set to `gl-forked-from` by default, but if your initials
are `JM` you can set it to `kde-cloned-from` to save time and trouble ;-)

13
contrib/adc/list-trash Executable file
View file

@ -0,0 +1,13 @@
#!/bin/sh
. $(dirname $0)/adc.common-functions
# this is a helper ADC for "trash"; see that one for option settings etc
cd $TRASH_CAN 2>/dev/null || exit 0
find . -name gl-creater | sort | while read t
do
owner=
owner=`cat "$t"`
[ "$owner" = "$GL_USER" ] && dirname $t
done | cut -c3-

12
contrib/adc/lock Executable file
View file

@ -0,0 +1,12 @@
#!/bin/sh
. $(dirname $0)/adc.common-functions
# this is a helper ADC for "rm"; see that one for documentation
# cd to repo base and make sure arg1 is a valid repo (also sets $repo)
valid_owned_repo $1
rm -f $repo.git/gl-rm-ok
echo "$repo has been locked. Please run the 'help' adc for more info."

115
contrib/adc/perms Executable file
View file

@ -0,0 +1,115 @@
#!/usr/bin/env python
#
# Original author: Richard Bateman <taxilian@gmail.com>
#
# Any questions or concerns about how this works should be addressed to
# me, not to sitaram. Please note that neither I nor sitaram make any
# guarantees about the security or usefulness of this script. It may
# be used without warantee or any guarantee of any kind.
#
# That said, it works fine for me.
#
# This script is licensed under the New BSD license
# Copyright 2011 Richard Bateman
#
import sys, os
from pygitolite import *
def list(gl, user, repo, filter_var = ""):
perms = gl.get_perms(repo, user)
for var, ppl in perms.iteritems():
if filter_var == "" or filter_var == var:
print "%s:" % var
for item in ppl:
print " %s" % item
def clear(gl, user, repo, filter_var = ""):
try:
os.system(r"echo Are you sure? Type YES \(all caps\) to continue: ")
bval = raw_input()
if bval != "YES":
print "Canceling..."
if filter_var == "":
gl.set_perms(repo, user, {})
else:
perms = gl.get_perms(repo, user)
if filter_var in perms:
del perms[filter_var]
gl.set_perms(repo, user, perms)
print "Perms after clear:"
list(gl, user, repo)
except:
print "An error occured"
def add(gl, user, repo, var, *users):
perms = gl.get_perms(repo, user)
if var not in perms:
perms[var] = []
if len(users) == 0:
print "Usage: perms add %s %s <username>" % (repo, var)
return
for cur in users:
if cur not in perms[var]:
perms[var].append(cur)
gl.set_perms(repo, user, perms)
list(gl, user, repo, var)
def set(gl, user, repo, var, *users):
perms = gl.get_perms(repo, user)
perms[var] = []
if len(users) == 0:
print "Usage: perms set %s %s <username>" % (repo, var)
return
for cur in users:
if cur not in perms[var]:
perms[var].append(cur)
gl.set_perms(repo, user, perms)
list(gl, user, repo, var)
def remove(gl, user, repo, var, *users):
perms = gl.get_perms(repo, user)
if var not in perms:
print "%s isn't a valid type" % var
return
if len(users) == 0:
print "No users specified to remove; perhaps you want clear?"
return
for cur in users:
if cur in perms[var]:
perms[var].remove(cur)
gl.set_perms(repo, user, perms)
list(gl, user, repo, var)
commands = {
"list": list,
"clear": clear,
"add": add,
"set": set,
"remove": remove,
}
if __name__ == "__main__":
if "GL_USER" not in os.environ:
raise "No user!"
user = os.environ["GL_USER"]
command = sys.argv[1] if len(sys.argv) > 2 else ""
if len(sys.argv) < 3 or command not in commands:
print "Usage: perms <command> <repository> <args>"
print " list <repository> [TYPE]"
print " clear <repository>"
print " add <repository> <TYPE> [user and group list]"
print " set <repository> <TYPE> [user and group list]"
print " remove <repository> <TYPE> [user and group list]"
sys.exit(1)
repo = sys.argv[2]
gl = gitolite()
rights, owner = gl.get_rights_and_owner(repo, user)
if owner != user:
print "Either %s does not exist or you are not the owner." % repo
sys.exit(1)
commands[command](gl, user, repo, *sys.argv[3:])

77
contrib/adc/pygitolite.py Normal file
View file

@ -0,0 +1,77 @@
#!/usr/bin/env python
#
# Original author: Richard Bateman <taxilian@gmail.com>
#
# Any questions or concerns about how this works should be addressed to
# me, not to sitaram. Please note that neither I nor sitaram make any
# guarantees about the security or usefulness of this script. It may
# be used without warantee or any guarantee of any kind.
#
# This script is licensed under the New BSD license
# Copyright 2011 Richard Bateman
#
import sys, os, subprocess
class gitolite(object):
def __init__(self, **kvargs):
self.GL_BINDIR = kvargs["GL_BINDIR"] if "GL_BINDIR" in kvargs else os.environ["GL_BINDIR"]
self.user = kvargs["GL_USER"] if "GL_USER" in kvargs else os.environ["GL_USER"]
pass
def gitolite_execute(self, command, std_inputdata = None):
cmd = "perl -I%s -Mgitolite -e '%s'" % (self.GL_BINDIR,command)
p = subprocess.Popen(cmd, shell = True, stdout = subprocess.PIPE, stderr = subprocess.PIPE, stdin = subprocess.PIPE)
stdout, stderr = p.communicate(std_inputdata)
if p.returncode is not 0:
raise Exception(stderr)
return stdout.strip()
def run_custom_command(self, repo, user, command, extra = None):
os.environ["SSH_ORIGINAL_COMMAND"] = "%s %s" % (command, repo)
return self.gitolite_execute('run_custom_command("%s")' % user, extra)
def get_perms(self, repo, user):
full = self.run_custom_command(repo, user, "getperms")
plist = full.split("\n")
perms = {}
for line in plist:
if line == "":
continue
var, strlist = line.split(" ", 1)
perms[var] = strlist.split(" ")
return perms
def set_perms(self, repo, user, perms):
permstr = ""
for var, curlist in perms.iteritems():
if len(curlist) == 0:
continue;
varstr = var
for cur in curlist:
varstr += " %s" % cur
permstr = permstr + "\n" + varstr
resp = self.run_custom_command(repo, user, "setperms", permstr.strip())
def valid_owned_repo(self, repo, user):
rights, user = self.get_rights_and_owner(repo, user)
return owner == user
def get_rights_and_owner(self, repo, user):
if not repo.endswith(".git"):
repo = "%s.git" % repo
ans = self.gitolite_execute('cli_repo_rights("%s")' % repo)
perms, owner = ans.split(" ")
rights = {"Read": "R" in perms, "Write": "W" in perms, "Create": "C" in perms}
return rights, owner
if __name__ == "__main__":
if "GL_USER" not in os.environ:
raise "No user!"
user = os.environ["GL_USER"]
repo = sys.argv[1]
gl = gitolite()
print gl.get_rights_and_owner(repo, user)
print gl.get_perms(repo, user)

View file

@ -0,0 +1,55 @@
# F=wild_repodel deleting repos safely
**NOTE**: this page is about deleting [user-created repos][wild]. It is
**not** about deleting "normal" repos (the kind that are specified in the
gitolite.conf file itself) -- to delete those read [here][repodel].
(see [this thread][thr] on the gitolite mailing list)
[thr]: http://groups.google.com/group/gitolite/browse_thread/thread/fb9cf5a464b6dfee
By default, the old 'rmrepo' ADC (admin-defined command) just went and deleted
the repo -- no questions asked! Sometimes, that could be a disaster -- you
lose the whole thing in one mad moment of typo-ing or frustration. Ouch.
This has been replaced by 2 families of ADCs. I say "families" because each
has one main command and 2 ancillary ones. Admins can choose to install
either, both, or neither family of commands.
Local settings for these ADCs can be found in the common settings file
"adc.common-functions".
1. 'rm' will remove the repo. If `USE_LOCK_UNLOCK` is set, rm will refuse to
remove a locked repo. All repos are locked by default, and you have to
explicitly 'unlock' a repo to remove it. You can also 'lock' it again
instead of removing it of course.
There's also `ARE_YOU_SURE`, for situations where a simple warning
suffices.
You can also use both these flags if you wish.
2. 'trash' will move the repo to a safe location. There are settings for
where this location is and what suffix is added to the repo name. You can
'list-trash' to see what trash you have collected, and you can 'restore'
one of the listed repos.
It's easy to automatically clean out the trash occasionally. By default,
entries in the trash look like this:
foo/r1/2010-10-22_13:14:24
foo/r1/2010-10-22_13:14:50
This shows a repo foo/r1 that was created and trashed twice.
Since the date appears in the name, you can use it with a cutoff to clean
up old repos. Untested example:
cutoff=`date -I -d '28 days ago'`
find $TRASH_CAN -type d -name "20??-??-??_*" | while read r
do
d=`basename $r`
[[ $d < $cutoff ]] && rm -rf $r
done
Put this in cron to run once a day and that should be it.

16
contrib/adc/restore Executable file
View file

@ -0,0 +1,16 @@
#!/bin/sh
. $(dirname $0)/adc.common-functions
repo=$1
[ -z "$1" ] && die need a repo name
owner=
owner=`cat $TRASH_CAN/$repo/gl-creater 2>/dev/null`
[ "$owner" = "$GL_USER" ] || die "$repo is not yours!"
cd $TRASH_CAN
realrepo=`dirname $repo`
[ -d $GL_REPO_BASE_ABS/$realrepo.git ] && die $realrepo already exists
mv $repo $GL_REPO_BASE_ABS/$realrepo.git
echo $repo restored to $realrepo

10
contrib/adc/restrict-admin Executable file
View file

@ -0,0 +1,10 @@
#!/bin/sh
. $(dirname $0)/adc.common-functions
is_admin || die "just *what* are you trying to pull, young man?"
# and here you let them do the dangerous stuff
echo "+rm -rf $GL_REPO_BASE_ABS"
sleep 2
echo ...just kidding!

40
contrib/adc/rm Executable file
View file

@ -0,0 +1,40 @@
#!/bin/sh
. $(dirname $0)/adc.common-functions
[ -z "$GL_RC" ] && die "ENV GL_RC not set"
# options settable in adc.common-functions are
# ARE_YOU_SURE -- prompts "are you sure?"
# USE_LOCK_UNLOCK -- allows delete only if repo is "unlock"ed
# As shipped, both options are set. If you set both of them to "0", repos are
# just deleted blindly, with no confirmation
# helper ADCs: lock, unlock
# cd to repo base and make sure arg1 is a valid repo (also sets $repo)
valid_owned_repo $1
opt $USE_LOCK_UNLOCK && {
if [ -f $repo.git/gl-rm-ok ]
then
:
else
die "$repo is locked! Please run the 'help' adc for more info."
fi
}
opt $ARE_YOU_SURE && {
echo "Are you sure? (type 'yes' if you are)" >&2
read s
[ $s = "yes" ] || die aborting...
}
rm -rf $repo.git
echo "$repo is now GONE!"
# gitweb specific; code for cgit users left as an exercise for the reader
cd $HOME
PROJECTS_LIST=$($GL_BINDIR/gl-query-rc PROJECTS_LIST)
export repo
perl -ni -e 'print unless /^\Q$ENV{repo}.git\E$/' $PROJECTS_LIST

4
contrib/adc/rmrepo Executable file
View file

@ -0,0 +1,4 @@
#!/bin/bash
echo "this command no longer exists. Please run the 'help' adc for more info."
exit 1

76
contrib/adc/rsync Executable file
View file

@ -0,0 +1,76 @@
#!/usr/bin/perl
# 'rsync' helper ADC. See bottom of this file for more info
use strict;
use warnings;
BEGIN {
die "ENV GL_RC not set\n" unless $ENV{GL_RC};
die "ENV GL_BINDIR not set\n" unless $ENV{GL_BINDIR};
unshift @INC, $ENV{GL_BINDIR};
}
use gitolite_rc;
use gitolite;
my $cmd = $ENV{SSH_ORIGINAL_COMMAND};
# test the command patterns; reject if they don't fit. Rsync sends
# commands that looks like one of these to the server (the first one is
# for a read, the second for a write)
# rsync --server --sender -some.flags . some/path
# rsync --server -some.flags . some/path
die "bad rsync command: $cmd"
unless $cmd =~ /^rsync --server( --sender)? -[\w.]+(?: --(?:delete|partial))* \. (\S+)$/;
my $perm = "W";
$perm = "R" if $1;
my $path = $2;
die "I dont like some of the characters in $path\n" unless $path =~ $REPONAME_PATT;
# please see notes below on replacing this line if needed
die "I dont like absolute paths in $cmd\n" if $path =~ /^\//;
die "I dont like '..' paths in $cmd\n" if $path =~ /\.\./;
# ok now check if we're permitted to execute a $perm action on $path
# (taken as a refex) using rsync.
my $ret = check_access('EXTCMD/rsync', "NAME/$path", $perm, 1);
die "$perm NAME/$path $ENV{GL_USER} $ret\n" if $ret =~ /DENIED/;
wrap_chdir($RSYNC_BASE);
log_it();
exec $ENV{SHELL}, "-c", $ENV{SSH_ORIGINAL_COMMAND};
__END__
This is an rsync helper ADC. It is an example of using gitolite's config
language, combined with the 'check_access()' function, to implement access
control for non-git software using a "fake" repo. For historical reasons,
fake repos start with "EXTCMD/". Gitolite does not auto-create fake repos, so
you can use those as namespaces to hold collections of rules for various
purposes.
So here's a fake git repository to collect rsync rules in one place. It
grants permissions to files/dirs within the $RSYNC_BASE tree. A leading NAME/
is required as a prefix; the actual path starts after that. Matching follows
the same rules as given in "FILE/DIR NAME BASED RESTRICTIONS" elsewhere in the
gitolite documentation.
repo EXTCMD/rsync
RW NAME/ = sitaram
RW NAME/foo/ = user1
R NAME/bar/ = user2
RW NAME/baz/.*/.*\.c$ = user3
Finally, if the filepaths your users are reading/writing have names that fall
outside ADC_CMD_ARGS_PATT, see the "passing unchecked arguments" section in
doc/admin-defined-commands.mkd (online at [1]).
[1]: http://sitaramc.github.com/gitolite/doc/admin-defined-commands.html#_passing_unchecked_arguments
If you do this, you will also need to replace the line above (where $path is
being matched against $REPONAME_PATT) with an equivalent check of your own.
Remember that whole command is being sent off to be executed by the *SHELL*.
It may be best to split it into arguments and call rsync directly, preventing
issues with shell metas. Patches welcome ;-)

150
contrib/adc/s3backup Normal file
View file

@ -0,0 +1,150 @@
#!/usr/bin/perl
# Copyright 2011, David Bremner <bremner@debian.org>
#
# This add-on to gitolite is licensed under the same terms as gitolite
# At the time of this writing this is GPL-2, but I also grant
# Sitaram Chamarty the right to re-license as he chooses.
=pod
S3BACKUP - Backup the whole gitolite home directory to amazon s3.
RUNNING
To run it (assuming you call this "s3backup")
As an ADC, only by a user with read access to gitolite-admin :
ssh git@server s3backup [-v error|warning|notice|info|debug] [subcommands]
You must pass the AWS_SECRET_ACCESS_KEY on stdin.
SUBCOMMANDS
You may optionally pass one of the following sub commands
=over
=item incr|full
Run incremental or full backup. By default, leave it to duplicity to
guess.
=item prune
remove all but $S3_KEEP_FULL (see Configuration below)
=back
OPTIONS
-v specifies a log level, passed straight to duplicity.
incr or full specify backup level
CONFIGURATION
Make a file ~git/.s3backup.rc that looks like
$S3_EUROPE=0; # 1 to store in europe
# (only matters at creation)
$S3_KEEP_FULL=3; # keep 3 full backups
$S3_BUCKET="my_bucket"; # s3 bucket name, will be created
# if it doesn't exist.
$S3_AWS_KEY_ID="ABADABA57DOO"; # this is the _non_ secret one
$S3_ENCRYPT_KEY="DEADBEEF AA232332"; # gpg keys, space delimited.
# note that duplicity will abort if
# these keys are not trusted
=cut
use strict;
use warnings;
die "ENV GL_RC not set\n" unless $ENV{GL_RC};
die "ENV GL_BINDIR not set\n" unless $ENV{GL_BINDIR};
unshift @INC, $ENV{GL_BINDIR};
require gitolite or die "parse gitolite.pm failed\n";
gitolite->import;
my ($perm, $creator) = check_access("gitolite-admin");
die "no read access to gitolite-admin\n" unless $perm =~ /R/;
our ($S3_EUROPE, $S3_BUCKET, $S3_AWS_KEY_ID, $S3_ENCRYPT_KEYS, $S3_KEEP_FULL);
chdir($ENV{HOME}) or die "chdir $ENV{HOME} : $!\n";
my $configfile = $ENV{HOME} . "/.s3backup.rc";
do $configfile or die "error parsing $configfile\n";
# ANCHOR ALL PATTERNS
die 'bad value for $S3_EUROPE'
unless (defined($S3_EUROPE) && $S3_EUROPE =~ m/^0|1$/);
die 'bad value for $S3_KEEP_FULL'
unless (defined($S3_KEEP_FULL) && $S3_KEEP_FULL =~ m/^[0-9]+$/);
die 'bad value for $S3_BACKUP'
unless (defined($S3_BUCKET) && $S3_BUCKET =~ m/^[a-z\-_0-9\.]+$/);
die 'bad value for $S3_AWS_KEY_ID'
unless (defined($S3_AWS_KEY_ID) && $S3_AWS_KEY_ID =~ m/^[A-Z0-9]+$/);
die 'bad value for $S3_ENCRYPT_KEYS'
unless (defined($S3_ENCRYPT_KEYS) &&
$S3_ENCRYPT_KEYS =~ m/^[a-fA-F0-9]+(\s+[a-fA-F0-9]+)*/);
my $verbosity='notice';
if (scalar(@ARGV)>0 and $ARGV[0] eq '-v'){
shift;
$verbosity = shift;
}
die "bad verbosity" unless ($verbosity =~ m/^(error|warning|notice|info|debug)$/);
my $subcommand=shift || 'default';
die "bad subcommand" if (defined($subcommand) &&
$subcommand !~ m/^(incr|full|prune|default)$/);
$ENV{AWS_ACCESS_KEY_ID}=$S3_AWS_KEY_ID;
chomp($ENV{AWS_SECRET_ACCESS_KEY}=<>) or
die "must pass SECRET_ACCESS_KEY on stdin";
my @args=();
if ($subcommand ne 'default' ){
if ($subcommand eq 'prune') {
push(@args, 'remove-all-but-n-full', $S3_KEEP_FULL, '--force');
} else {
push(@args, $subcommand);
}
}
push(@args, '--verb', $verbosity);
push(@args, '--s3-use-new-style');
foreach my $key (split(' ',$S3_ENCRYPT_KEYS)){
push(@args, '--encrypt-key', $key);
}
push(@args, '--s3-european-buckets') if ($S3_EUROPE);
push(@args, $ENV{HOME}) unless ($subcommand eq 'prune');
push(@args, 's3+http://'.$S3_BUCKET);
my $semaphore=$ENV{HOME}."/.gitolite.down";
die "$semaphore already exists" if (-f $semaphore);
eval {
open (SEMFD,'>',$semaphore) or die ("failed to open $semaphore");
my $now = gmtime();
print SEMFD "Repo unavailable due to $subcommand backup started at $now GMT\n";
close SEMFD;
system '/usr/bin/duplicity', @args;
};
unlink $semaphore;

25
contrib/adc/setdesc Normal file
View file

@ -0,0 +1,25 @@
#!/usr/bin/perl
use strict;
use warnings;
die "ENV GL_RC not set\n" unless $ENV{GL_RC};
die "ENV GL_BINDIR not set\n" unless $ENV{GL_BINDIR};
unshift @INC, $ENV{GL_BINDIR};
require gitolite or die "parse gitolite.pm failed\n";
gitolite->import;
my $repo = shift;
die "need a reponame\n" unless $repo;
my $ret = check_access($repo, 'refs/heads/master', '+', 1);
die "sorry you don't have rights to do this\n" if $ret =~ /DENIED/;
wrap_chdir($ENV{GL_REPO_BASE_ABS});
wrap_chdir("$repo.git");
wrap_print("description", <>);
print "New description is:\n";
print slurp("description");

275
contrib/adc/sskm Executable file
View file

@ -0,0 +1,275 @@
#!/usr/bin/perl
use strict;
use warnings;
die "ENV GL_RC not set\n" unless $ENV{GL_RC};
die "ENV GL_BINDIR not set\n" unless $ENV{GL_BINDIR};
# pull in modules we need
unshift @INC, $ENV{GL_BINDIR};
require gitolite_rc or die "parse gitolite_rc.pm failed\n";
gitolite_rc->import;
require gitolite or die "parse gitolite.pm failed\n";
gitolite->import;
# get to the keydir
die "keydir not accessible\n" unless -d $gitolite_rc::GL_KEYDIR;
chdir($gitolite_rc::GL_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", "$ENV{GL_REPO_BASE_ABS}/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");
wrap_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("env GL_BYPASS_UPDATE_HOOK=1 git 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("env GL_BYPASS_UPDATE_HOOK=1 git 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("env GL_BYPASS_UPDATE_HOOK=1 git 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("env GL_BYPASS_UPDATE_HOOK=1 git 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("env GL_BYPASS_UPDATE_HOOK=1 git 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("env GL_BYPASS_UPDATE_HOOK=1 git push >/dev/null 2>/dev/null") and die "git push failed\n";
}

237
contrib/adc/sskm.mkd Normal file
View file

@ -0,0 +1,237 @@
# F=sskm changing keys -- self service key management
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 an ADC (admin-defined command) called `sskm`.
----
## 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 enable this ADC for your users:
* 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 ADC. Your users will
eventually come after you with pitchforks ;-)
* there is no way to distinguish `foo/alice.pub` from `bar/alice.pub` using
this ADC. 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.
So, if you have the same *filename* in different subdirectories of
`keydir`, you can't use this tool.
* keys placed in specific folders (perhaps to do [this][authkeyopt], or for
whatever other reasons), will probably not stay in those folders if this
ADC 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).

55
contrib/adc/su-expand Executable file
View file

@ -0,0 +1,55 @@
#!/bin/sh
# adc for someone with admin privs to invoke "expand" on other users.
# This doc block as "WHY", "HOW", and "CAVEATS" sections; I mention that only
# for those people with very small xterms who may miss the CAVEATS section
# otherwise...!
# WHY
# ===
# ...because info has it, and expand doesn't :-)
# (Possible question: Why not add that capability to expand instead to be
# consistent? Probable answer: how about we go the other way and make
# "info" also work like this, and remove that code from core? I like
# having less less code in core!)
# HOW
# ===
# ssh gitolite su-expand . user1 user2 user3
# note that the first argument is still treated as a "pattern", so at
# least put in a "." for a catch-all pattern. Also see caveats for
# patterns below.
# This will output the same thing as "ssh git@server expand ." performed
# by the individual users
# CAVEATS
# =======
# (1) LIMITS TO THE PATTERN
# Due to this being an ADC, and ADC arguments being very, Very, VERY,
# stricly limited, you won't be able to use any regex meta characters
# expect ".". I'll worry about changing it if enough people complain.
# (2) NAME OF THIS SCRIPT
# please name this script something other than "expand", because we can't
# have name clashes between internal commands (info, expand,
# (get|set)(perms|desc), etc) and admin-defined (external) commands. I'm
# calling it su-expand; feel free to change it.
. $(dirname $0)/adc.common-functions
is_admin || die "just *what* are you trying to pull here, $GL_USER?"
pat="$1"; shift
for user
do
SSH_ORIGINAL_COMMAND="expand $pat" $GL_BINDIR/gl-auth-command $user
done

32
contrib/adc/su-getperms Executable file
View file

@ -0,0 +1,32 @@
#!/bin/sh
# adc for someone with admin privs to invoke "setperms/getperms" on other
# users.
# copy this file under both names (su-setperms and su-getperms). It'll figure
# out from the name what to do, since the code is almost the same for both
# cases.
# usage
# ssh gitolite su-setperms user1 reponame # expects STDIN, like setperms
# ssh gitolite su-getperms user1 reponame # prints existing perms, like getperms
# WARNING: DO NOT NAME THIS SCRIPT "setperms" or "getperms". It won't work.
. $(dirname $0)/adc.common-functions
is_admin || die "just *what* are you trying to pull here, $GL_USER?"
# find the command name; we don't do a lot of fancy checking -- we just go
# "safe" and assume that anything but a name of "su-setperms" runs getperms
cmd=getperms
echo $0 | grep su-setperms$ >/dev/null 2>&1 && cmd=setperms
# user and repo
user="$1"; shift
repo="$1"; shift
[ -z "$repo" ] && die requires two parameters: a username and a reponame
SSH_ORIGINAL_COMMAND="$cmd $repo" $GL_BINDIR/gl-auth-command $user

1
contrib/adc/su-setperms Symbolic link
View file

@ -0,0 +1 @@
su-getperms

23
contrib/adc/sudo Executable file
View file

@ -0,0 +1,23 @@
#!/bin/sh
# this command is pretty cool, even if I may say so myself :)
# for any ADC that a normal user can run, like
# ssh git@server adc arguments
# this adc lets a "super user" (defined as "have write access to the
# gitolite-admin repo"), do this
# ssh git@server sudo normal_user adc arguments
. $(dirname $0)/adc.common-functions
is_admin || die "just *what* are you trying to pull, young man?"
user="$1"; shift
cmd="$1"; shift
GL_USER=$user; export GL_USER
[ -x $(dirname $0)/$cmd ] || die "no adc called $cmd"
exec $(dirname $0)/$cmd "$@"

20
contrib/adc/svnserve Normal file
View file

@ -0,0 +1,20 @@
#!/usr/bin/perl
use strict;
use warnings;
BEGIN {
die "ENV GL_RC not set\n" unless $ENV{GL_RC};
die "ENV GL_BINDIR not set\n" unless $ENV{GL_BINDIR};
unshift @INC, $ENV{GL_BINDIR};
}
use gitolite_rc;
use gitolite;
my $cmd = $ENV{SSH_ORIGINAL_COMMAND};
die "expecting 'svnserve -t', got '$cmd'\n" unless $cmd eq 'svnserve -t';
$SVNSERVE =~ s/%u/$ENV{GL_USER}/g;
exec $SVNSERVE;
die "svnserve exec failed\n";

35
contrib/adc/symbolic-ref Executable file
View file

@ -0,0 +1,35 @@
#!/bin/sh
# allow 'git symbolic-ref' over a gitolite connection
# replaces and obsoletes the 'set-head' ADC, which was too specific
# Security: remember all arguments to ADCs must match a very conservative
# pattern; see doc/admin-defined-commands.mkd. Once that is assured, the
# symbolic-ref command has no security related side-effects, so we don't check
# arguments at all -- we just pass them on.
# Note: because of the restriction on allowed characters in arguments, you
# can't supply an arbitrary string to the '-m' option. The simplest
# work-around is-to-just-use-join-up-words-like-this if you feel the need to
# supply a "reason" string. In any case this is useless by default; you'd
# have to have core.logAllRefUpdates set for it to have any meaning.
. $(dirname $0)/adc.common-functions
[ -z "$GL_RC" ] && die "ENV GL_RC not set"
# notice we don't check for existence of arg 3, to allow the query semantics
# of git-symbolic-ref to also work
[ -z "$2" ] && die "usage: symbolic-ref /path/to/repo.git <arguments to git-symbolic-ref>"
# all the can_* functions set $repo
can_write $1 || die "no write permissions on $repo"
to=$repo
shift
# change head
cd $GL_REPO_BASE_ABS/$to.git
git symbolic-ref "$@"

29
contrib/adc/trash Executable file
View file

@ -0,0 +1,29 @@
#!/bin/sh
. $(dirname $0)/adc.common-functions
# options settable in adc.common-functions are
# TRASH_CAN -- where the trashed repos are moved. Can be anywhere that the
# hosting user has write access to. Does not have to be (and ideally
# should NOT be) inside $GL_REPO_BASE_ABS (but see note below)
# TRASH_SUFFIX -- a timestamp, (ideally and by default), to be
# suffixed to the moved repo
# helper ADCs: list-trash, restore
# NOTE: although I would NOT advise it in the interests of keeping things
# simple, it *is* possible to have even deleted repos be *directly* accessible
# via normal gitolite mechanisms (clone, etc). Here's how:
# (1) make TRASH_CAN point somewhere *within* $REPO_BASE
# (2) change TRASH_SUFFIX to something that has a .git at the end, like:
# TRASH_SUFFIX=`date +%Y-%m-%d-%H-%M-%S`.git
# (3) set ACL rules in conf/gitolite.conf for repos named
# deleted/foo/sitaram/bar.*
# cd to repo base and make sure arg1 is a valid repo (also sets $repo)
valid_owned_repo $1
mkdir -p $TRASH_CAN/$repo 2>/dev/null || die "failed creating directory in trashcan"
[ -d $TRASH_CAN/$repo/$TRASH_SUFFIX ] && die try again in a few seconds
mv $repo.git $TRASH_CAN/$repo/$TRASH_SUFFIX
echo "$repo moved to trashcan. Please run the 'help' adc for more info."

12
contrib/adc/unlock Executable file
View file

@ -0,0 +1,12 @@
#!/bin/sh
. $(dirname $0)/adc.common-functions
# this is a helper ADC for "rm"; see that one for documentation
# cd to repo base and make sure arg1 is a valid repo (also sets $repo)
valid_owned_repo $1
touch $repo.git/gl-rm-ok
echo "$repo has been unlocked. Please run the 'help' command for more info."

84
contrib/adc/watch Executable file
View file

@ -0,0 +1,84 @@
#!/bin/sh
. $(dirname $0)/adc.common-functions
# Copyright 2010 Massachusetts Institute of Technology
#
# Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php
# This "watch" ADC can be used to maintain a list of watchers to a
# repository. You can then perform actions on the watchlist from a
# hook or ADC.
#
# An obvious function of this would be to maintain a list of email
# addresses that are sent commit notifications. There are however
# unlimited uses, such as:
#
# * Specifying Twitter accounts that should receive notifications
# of new clones created via the "fork" ADC
# * Specifying hostnames of machines that should be poked to update
# their repository web views
# * Etc...
#
# The parameters are the repository path, "add"/"remove", and the
# identifier. These are added along with your system identifier to a
# gl-watchers file. However, there is an optional argument that allows you
# specify what the identifier is used for. This allows you to perform
# a default action if there is no optional argument, or a specific action
# depending on what the optional argument. For instance, the gl-watchers
# file on the server might look like this (note the actions described would
# only occur if you actually code it in!):
#
# mitchell mitchell@kde.org [no optional argument; would default to email]
# mitchell mitchell@kde.org email [will send email on pushes]
# mitchell projects.kde.org httppost [will send commit details in an
# HTTP POST to proejcts.kde.org]
#
#
# Note that there is no authentication here; any user is allowed to
# set any identifier, although users can only modify their own
# identifiers. (This applies of course only to repositories a user can read
# in the first place!) This script can easily be modified to only allow only
# administrators (those with write access to the gitolite
# administration repository) to add or remove watchers. See the
# commented code below; uncomment it to enable this functionality.
# Adminstrators can use the "sudo" ADC to add entries for specific
# users.
# Uncomment this and comment the corresponding block below to
# restrict this functionality to administrators only
#get_rights_and_owner gitolite-admin
#[ -z "$perm_write" ] && die "just *what* are you trying to pull, young man?"
#get_rights_and_owner $1;
# all the can_* functions set $repo
can_read $1 || die "no read permissions on $repo"
cmd=$2
identifier=$3
arg=$4
[ -z "$cmd" -o -z "$identifier" -o "$cmd" != "add" -a "$cmd" != "remove" ] && die "paramters must be <repopath> <add|remove> <identifier> <optional argument>"
if [ ! -z "$arg" ]
then
identarg="$identifier $arg"
else
identarg="$identifier"
fi
cd $GL_REPO_BASE_ABS/$repo.git
[ ! -e gl-watchers ] && { touch gl-watchers || die "cannot create blank watchers file"; }
[ ! -r gl-watchers ] && die "cannot read watchers file"
grep "^$GL_USER $identarg$" gl-watchers > /dev/null
found=$?
[ $found -eq 0 -a $cmd == "add" ] && die "There is already a watch \"$identarg\" for user $GL_USER"
[ $found -ne 0 -a $cmd == "remove" ] && die "No watch \"$identarg\" found for user $GL_USER"
[ $cmd == "add" ] && echo "$GL_USER $identarg" >> gl-watchers && { echo "Added a watch \"$identarg\" for user $GL_USER"; exit 0; }
[ $cmd == "remove" ] && sed -i -e "/^$GL_USER $identarg$/d" gl-watchers && { echo "Removed a watch \"$identarg\" for user $GL_USER"; exit 0; }
die "16 cores, 320GB of RAM, 4TB of disk, and you give me a command I am not programmed to do. Humans..."

32
contrib/adc/who-pushed Executable file
View file

@ -0,0 +1,32 @@
#!/bin/bash
# find the last person to push the given commit
# XXX we assume the logfile names have been left as default, or at least, if
# changed, in such a way that when sorted by "ls", they come up oldest first
. $(dirname $0)/adc.common-functions
sha=$2
[ -n "$sha" ] || die Usage: ssh ... who-pushed reponame SHA \# at least first few hex digits
# all the can_* functions set $repo
can_read $1 || die "no read permissions on $repo"
cd $GL_REPO_BASE_ABS/$repo.git
logdir=$(dirname $GL_LOG) # uncodumented env var ;-)
ls $logdir | tac | while read lf
do
< $logdir/$lf perl -ne "print if /receive-pack.*\s$repo\s/" | cut -f1,2,3,5- | tac
done | while read ts who IP perm old new repo ref rule
do
save_old=$old
[ "$new" = "00000000000000" ] && continue
[ -z "$old" ] && continue
[ "$old" = "00000000000000" ] && old=
[ -n "$old" ] && old=$old..
git rev-list $old$new 2>/dev/null | grep ^$sha >/dev/null &&
printf "$ts $who $IP $perm $save_old $new $repo $ref $rule\n"
done

View file

@ -0,0 +1,8 @@
# F=emacsmode_ Emacs major mode for gitolite.conf
[Emacs][] major mode for `gitolite.conf` can be found here:
- [GitHub][]
[Emacs]: http://www.gnu.org/software/emacs
[GitHub]: http://github.com/llloret/gitolite-emacs

116
contrib/gerrit.mkd Normal file
View file

@ -0,0 +1,116 @@
# F=gerrit comparing gerrit and gitolite
Gerrit and gitolite have too many high level differences. Size is most
visible of course: 56000 lines of Java versus 1300 lines of perl+shell,
according to David A. Wheeler's 'SLOCCount' tool. Gerrit needs a database (it
comes with a perfectly usable one, or I believe you can use any of the usual
suspects),
and even comes with its own ssh server and git server, and since the git
engine is internal it probably has to include a lot of things that normal git
already has; I wouldn't know for sure.
Gerrit allows a lot more de-centralisation in managing the system, and of
course excels at code review, and it seems geared to really large, open
source-ish projects with lots of contributors.
Gitolite works on a pure command-line install and a plain text file config,
and is designed to run unobtrusively and quite transparently to all developers
-- other than sending the admin their pubkey, nothing really changes for them
in their workflow, toolset, etc. The "lite" in the name still holds, despite
all the extra features being pumped in!
Gitolite was mainly written for a corporate environment, where we really,
really, need branch-level ACLs. While they would certainly love the code
review part, things like *voting* on a change, and so on seem a bit alien, and
it seems to me that code review itself is more likely to be a hierarchical
thing, not a peer-to-peer, "anyone can comment" thing. I could be wrong.
----
In short, gitolite doesn't do the main thing that gerrit does, and gerrit is
so much bigger than gitolite in so many ways, it seems really odd to compare
them at all.
However, it seems gerrit comes closest to gitolite in terms of flexibility of
access control, which is gitolite's main strength, so I thought it would be
useful to compare gitolite with just what is in the "access-control.html" in
the gerrit war file. Or see [this][gdac]. [...and stop sniggering at the
"svn" in the link dammit!]
Note that I don't necessarily list something that both tools have. (For
example, per-user branches denoted by `/USER/` in gitolite and `$(username)`
in gerrit, or being able to distinguish between creating a branch and pushing
to an already created one, etc).
[gdac]: http://gerrit.googlecode.com/svn/documentation/2.1.2/access-control.html
[jwzq]: http://regex.info/blog/2006-09-15/247
**Administrators**: anyone who has gitolite-admin push privs
**Anonymous Users**: gitolite doesn't do that, though the "ssh-plus" branch,
combined with git-daemon2 (Ilari) will allow that in future. When git-daemon2
becomes mainstream, the supporting code in this branch will also be merged
into "master".
**Registered Users**: @all
**Account Groups**: @groups in gitolite. We do allow them to be nested,
although the parsing is single-pass. We also don't have group `foo-admin`
managing membership to group `foo` though; all groups are managed by the eqvt
of "Administrators".
**Project ACLs**: first, let's remember (again) that we don't have any of the code
review stuff :)
* This is a subjective point, but gerrit doesn't give permissions to
individual users, only groups. People who have several small projects
(only one QA, one integrator, etc.), would have to create lots of 1-man
groups, which could be cumbersome.
* **Evaluation** order and priority of access control rules are different.
Gerrit goes by specificity, gitolite goes by sequence. It shouldn't
matter; they're probably equivalent except perhaps in some far-fetched
scenarios.
* Update 2010-10-24: as per [this][gitlog1] Gerrit now has *read* access
control at the branch level -- they can afford to do that because they
have a full jgit stack to play with. Even then it was not easy -- they
had to implement a callback from jgit to gerrit for the fetch, *and* deal
with evil clients that might try to read an object by *pushing* a supposed
change on top of a SHA that they know but don't actually have. (You'll
have to think about this carefully; it may not be immediately obvious to
people who do not know the ref-exchange in the git protocol).
Gitolite is dependent on git itself to provide that -- it just cannot be
done without support from git core. I can see some corporates drooling at
this possibility (makes no sense for open source projects IMO) ;-)
My normal recommendation is to **use separate repos** if you really need
this while continuing to use gitolite. Much simpler and easier to audit
and to convince auditors that "those people can't see that code".
**Categories**:
* gitolite doesnt have an "owner" for each project in any administrative
sense. Perhaps you could consider whoever has `RW+` perms to be an owner
but it doesn't go beyond what that implies.
* gitolite doesnt do anything special to signed or annotated tags
* Force push is the same as delete by default. However, gitolite can now
allow these two separately if that's how you need it.
Of course, direct pushing clashes with code review, and gerrit recommends
that if you want code review you should not use this feature. [Normal
pushes in gerrit go through a temp branch that is moved to the correct one
after a review is done; direct pushes are all that gitolite has].
* author/committer identity: checking these fields in pushed commits is
likely to be important in some projects, but gitolite doesn't have any
notion of this. Hmm... I smell another feature in the future :)
The rest of it is in areas that the two tools have no overlap on (again, code
review being the main thing), or, as I said at the top, features that both
tools have.
[gitlog1]: http://colabti.org/irclogger/irclogger_log/git?date=2010-09-17#l2710

View file

@ -0,0 +1,15 @@
# F=gitolite_tools_ gitolite-tools
gitolite-tools is a collection of external git commands to work with
gitolite server and repositories:
* git gl-info - Display gitolite server information
* git gl-ls - List accessible gitolite repositories
* git gl-desc - Display or edit description of gitolite wildcard repositories
* git gl-perms - Display or edit permissions of gitolite wildcard repositories
* git gl-htpasswd - Set password for gitweb/apache
## Homepage
The project in GitHub:
[http://github.com/tmatilai/gitolite-tools](http://github.com/tmatilai/gitolite-tools)

View file

@ -0,0 +1,51 @@
# --------------------------------------------
# Per-repo authorization based on gitolite ACL
# Include this in gitweb.conf
# See doc/3-faq-tips-etc.mkd for more info
# please note that the author does not have personal experience with gitweb
# and does not use it. Some testing may be required. Patches welcome but
# please make sure they are tested against a "github" version of gitolite
# and not an RPM or a DEB, for obvious reasons.
# HOME of the gitolite user
my $gl_home = $ENV{HOME} = "/home/git";
# the following variables are needed by gitolite; please edit before using
# this should normally not be anything else
$ENV{GL_RC} = "$gl_home/.gitolite.rc";
# this can have different values depending on how you installed.
# if you used RPM/DEB or "root" methods it **might** be this:
$ENV{GL_BINDIR} = "/usr/local/bin";
# if you used the "non-root" method it **might** be this:
$ENV{GL_BINDIR} = "$gl_home/bin";
# If in doubt take a look at ~/.ssh/authorized_keys; at least one of the lines
# might contain something like:
# command="/home/git/.gitolite/src/gl-auth-command
# and you should use whatever directory the gl-auth-command is in (in this
# example /home/git/.gitolite.src)
# finally the user name
$ENV{GL_USER} = $cgi->remote_user || "gitweb";
# now get gitolite stuff in...
unshift @INC, $ENV{GL_BINDIR};
require gitolite_rc; gitolite_rc -> import;
require gitolite; gitolite -> import;
# set project root etc. absolute paths
$ENV{GL_REPO_BASE_ABS} = ( $REPO_BASE =~ m(^/) ? $REPO_BASE : "$gl_home/$REPO_BASE" );
$projects_list = $projectroot = $ENV{GL_REPO_BASE_ABS};
$export_auth_hook = sub {
my $repo = shift;
# gitweb passes us the full repo path; so we strip the beginning
# and the end, to get the repo name as it is specified in gitolite conf
return unless $repo =~ s/^\Q$projectroot\E\/?(.+)\.git$/$1/;
# check for (at least) "R" permission
my ($perm, $creator) = &repo_rights($repo);
return ($perm =~ /R/);
};

View file

@ -0,0 +1,17 @@
# F=ldap_helpers ldap helper programs
These programs were contributed by the Nokia MeeGo folks.
The first 2 are perl and shell verisions of programs meant to be used as
`$GL_GET_MEMBERSHIPS_PGM` (see [this][ldap] for more).
* ldap-query-example.pl
* ldap-query-example.sh
The third program is meant to be installed as an adc (admin-defined command,
see [here][ADCs]), and helps users change their LDAP passwords.
* passwd
Enjoy!

View file

@ -0,0 +1,80 @@
#!/usr/bin/perl
#
# Copyright (c) 2010 Nokia Corporation
#
# This code is licensed to you under MIT-style license. License text for that
# MIT-style license is as follows:
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
#
# ldap-query.pl <arg1>
#
# this script is used to perform ldap querys by giving one argument:
# - <arg1> the user UID for ldap search query
#
# NOTICE: This script requires libnet-ldap-perl package to be installed
# to the system.
#
use Net::LDAP;
# Script requires user UID as the only parameter
if ( $ARGV[0] eq '' || $ARGV[1] ne '' )
{
print "ldap-query.pl requires one argument, user's uid\n";
exit 1;
}
$user = $ARGV[0];
# Create communication structure for LDAP connection
$ldap = Net::LDAP->new(
'localhost',
port => 389,
debug => 0,
timeout => 120,
version => 3 ) or die "$@";
# Bind to LDAP with proper user
$ldapret = $ldap->bind( 'cn=administrator,o=company',
password => '5ecretpa55w0rd' );
die "$ldapret->code" if $ldapret->code;
# Create filter for LDAP query
my $filter = '(&'.
'(objectClass=groupAttributeObjectClassName)'.
"(uid=$user)".
')';
# Execute the actual LDAP search to get groups for the given UID
$ldapret = $ldap->search( base => 'ou=users,ou=department,o=company',
scope => 'subtree',
filter => $filter );
# Parse search result to get actual group names
my $default_group = '';
my $extra_groups = '';
foreach my $entry ( $ldapret->entries ) {
$default_group = $entry->get_value( 'defaultGroupAttributeName' ) . ' ' . "$default_group";
$extra_groups = $entry->get_value( 'extraGroupsAttributeName' ) . ' ' . "$extra_groups";
}
# Return group names for given user UID
print "$default_group" . "$extra_groups";

View file

@ -0,0 +1,68 @@
#!/bin/sh
#
# Copyright (c) 2010 Nokia Corporation
#
# This code is licensed to you under MIT-style license. License text for that
# MIT-style license is as follows:
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
#
# ldap-query.sh <arg1>
#
# this script is used to perform ldap querys by giving one argument:
# - <arg1> the user UID for ldap search query
#
# NOTICE: This script requires ldap-utils and sed to be installed to the system.
#
# Script requires user UID as the only parameter
#
if [ $# -ne 1 ]
then
echo "ldap-query.sh requires one argument, user's uid"
exit 1
fi
uid_param="${1}"
# Set needed LDAP search tool options for the query
ldap_host="localhost"
ldap_binddn="cn=administrator,o=company"
ldap_bindpw="5ecretpa55w0rd"
ldap_searchbase="ou=users,ou=department,o=company"
ldap_scope="subtree"
# Construct the command line base with needed options for the LDAP query
ldap_options="-h ${ldap_host} -x -D ${ldap_binddn} -w ${ldap_bindpw} -b ${ldap_searchbase} -s ${ldap_scope}"
# Construct the search filter for the LDAP query for the given UID
ldap_filter="(&(objectClass=groupAttributeObjectClassName)(uid=${uid_param}))"
# Construct return attribute list for LDAP query result
attr1="defaultGroupAttributeName"
attr2="extraGroupsAttributeName"
ldap_attr="${attr1} ${attr2}"
# Execute the actual LDAP search to get groups for the given UID
ldap_result=$(ldapsearch ${ldap_options} -LLL ${ldap_filter} ${ldap_attr})
# Edit search result to get space separated list of group names
ldap_result=$(echo ${ldap_result} | sed -e "s/.* ${attr1}://" -e "s/ ${attr2}://")
# Return group names for given user UID
echo ${ldap_result}

112
contrib/ldap/passwd Normal file
View file

@ -0,0 +1,112 @@
#!/usr/bin/perl
use Net::LDAP;
use Term::ReadPassword;
use Digest::SHA1;
use MIME::Base64;
use Data::UUID;
use Crypt::Cracklib;
my $PASSWD_MIN_LEN = 8;
my $password;
# parse RC file
# $ENV{GL_RC} = "/home/gitolite/.gitolite.rc";
die "parse $ENV{GL_RC} failed: " . ($! or $@) unless do $ENV{GL_RC};
# These come from .gitolite.rc file
our ($GL_LDAP_HOST, $GL_LDAP_BIND_DN, $GL_LDAP_BIND_PASSWORD, $GL_LDAP_USER_DN);
$Term::ReadPassword::ALLOW_STDIN = 1;
# NOTICE: For some reason Perl fails to disable terminal echo
# so following warning about ECHO must be given to the user
# Warn about password echo because of bugs in Perl ReadPasword
print "\nNOTE THAT THE PASSWORD WILL BE ECHOED TO THE SCREEN!\n" .
"Please make sure no one is shoulder-surfing, and make sure\n" .
"you clear your screen and scrollback history after you are done\n" .
"(or close your terminal session).\n\n";
print "Please type in your new password at the prompt.\n\n" .
"Following special keys are available while typing:\n" .
" <BackSpace> key to remove the last character\n" .
" <Ctrl-U> to remove all characters\n" .
" <Ctrl-C> to terminate password change operation\n" .
" <Enter> to end password typing\n";
while ( 1 ) {
print "\n"; # Start reading with new line
$password = read_password("Enter new password: ", 0, 1);
# Check the validity of new password
if ( length( $password ) >= $PASSWD_MIN_LEN # require minimum length
&& $password =~ /([\x20-\x7E])/ # require printable characters
&& $password =~ /[a-z]/ # require lower case letter
&& $password =~ /[A-Z]/ # require upper case letter
&& $password =~ /[0-9]/ # require number
&& check( $password ) ) # require other than dictionary words
{
# Re-enter new password to check possible typos
if ( $password ne read_password("Enter password again: ") ) {
print "Passwords do not match!\n";
redo;
} else {
last; # Password is valid and there are no typos, so break out
}
} else { # Given password is not valid
print "Password must contain at least $PASSWD_MIN_LEN characters and numbers,\n" .
"must have both upper and lower case characters,\n" .
"can have special characters like !,",#,...\n" .
"but cannot be any valid dictionary word.\n";
redo;
}
}
# Create hash from the password to be stored to the LDAP
my $ctx = Digest::SHA1->new();
my $ug = new Data::UUID;
my $salt = $ug->create_b64();
$ctx->add( $password );
$ctx->add( $salt );
$password = '{SSHA}' . encode_base64( $ctx->digest . $salt, '' );
# Create communication structure for LDAP connection
my $ldap = Net::LDAP->new( $GL_LDAP_HOST ) or die "$@";
my $r = $ldap->start_tls( verify => 'none',
sslversion => 'tlsv1' );
if ( $r->code ) {
print "Password handling failed with $r->code return code!\n";
log_it( "Password change, LDAP connection failed for $ENV{GL_USER}" );
exit 1;
}
# Bind to LDAP with proper user
$r = $ldap->bind( $GL_LDAP_BIND_DN,
password => $GL_LDAP_BIND_PASSWORD );
if ( $r->code ) {
print "Password update failed with $r->code return code!\n";
log_it( "Password change, LDAP bind failed for $ENV{GL_USER}" );
exit 1;
}
# Update new password to the LDAP
$r = $ldap->modify( "uid=$ENV{GL_USER},
$GL_LDAP_USER_DN",
replace => { 'userPassword', $password } );
if ( $r->code ) {
print "Password change failed!\n" .
"Please contact administrator to change password.\n";
# log_it( "Password change, LDAP modify failed for $ENV{GL_USER}" );
} else {
print "Password changed succesfully.\n";
# log_it( "Password change, LDAP modify done for $ENV{GL_USER}" );
}
$r = $ldap->unbind();

182
contrib/mirrorconf-helper.sh Executable file
View file

@ -0,0 +1,182 @@
#!/bin/bash
# tool to make adding/editing products easier
# see subconf-example.mkd or html version somewhere
# PRE-REQUISITES
# 1. gitolite installed on all servers
# 2. the gitolite-admin repo is also mirrored
# run this program ONLY in a clone of a gitolite-admin repo in a committed
# state. This way a "git diff" will tell you what changed, and a "git status"
# will tell you what new files were created, and you can rollback if needed.
usage() {
cd $od
echo commands:
grep '^#.*$0' $0 | cut -c7-
echo
echo '(please read the inline documentation for more info)'
}
# ------------------------------------------------------------------------------
# COMMANDS
# ------------------------------------------------------------------------------
# adding a new host:
# $0 newhost hostname admin-username
# NOTE: this requires you to first add the newhost to the gitolite.conf file
# in the list of slaves for the admin repo. That is manually done; this
# script will not do it. You will also have to ensure that the new server
# being added has been updated and is receiving changes to the admin repo
# automatically.
# DO NOT PROCEED OTHERWISE. If necessary, check by making a dummy change to
# the admin repo and pushing, then make sure the new server has received the
# change.
# ------------------------------------------------------------------------------
# adding a new product to a master host:
# $0 newprod hostname product-name
# NOTE: the host admin must first create and propagate the
# master/host/prod.conf file (see section 3, "host admins only").
# ------------------------------------------------------------------------------
# adding a new slave to a master/prod combo
# $0 newslave master-hostname product-name slave-hostname
# ------------------------------------------------------------------------------
# new *server*: edit gitolite.conf manually (slaves list for the admin repo)
# everything else is done by this tool
# ASSUMPTIONS: we are in a gitolite-admin clone somewhere
die() { echo "$@" >&2; usage; exit 1; }
finish() { echo >&2; exit 0; }
# go to the conf directory
od=$PWD; export od
git rev-parse --show-toplevel >/dev/null || die not in a git directory?
cd $(git rev-parse --show-toplevel)
cd conf || die cant find a conf/ subdirectory
[ -f gitolite.conf ] || die cant find a gitolite.conf file
verify_host() {
grep config.*gitolite.mirror gitolite.conf |
perl -pe 's/"/ " /g' |
grep " $2 " >/dev/null || die "$2 not found in gitolite.conf mirror config"
}
update_file() {
echo >&2
echo >&2 ==== appending lines to $1 ====
tee -a $1
}
# ------------------------------------------------------------------------------
# adding a new host:
# newhost hostname admin-username
[ "$1" == "newhost" ] && {
[ -z "$2" ] && die "need hostname"
verify_host master $2
[ -f master/$2.conf ] && die "master/$2.conf already exists"
[ -z "$3" ] && die "need admin username for host $2"
(
echo
printf "@$2\t= $2/..*\n" | expand -32
) | update_file host-product-map.conf
# setup the first line of the NAME-restrictions.conf file
[ -f NAME-restrictions.conf ] || echo "repo gitolite-admin" > NAME-restrictions.conf
(
echo
printf "RW\t= $3\n" | expand -40
printf "RW NAME/conf/master/$2/\t= $3\n" | expand -40
) | update_file NAME-restrictions.conf
mkdir -p master
(
echo
echo "include \"master/$2/*.conf\""
) | update_file master/$2.conf
finish
}
# ------------------------------------------------------------------------------
# adding a new product to a master host:
# newprod hostname product-name
[ "$1" == "newprod" ] && {
[ -z "$2" ] && die "need hostname"
verify_host master $2
[ -f master/$2.conf ] || die "host $2 not found; forgot to run 'newhost'?"
[ -z "$3" ] && die "need product name to add"
[ -f master/$2/$3.conf ] || die "master/$2/$3.conf not found; contact host-admin for $2"
[ -f mirrors/$2/$3.conf ] && die "mirrors/$2/$3.conf already exists"
(
echo
printf "@$2\t= $3/..*\n" | expand -32
) | update_file host-product-map.conf
finish
}
# ------------------------------------------------------------------------------
# adding a new slave to a master/prod combo
# newslave master-hostname product-name slave-hostname
[ "$1" == "newslave" ] && {
[ -z "$2" ] && die "need hostname"
verify_host master $2
[ -f master/$2.conf ] || die "host $2 not found; forgot to run 'newhost'?"
[ -z "$3" ] && die "need product name to add"
[ -f master/$2/$3.conf ] || die "master/$2/$3.conf not found; contact host-admin for $2"
[ -z "$4" ] && die "need slave name to add"
verify_host slave $4
# first create lines in slave/slavename/mastername.conf
f="slave/$4/$2.conf"
i="$2/$3.conf"
[ -f $f ] && grep "$i" "$f" >/dev/null && die "$f already contains lines for $i"
mkdir -p slave/$4
(
echo
echo "include \"master/$i\""
echo "include \"mirrors/$i\""
) | update_file "$f"
# now check how many slaves we have for this and overwrite mirrors/$2/$3.conf
sl=$(echo slave/*/$2.conf | perl -pe "chomp; s(slave/(.*?)/$2.conf)(\$1)g")
f="mirrors/$2/$3.conf"
[ -f $f ] && {
echo >&2
echo >&2 "==== overwriting file $f; old contents:"
cat >&2 $f
> $f
}
mkdir -p mirrors/$2
(
echo "repo $3/..*"
echo " config gitolite.mirror.master = \"$2\""
echo " config gitolite.mirror.slaves = \"$sl\""
) | update_file $f
finish
}
usage

View file

@ -0,0 +1,294 @@
# F=mirr_complex_example semi-autonomous mirroring setup example
This document describes one way to do this. Gitolite is powerful so you can
probably find other ways to suit you.
## overview of problem
The example is from real life, with the following characteristics:
* multiple servers (hosts)
* multiple "products", each product has one or more git repos
* different products are "native to" (mastered on) different hosts
* a product may be mirrored to zero or more other hosts (mirrored to zero
hosts means it is **local** to the host)
The admin requirements are:
* the overall system will have one or more **master admins**; they manage
the main config file, for instance.
* each host will have one or more **host admins**
* these host admins should be allowed to create any repos they need (within
one of a set of directory names allocated to them), and assign access to
whomever they please
* they should not be able to "step on each other" -- setup access rules for
repos not in their control
The following can only be done by the master admins:
* authentication (ssh keys) are centrally managed and distributed. Host
admins should not be allowed to do this.
* mirroring setup -- who's the master and who're the slaves for any repo
* allowing redirected pushes from slaves
## overview of setup
We will use p1 as the product, with sam as the master and frodo as a slave.
Assume equivalent text/code for other product/master/slave combos.
This setup imposes the condition that all repos should be under some directory
name; either a product name or, for local repos, a hostname. In our example,
these directory names would be p1 or sam on the host sam, and frodo on the
host frodo.
### gitolite feature recap
We use [delegation][deleg], to ensure that admins for sam can only write
files whose names start with `master/sam/`. The actual files they will write
are `master/sam/p1.conf` etc., one for each product that is mastered on their
server.
We use [subconf][]. When you say `subconf "path/to/foo.conf`, then within
that file (and anything included from it), access can only be defined for
repos that regex-match one of the elements of `@foo`.
## pre-requisites
First, install mirroring on all servers according to the main mirroring
document. Set it up so that the gitolite-admin repo is mastered at one server
and everyone else slaves it.
Also, after (or during) the normal mirroring install, edit `~/.gitolite.rc` on
all servers and set `$GL_WILDREPOS` to 1 (from its default of 0).
## quick setup
* edit your `gitolite.conf` file as given in step 1 below
* ignore all the comments, even the ones that tell you to do something :-)
* change only the names of the admin users, and the names of the servers
in the config lines. Everything else stays the same
* now copy `mirror-conf-helper` from the contrib directory to your home or
some easy to type place, and run it to setup your hosts, products, and slaves
A typical sequence with that script is:
# cd to your gitolite-admin clone
# create a new host
~/mirror-conf-helper newhost sam sam-admin
# create the actual repository and access rules. This is done by the host
# admin for the host on which it is mastered (or you can do it yourself).
# See step 3 below for details.
mkdir -p conf/master/sam
vim conf/master/sam/p1.conf
# now add in some "repo p1/..." and "RW ..." lines for the repos in
# product p1 and save
# add product p1 to the list of repos sam is allowed to control
~/mirror-conf-helper newprod sam p1
# add a slave
~/mirror-conf-helper newslave sam p1 frodo
You can then treat the detailed steps described below as extra information or
"background reading" ;-)
## F=mirrexsteps_ step by step
If the script is not cutting it for you and want to vary the technique for
some reason, or you simply want to gain a better understanding of what is
happening, it may be better to do each step manually instead of just using the
script.
**Note**: all files mentioned below are assumed to be under **`conf/`**. The
only place where you have to explicitly state this is in the delegation code
in the appendix. The rest of the time, "conf/" is assumed.
### (1) `gitolite.conf`
The main config file has these items in it. **Please add them in this
order**.
If you follow this document completely, your gitolite.conf file can be pretty
static, changing only if the master admin changes or you need to add a new
host as slave to the gitolite-admin repo. Therefore you can set it up first.
Here's what it looks like:
# (1.1)---------------------------------------------------------------------
# First the main setup:
@master-admins = sitaram dilbert
repo gitolite-admin
RW+ = @master-admins
config gitolite.mirror.master = "master"
config gitolite.mirror.slaves = "list of slave servers"
# you cannot use continuation lines for this; sorry! You have to list
# them all in ONE long line within one set of double quotes...
# (1.2)---------------------------------------------------------------------
# If you have any files with "convenience" group definitions, pull them in:
include "groups/users.conf"
include "groups/repos.conf"
# (1.3)---------------------------------------------------------------------
# Next is delegation. If you don't want delegation, omit this section,
# and replace all "subconf" commands with "include" in the rest of this
# document.
include "host-product-map.conf"
# create this file; see step A1 in appendix A
repo gitolite-admin
# now that you're adding a NAME/ section, you need this for master
# admins to retain their access
RW+ NAME/ = @master-admins
include "NAME-restrictions.conf"
# create this file; see step A2 in appendix A
# (1.4)---------------------------------------------------------------------
# Now you include the access rules for native repos
# (example: master/sam.conf)
subconf "master/HOSTNAME.conf"
# (1.5)---------------------------------------------------------------------
# After this you have the mirror config for native repos. We place this
# *after* the access rules above, to make sure we override any mirror
# config lines accidentally added by a host admin!
# (example: mirrors/sam/p1.conf)
include "mirrors/HOSTNAME/*.conf"
# (1.6)---------------------------------------------------------------------
# Now we pull in all setup (mirror config *and* access rules) for
# non-native repos. For the product "p1", this file will get pulled in
# when the config is processed on frodo.
# (example: slave/frodo/sam.conf)
subconf "slave/HOSTNAME/*.conf"
You'll get some warnings about missing include files; ignore them.
### (2) `master/sam.conf`
For each host sam, one file called `master/sam.conf` is needed. This file
contains just one line:
include "master/sam/*.conf"
<font color="gray">It is pulled in by the main config file using `subconf
"master/HOSTNAME.conf"`, which -- on host sam -- translates to `subconf
"master/sam.conf"`. The only purpose of this is to setup the subconf
restriction on the combined contents of `master/sam/*.conf`.</font>
### (3) host admins only -- `master/sam/p1.conf`
(recap: the host admins for sam can only write files in `master/sam`).
For each product p1 with master on host sam, the admins for host sam will
create a file `master/sam/p1.conf`. This file will contain reponames (must
start with `p1/`) and access rules for these repos.
If they have some common groupnames etc., they can probably put them in
`master/sam/users.conf` or some such file and pull those in into each of their
product.conf files.
By default, everything is local to their server. (Mirroring can only be setup
by the master admins).
### (4) `mirrors/sam/p1.conf`
For each product p1 mastered on host sam, a file called `mirrors/sam/p1.conf`
will be created, containing mirror config lines for all repos of product p1.
In this case, it could be
repo p1/..*
config gitolite.mirror.master = "sam"
config gitolite.mirror.slaves = "frodo"
If this file does not exist, p1 is local to sam and not mirrored.
### (5) `slave/frodo/sam.conf`
For each product that slave frodo gets from master sam, this file has the
following lines
# pull in the access lines
include "master/sam/p1.conf"
# pull in the mirror config lines
include "mirrors/sam/p1.conf"
<font color="gray">This file is pulled in on a slave server via a `subconf
slave/HOSTNAME/*.conf` line in the main config file. On frodo, this would
pull in `slave/frodo/sam.conf` (among others), establishing, again, a subconf
restriction on `@sam`.</font>
<font color="gray">Security note: what this achieves is that the access lines,
which were written by sam's admins, are parsed on frodo *under the subconf
restriction of "sam"*. This is important to prevent sam's admins from writing
rules for repos they don't own and having them processed on other
servers!</font>
### (6) manual sync
The new repo(s) you just created would not have been synced up to frodo. You
can either make an empty commit and push, or log on to sam and run
gl-mirror-shell request-push p1/reponame
## next steps
Once you've done the initial setup, here's what ongoing additions will
require.
* any new repos that are created for the same *product* require only step 3
* a new *product* will require steps A1, 3, 4 and 5
* a new *host* will require additions in all the steps, including adding the
hostname in the slaves list for the admin repo (this is in the main
gitolite.conf file)
## F=mirrappA_ appendix A: delegation helper files
These two files were briefly mentioned in the delegation setup.
(A1) `conf/host-product-map.conf` has the following contents:
# For each host foo, there will be one line:
# @foo = foo/..*
# line (for local repos for the host).
# For each product bar whose master is a host foo, there will be one line:
# @foo = bar/..*
# to add bar/..* to the allowed patterns when subconf "foo" is in effect
# ------------------------------------------------------------------------------
# so for our example:
@sam = sam/..*
@sam = p1/..*
@frodo = frodo/..*
(A2) `conf/NAME-restrictions.conf` has the following contents:
# For each host foo, there will be two lines:
# RW = username-of-foo-host-admin
# RW NAME/conf/master/foo/ = username-of-foo-host-admin
# IMPORTANT: DO NOT MISS THE TRAILING SLASH IN THE LINE ABOVE!
# ------------------------------------------------------------------------------
repo gitolite-admin # this line is required
RW = sam-admin
RW NAME/conf/master/sam/ = sam-admin
RW = frodo-admin
RW NAME/conf/master/frodo/ = frodo-admin

67
contrib/monkeysphere.mkd Normal file
View file

@ -0,0 +1,67 @@
# F=monkeysphere (contributed doc: integrating gitolite with monkeysphere)
This document attempts to describe one way to integrate
[Monkeysphere](http://web.monkeysphere.info/) authentication
with [gitolite](http://github.com/sitaramc/gitolite).
We presuppose that you have a system with a new enough
version of Monkeysphere to support ssh `authorized_keys`
options, and that you are not making use of
monkeysphere-authentication on this system.
As a first step, import the key or keys you wish to
act as Monkeysphere certifiers into the GnuPG public
keyring of the gitolite user (for example,
`gpg --keyserver pool.sks-keyservers.net --recv-keys B0AE9A02`)
Then edit such keys (`gpg --edit B0AE9A02`) and assign them
*ultimate* ownertrust.
Next install a script of this nature as `post-update.secondary`
in the `hooks/` directory of the `gitolite-admin` repository. You can also
follow the "using hooks" section in gitolite's "admin" document to let
gitolite put your new hook in the correct place.
#!/bin/zsh
# this should use locking
pushd ${GL_ADMINDIR}
if [[ -d monkeydir ]]
then
cp ~/.monkeysphere/authorized_user_ids ~/.monkeysphere/old-authorized_user_ids
rm -f ~/.monkeysphere/new-authorized_user_ids
for i in monkeydir/*.pub
do
username=$i:t:r
for j in ${(f)"$(<$i)"}
do
cat >> ~/.monkeysphere/new-authorized_user_ids <<EOF
$j
command="/usr/share/gitolite/gl-auth-command $username"
no-port-forwarding
no-X11-forwarding
no-agent-forwarding
no-pty
EOF
done
done
mv ~/.monkeysphere/new-authorized_user_ids ~/.monkeysphere/authorized_user_ids
monkeysphere update-authorized_keys
fi
popd
ADMIN_POST_UPDATE_CHAINS_TO=hooks/post-update.tertiary
if [[ -x $ADMIN_POST_UPDATE_CHAINS_TO ]]; then
exec $ADMIN_POST_UPDATE_CHAINS_TO "$@"
fi
Finally, place *username*.pub files containing OpenPGP IDs into
a directory called `monkeydir/` in the root of the gitolite-admin
repository. If everything has been set up correctly, adding
and pushing these files should then result in the appropriate
generation of `~/.ssh/authorized_keys`.

51
contrib/partial-copy/gl-pre-git Executable file
View file

@ -0,0 +1,51 @@
#!/usr/bin/perl
use strict;
use warnings;
# called from gitolite before any git operations are run
# "we", "our repo" => the partial copy
# "main", "pco" => the one which we are a "partial copy of"
my $main=`git config --file $ENV{GL_REPO_BASE_ABS}/$ENV{GL_REPO}.git/config --get gitolite.partialCopyOf`;
chomp ($main);
exit 0 unless $main;
die "ENV GL_RC not set\n" unless $ENV{GL_RC};
die "ENV GL_BINDIR not set\n" unless $ENV{GL_BINDIR};
unshift @INC, $ENV{GL_BINDIR};
require gitolite or die "parse gitolite.pm failed\n";
gitolite->import;
# go to the main repo. Find a list of all the refs it has, and for each one,
# check if this user is allowed to read that ref from our repo. If he is, add
# it to a list.
my %allowed;
wrap_chdir("$ENV{GL_REPO_BASE_ABS}/$main.git");
for my $ref (`git for-each-ref refs/heads '--format=%(refname)'`) {
chomp($ref);
my $ret = check_access($ENV{GL_REPO}, $ref, 'R', 1);
$allowed{$ref} = 1 unless $ret =~ /DENIED/;
}
# now go to our repo and...
wrap_chdir("$ENV{GL_REPO_BASE_ABS}/$ENV{GL_REPO}.git");
# delete all existing refs that are not "allowed" (e.g., refs that were
# previously allowed but now are not, due to config file/rules change)
for my $ref (`git for-each-ref refs '--format=%(refname)'`) {
chomp($ref);
next if $allowed{$ref};
system("git", "update-ref", "-d", $ref);
}
# now copy all allowed branches (and their tags, implicitly)
for my $ref (sort keys %allowed) {
system("git", "fetch", "-f", "$ENV{GL_REPO_BASE_ABS}/$main.git", "$ref:$ref");
}
# now allow the git operation to proceed
exit 0

View file

@ -0,0 +1,130 @@
# F=partialcopy maintaining a partial copy of a repo
The regular documentation on basic access control mentions [here][rpr_] that
it is easy to maintain two repositories if you need a (set of) branch(es) to
be "secret", with one repo that has everything, and another that has
everything but the secret branches.
Here's how gitolite can help do that sanely, with minimal hassles for all
concerned. This will ensure the right branches propagate correctly when
people pull/push -- you don't have to do anything manually after setting it up
unless the rules change.
To start with, here's a **NON-WORKING** config that merely describes what
we're **trying** to achieve:
# THIS WILL NOT WORK!
repo foo
- secret-1$ = wally
RW+ dev/USER/ = wally
RW+ = dilbert alice ashok wally
We want Wally the slacker to not be able to see the "secret-1" branch.
The only way to do this is to have two repos -- one with and the other without
the secret branch.
<font color="gray">These two repos cannot share git objects (to save disk
space) using hardlinks etc. Doing so would cause a data leak if Wally decides
to stop slacking and start hacking. See my conversation with Shawn
[here][gitlog1] for more on this, but it basically involves Wally finding out
the SHA of one of the secret branches, pushing a branch that he claims to have
built on that SHA, then fetching that branch again.
It requires a serious understanding of the git transport protocol, how objects
are sent/received, how thin packs are created, etc., to implement it. Or to
convince yourself that someone's implementation is correct.
Meanwhile, the method described here, once you accept the disk space cost, is
quite understandable to mere mortals like me :-)</font>
In the above example you had 2 sets of read access -- (1) all branches (2) all
branches except secret-1. If you end up with one more set (say, "all branches
except secret-2") then you need one more repo to handle it. If you can afford
the storage, the following recipe can certainly make it *manageable*.
[gitlog1]: http://colabti.org/irclogger/irclogger_log/git?date=2010-09-17#l2710
## first, as usual, the caveats!
* if you change the config to disallow something that used to be allowed,
any tags pointing to objects that Wally's repo acquired before the change,
will keep coming back! That is, branch B1 had a tag T1 within it. Later,
B1 was disallowed for Wally. However, Wally's repo will still retain the
tag T1!
So, if you ever disallow a branch that used to be allowed, it's best to
purge Wally's repo manually and let it get rebuilt on the next access.
Just delete it from the disk, push the gitolite-admin config to force it
to re-create, then access it as a legitimate user.
* this recipe has not been, and will not be, tested with smart http.
* it probably won't play well with wildcard repos either; not tested.
* finally, mirroring support for such repos has not been tested too.
## the basic idea
The basic idea is very simple.
* one repo is the "main" one. It contains all the branches, and is the one
that people with full access will use.
* the other repo (or all the other repos, if you have more than one set, as
described above) is a "partial copy", with only a subset of the branches
in the main repo.
* every time someone accesses the partial copy, the branches that that user
is allowed to read are fetched from the main repo. **See note in example
below**.
* every time someone pushes to the partial copy, the branch being pushed is
sent back to the main repo before the update succeeds.
The main repo is always the canonical/current one. The others may or may not
be uptodate.
## the config file
Here's what we actually need to put in the config file. Note that the
reponames can be whatever you want of course.
repo foo
RW+ = dilbert alice ashok
repo foo-partialcopy-1
- secret-1$ = wally
R = wally
RW+ dev/USER/ = wally
config gitolite.partialCopyOf = foo
**Important notes**:
* Wally must not have any access to "foo". Absolutely none at all.
* Wally's rules for `foo-partialcopy-1` must be written such that restricted
branches are denied. You could list only the branches he's allowed to
read, or you could deny the ones he's not allowed and add a blanket "R"
for the others later, as in this example.
Note that this is the same [deny][] logic that is normally used for write
operations, but applied to "read" in this case. All we're doing is using
this logic to determine what branches from `foo` are allowed to propagate
to the partial copy repo. *This is NOT being used by git to restrict
reads; at the risk of repetition, git does NOT have that capability*.
* All the other users with access to `foo-partialcopy-1` must be under the
same restrictions as Wally. So let's say Ashok is not allowed to view a
branch called "USCO". That needs to be defined in yet another partial
copy repo, and `ashok` must be removed from the access list for `foo`.
## the hooks
The code for both hooks is included in the source directory
`contrib/partial-copy`. Note that this is all done *without* touching
gitolite core at all -- we only use two hooks; both described in the [hooks][]
section. A pictorial representation of all the stuff gitolite runs is
[here][flow]; it may help you understand the role that these two hooks are
playing in this scenario.

208
contrib/partial-copy/t.sh Executable file
View file

@ -0,0 +1,208 @@
#!/bin/bash
# test script for partial copy feature
# WARNING 1: will wipe out your gitolite.conf file (you can recover by usual
# git methods if you need of course).
# WARNING 2: will wipe out (completely) the following directories:
rm -rf ~/repositories/{foo,foo-pc}.git ~/td
# REQUIRED 1: please make sure rc file allows config 'gitolite.partialCopyOf'.
# REQUIRED 2: please make sure you copied the 2 hooks in contrib/partial-copy
# and installed them into gitolite
# REQUIRED 3: the 'tsh' command and associated Tsh.pm in PATH
# ----
set -e
mkdir ~/td
tsh "plan 83";
# ----
cd ~/gitolite-admin
cat << EOF1 > conf/gitolite.conf
# testing partial-copy
repo gitolite-admin
RW+ = tester
repo testing
RW+ = @all
EOF1
tsh "## setup base conf
add conf; commit -m start; empty; ok; push -f; ok"
cat << EOF2 >> conf/gitolite.conf
repo foo
RW+ = u1 u2
repo foo-pc
- secret-1$ = u4
R = u4 # marker 01
RW next = u4
RW+ dev/USER/ = u4
RW refs/tags/USER/ = u4
config gitolite.partialCopyOf = foo
EOF2
tsh "
## setup partial-repos conf
add conf; commit -m partial-repos; empty; ok;
# /master.*partial-repos/
push; ok;
/Init.*empty.*foo\\.git/
/Init.*empty.*foo-pc\\.git/
/u3.*u5.*u6/; !/u1/; !/u2/; !/u4/
"
cd ~/td; rm -rf foo foo-pc
tsh "
## populate repo foo, by user u1
# create foo with a bunch of branches and tags
clone u1:foo
/appear.*cloned/
cd foo
dc a1; dc a2
checkout -b dev/u1/foo; dc f1; dc f2
checkout master; dc m1; dc m2
checkout master; checkout -b next; dc n1; dc n2; tag nt1
checkout -b secret-1; dc s11; dc s12; tag s1t1
checkout next; checkout -b secret-2; dc s21; dc s22; tag s2t1
push --all
/new branch/; /secret-1/; /secret-2/
push --tags
/new tag/; /s1t1/; /s2t1/
"
tsh "
## user u4 tries foo, fails, tries foo-pc
cd $HOME/td
clone u4:foo foo4; !ok
/R access for foo DENIED to u4/
clone u4:foo-pc ; ok;
/Cloning into 'foo-pc'/
/new branch.* dev/u1/foo .* dev/u1/foo/
/new branch.* master .* master/
/new branch.* next .* next/
/new branch.* secret-2 .* secret-2/
!/new branch.* secret-1 .* secret-1/
/new tag.* nt1 .* nt1/
/new tag.* s2t1 .* s2t1/
!/new tag.* s1t1 .* s1t1/
"
tsh "
## user u4 pushes to foo-pc
cd $HOME/td/foo-pc
checkout master
dc u4m1; dc u4m2; push; !ok
/W refs/heads/master foo-pc u4 DENIED by fallthru/
/hook declined to update refs/heads/master/
/To u4:foo-pc/
/remote rejected/
/failed to push some refs to 'u4:foo-pc'/
checkout next
dc u4n1; dc u4n2
push origin next; ok
/To /home/gl-test/repositories/foo.git/
/new branch\] ca3787119b7e8b9914bc22c939cefc443bc308da -> br-\d+/
/u4:foo-pc/
/52c7716..ca37871 next -> next/
tag u4/nexttag; push --tags
/To u4:foo-pc/
/\[new tag\] u4/nexttag -> u4/nexttag/
/\[new branch\] ca3787119b7e8b9914bc22c939cefc443bc308da -> br-\d+/
checkout master
checkout -b dev/u4/u4master
dc devu4m1; dc devu4m2
push origin HEAD; ok
/To /home/gl-test/repositories/foo.git/
/new branch\] 228353950557ed1eb13679c1fce4d2b4718a2060 -> br-\d+/
/u4:foo-pc/
/new branch.* HEAD -> dev/u4/u4master/
"
tsh "
## user u1 gets u4's updates, makes some more
cd $HOME/td/foo
git remote update
/Fetching origin/
/From u1:foo/
/new branch\] dev/u4/u4master -> origin/dev/u4/u4master/
/new tag\] u4/nexttag -> u4/nexttag/
/52c7716..ca37871 next -> origin/next/
checkout master; dc u1ma1; dc u1ma2;
/\[master 8ab1ff5\] u1ma2 at Thu Jul 7 06:23:20 2011/
tag mt2; push-om; ok
checkout secret-1; dc u1s1b1; dc u1s1b2
/\[secret-1 5f96cb5\] u1s1b2 at Thu Jul 7 06:23:20 2011/
tag s1t2; push origin HEAD; ok
checkout secret-2; dc u1s2b1; dc u1s2b2
/\[secret-2 1ede682\] u1s2b2 at Thu Jul 7 06:23:20 2011/
tag s2t2; push origin HEAD; ok
push --tags; ok
git ls-remote origin
/8ab1ff512faf5935dc0fbff357b6f453b66bb98b\trefs/tags/mt2/
/5f96cb5ff73c730fb040eb2d01981f7677ca6dba\trefs/tags/s1t2/
/1ede6829ec7b75a53cd6acb7da64e5a8011e6050\trefs/tags/s2t2/
"
tsh "
## u4 gets updates but without the tag in secret-1
cd $HOME/td/foo-pc
git ls-remote origin;
!/ refs/heads/secret-1/; !/s1t1/; !/s1t2/
/8ab1ff512faf5935dc0fbff357b6f453b66bb98b\tHEAD/
/8ced4a374b3935bac1a5ba27ef8dd950bd867d47\trefs/heads/dev/u1/foo/
/228353950557ed1eb13679c1fce4d2b4718a2060\trefs/heads/dev/u4/u4master/
/8ab1ff512faf5935dc0fbff357b6f453b66bb98b\trefs/heads/master/
/ca3787119b7e8b9914bc22c939cefc443bc308da\trefs/heads/next/
/1ede6829ec7b75a53cd6acb7da64e5a8011e6050\trefs/heads/secret-2/
/8ab1ff512faf5935dc0fbff357b6f453b66bb98b\trefs/tags/mt2/
/52c7716c6b029963dd167c647c1ff6222a366499\trefs/tags/nt1/
/01f04ece6519e7c0e6aea3d26c7e75e9c4e4b06d\trefs/tags/s2t1/
/1ede6829ec7b75a53cd6acb7da64e5a8011e6050\trefs/tags/s2t2/
git remote update
/3ea704d..8ab1ff5 master -> origin/master/
/01f04ec..1ede682 secret-2 -> origin/secret-2/
/\[new tag\] mt2 -> mt2/
/\[new tag\] s2t2 -> s2t2/
!/ refs/heads/secret-1/; !/s1t1/; !/s1t2/
"
echo DONE
# last words...
git ls-remote u4:foo-pc
cd ~/gitolite-admin
perl -ni -e 'print unless /marker 01/' conf/gitolite.conf
git test 'add conf' 'commit -m erdel' 'ok' 'push -f' 'ok'
git ls-remote u4:foo-pc
cat >&2 <<RANT
This is where things go all screwy. Because we still have the *objects*
pointed to by tags s2t1 and s2t2, we still get them back from the main repo.
<sigh>
RANT

View file

@ -0,0 +1,32 @@
#!/usr/bin/perl
use strict;
use warnings;
# called from gitolite before any git operations are run
# "we", "our repo" => the partial copy
# "main", "pco" => the one which we are a "partial copy of"
my $main=`git config --file $ENV{GL_REPO_BASE_ABS}/$ENV{GL_REPO}.git/config --get gitolite.partialCopyOf`;
chomp ($main);
exit 0 unless $main;
die "ENV GL_RC not set\n" unless $ENV{GL_RC};
die "ENV GL_BINDIR not set\n" unless $ENV{GL_BINDIR};
unshift @INC, $ENV{GL_BINDIR};
require gitolite or die "parse gitolite.pm failed\n";
gitolite->import;
my ($ref, $old, $new) = @ARGV;
my $rand = int(rand(100000000));
$ENV{GL_BYPASS_UPDATE_HOOK} = 1;
system("git", "push", "-f", "$ENV{GL_REPO_BASE_ABS}/$main.git", "$new:refs/heads/br-$rand") and die "FATAL: failed to send $new\n";
wrap_chdir("$ENV{GL_REPO_BASE_ABS}/$main.git");
system("git", "update-ref", "-d", "refs//heads/br-$rand");
system("git", "update-ref", $ref, $new, $old) and die "FATAL: update-ref for $ref failed\n";
exit 0;

203
contrib/putty.mkd Normal file
View file

@ -0,0 +1,203 @@
# F=contrib_putty putty and msysgit
This document is intended for those who wish to use Putty/Plink with msysgit.
If you need more help with putty or component programs I suggest looking at [the official putty documentation](http://the.earth.li/~sgtatham/putty/latest/htmldoc/).
**If you are not already using Putty for SSH it is recommended you do _NOT_ use it with msysgit.**
**Please note that this only covers the client side of things, and does not involve server side components to troubleshooting. For that, please see the [ssh-troubleshooting document](http://sitaramc.github.com/gitolite/doc/ssh-troubleshooting.html).**
<a name="msysgit_setup"/>
## msysgit setup
Provided you have putty sessions msysgit should give you the option of specifying a location to plink. If it did not then you will need to add an environment variable named "GIT\_SSH" to point at plink.exe, wherever you have that sitting.
How to do that on your version of windows will likely vary, and is not covered here. For purposes of example, on a 64 bit Windows Vista machine the GIT\_SSH value could be:
C:\Program Files (x86)\PuTTY\plink.exe
Note the lack of quotes.
Testing that msysgit is properly configured can be done from the git bash shell. Simply type (case sensitive, include the quotes):
"$GIT_SSH" -V
You should get a response similar to this:
plink: Release 0.60
If instead you get a "command not found" type error you likely have a typo in your environment variable.
<a name="Going_back_to_OpenSSH"/>
## Going back to OpenSSH
If you wish to go back to OpenSSH all you need to do is delete the GIT\_SSH environment variable. This will vary by your version of windows and thus is not covered here.
<a name="Putty_keys"/>
## Putty keys
If you do not already have putty private key files (.ppk) you will need to make at least one. You can either make a new one or convert an existing key to putty private key format.
Either way, you will want to use puttygen. Note that you can go the other way if you want to stop using putty but keep the key by exporting the key to OpenSSH format.
<a name="Creating_a_new_key"/>
### Creating a new key
To make it simple, I suggest SSH-2 RSA and a bit size of at least 1024. Larger keys will take longer to generate and will take longer to authenticate you on most systems. Making the key is as simple at hitting "Generate".
It is recommended to give the key a meaningful comment.
<a name="Importing_an_existing_key"/>
### Importing an existing key
If you already have an OpenSSH or ssh.com key you can import it using the "Import" option on the "Conversions" menu.
If the key does not have a meaningful comment I would suggest adding one at this point.
<a name="Loading_an_existing_key"/>
### Loading an existing key
If you need to load an existing key to edit or view it you can do so from the File menu.
<a name="Public_key"/>
### Public key
To get your public key for use with gitolite, load (or generate, or import) your key into puttygen. There is a box labeled "Public key for pasting into OpenSSH `authorized_keys` file" there. Copy the text into your preferred text editor and save.
<a name="Putty_ageant"/>
### Putty ageant
Though not required in all cases you may wish to use the putty ageant, pageant, to load your key(s). This will allow for your key(s) to be passphrase protected but not have to enter the passphrase when you go to use them, provided you have already loaded the key into the ageant.
<a name="Sessionless_or_raw_hostname_usage"/>
## Sessionless or raw hostname usage
When using plink without a putty session you pretty much have to load your keys with putty ageant, if only so that plink can find them.
<a name="Putty_sessions"/>
## Putty sessions
In addition to hostnames msysgit can, when using putty, use putty sessions. This works in a manner similar to definitions in OpenSSH's `ssh_config` file. All settings in the session that apply to plink usage will be loaded, including the key file to use and even the username to connect to. Thus, instead of:
ssh://user@host.example.ext:port/repo
You can use:
ssh://session_name/repo
<a name="Host_key_authentication"/>
## Host key authentication
Whether you are using hostnames or sessions you still run into one potential problem. Plink currently wants to validate the server's SSH host key before allowing you to connect, and when git calls plink there is no way to tell it yes. Thus, you may get something like this:
The server's host key is not cached in the registry. You
have no guarantee that the server is the computer you
think it is.
The server's rsa2 key fingerprint is:
ssh-rsa 2048 00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00
Connection abandoned.
fatal: The remote end hung up unexpectedly
Or, in the case of the host key changing, something like this:
WARNING - POTENTIAL SECURITY BREACH!
The server's host key does not match the one PuTTY has
cached in the registry. This means that either the
server administrator has changed the host key, or you
have actually connected to another computer pretending
to be the server.
The new rsa2 key fingerprint is:
ssh-rsa 2048 00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00
Connection abandoned.
fatal: The remote end hung up unexpectedly
The solution is to call plink directly, or start putty and connect with it first. To use plink, open the Git Bash shell and enter:
"$GIT_SSH" hostname_or_session_name
When you do you will see something like this:
The server's host key is not cached in the registry. You
have no guarantee that the server is the computer you
think it is.
The server's rsa2 key fingerprint is:
ssh-rsa 2048 00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00
If you trust this host, enter "y" to add the key to
PuTTY's cache and carry on connecting.
If you want to carry on connecting just once, without
adding the key to the cache, enter "n".
If you do not trust this host, press Return to abandon the
connection.
Store key in cache? (y/n)
Or, in the case of a changed key, a response like this:
WARNING - POTENTIAL SECURITY BREACH!
The server's host key does not match the one PuTTY has
cached in the registry. This means that either the
server administrator has changed the host key, or you
have actually connected to another computer pretending
to be the server.
The new rsa2 key fingerprint is:
ssh-rsa 2048 00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00
If you were expecting this change and trust the new key,
enter "y" to update PuTTY's cache and continue connecting.
If you want to carry on connecting but without updating
the cache, enter "n".
If you want to abandon the connection completely, press
Return to cancel. Pressing Return is the ONLY guaranteed
safe choice.
Update cached key? (y/n, Return cancels connection)
In either case hit y and the key will be stored.
<a name="Debugging_multiple_putty_ageant_keys"/>
## Debugging multiple putty ageant keys
In the event you are using putty ageant with multiple keys loaded you may see the wrong key being used. In general, pageant keys are tried in the order they were loaded into the ageant. If you have descriptive comment on each of your keys you can try connecting with plink in verbose mode to see what keys are being tried. Simply open the Git bash shell and run:
"$GIT_SSH" -v user@hostname
Or, if using sessions with a pre-entered username:
"$GIT_SSH" -v session_name
In either case, you should look for lines like:
Trying Pageant key #0
Authenticating with public key "My Key" from agent
The first says which (numerical) key the ageant is trying. The second tells you the key comment for the authenticating key. To my knowledge the second line should only show up once, for the valid key.
<a name="Setperms_and_other_commands"/>
## Setperms and other commands
When using wildcard repos the setperms command is very important, and other commands can come in handy as well. See their documentation for how to use them, but where they use:
ssh user@host command etc etc
You will want to use:
"$GIT_SSH" user@host command etc etc
Otherwise everything should be identical.
<a name="About_this_document"/>
## About this document
This document was written by Thomas Berezansky (tsbere (at) mvlc (dot) org) in the hopes that it would be useful to those using putty on windows and wishing to use git/gitolite with their putty keys and sessions.

95
contrib/real-users/gl-shell Executable file
View file

@ -0,0 +1,95 @@
#!/usr/bin/perl
use strict;
use warnings;
# ------------------------------------------------------------------------------
# BEGIN site-local changes
# the original login shell your users had (or) the shell to forward
# non-gitolite commands to
my $shell = "/usr/bin/passwd";
# exceptions...
my %shells = (
'some.one' => '/bin/bash',
);
# the gitolite host you want to forward git commands to. Typically this will
# be 'git' or perhaps 'gitolite', but actually could be anything. Don't
# forget to change the host part if needed and mind the quotes!
my $gl_host = 'git@server2';
# ADCs...
# either list all the ADCs you wish to allow forwarding to (SPACE-separated):
my $ADC_list = "";
# -- OR --
# if you upgraded to the new 'help' adc with the '-list' option, set this to 1:
my $detect_ADCs = 0;
# if you do neither, ADCs are not forwarded
# END site-local changes
# ------------------------------------------------------------------------------
# change the user's default shell if he is an 'exception'
$shell= $shells{$ENV{USER}} if $shells{$ENV{USER}};
# no arguments? nothing to forward
exec($shell) if (not @ARGV and not $ENV{SSH_ORIGINAL_COMMAND});
# note: we attempt to work the same whether invoked via 'command=' of authkeys
# (in which case SSH_ORIGINAL_COMMAND is set) or via us being the login shell
# (chsh). Only the latter has been *tested* though.
# massage SSHOC into @ARGV shape for ease of parsing
@ARGV = ("-c", $ENV{SSH_ORIGINAL_COMMAND}) if $ENV{SSH_ORIGINAL_COMMAND};
# we ignore SSHOC from now on...
# ------------------------------------------------------------------------------
# forward normal git ops
forward(@ARGV) if
$ARGV[0] eq '-c' and
$ARGV[1] =~ /^(git-receive-pack|git-upload-pack|git-upload-archive) '(\S+)'$/ and
( not -d "$2" );
# ------------------------------------------------------------------------------
# forward gitolite special commands
forward(@ARGV) if $ARGV[0] eq '-c' and $ARGV[1] =~ /^(info|expand|((set|get)(perms|desc)))( |$)/;
# ------------------------------------------------------------------------------
# forward ADCs
if ($ADC_list or $detect_ADCs) {
$ADC_list ||= `ssh $gl_host help -list`;
$ADC_list =~ s/\s+/ /g;
# find the command he's running
my $cmd = $1 if $ARGV[1] =~ /^(\S+)/;
# forward if the command appears somewhere in the ADC list
forward(@ARGV) if $ARGV[0] eq '-c' and $cmd and $ADC_list =~ /(^| )$cmd( |$)/;
}
# ------------------------------------------------------------------------------
# at this point it's back to local processing
exec($shell, @ARGV);
# ------------------------------------------------------------------------------
# ------------------------------------------------------------------------------
# forward to the gitolite host
sub forward {
# this message is important in debugging and trouble shooting; see
# documentation
print STDERR "[forwarding to $gl_host]\n";
# but first we check for rsa key
-f ".ssh/id_rsa" or die "ask your admin to add you to gitolite";
shift if $_[0] eq '-c';
exec("ssh", "$gl_host", @_);
}

View file

@ -0,0 +1,77 @@
#!/bin/bash
# WARNING 1: probably contains bashisms galore. If you don't have bash,
# please install it.
# NOTE 1: this script is run as root.
# ------------------------------------------------------------------------------
# ------------------------------------------------------------------------------
# BEGIN site-local changes
# the full path to the new login shell to replace these users' existing shell
new_shell="/usr/local/bin/gl-shell"
my_chsh() {
# please replace with appropriate command for your OS/distro. This one is
# suitable at least for Fedora, maybe others also
chsh -s $new_shell $1 >&2
}
# remove these 2 lines after you have done your customisation
[ -f /tmp/done.gl-shell-setup ] || { echo please customise $0 before using >&2; exit 1; }
# END site-local changes
# ------------------------------------------------------------------------------
die() { echo "FATAL: $@" >&2; exit 1; }
# ------------------------------------------------------------------------------
euid=$(perl -e 'print $>')
if [ "$euid" = "0" ]
then
[ -n "$1" ] || die "need a valid username"
user=$1
id $user >/dev/null || die "need a valid username"
# now fix up the user's login shell
my_chsh $user
pubkey="$PWD/$user.pub"
[ -f "$pubkey" ] && {
echo "$user.pub already exists. Shell changed, exiting..." >&2
exit 0
}
# drat... 'cd ~$user` doesn't work...
cd $(bash -c "echo ~$user") || die "can't cd to $user's home directory"
# now set up her rsa key, creating it if needed. This will get used if
# she comes in via password or without agent forwarding.
[ -d .ssh ] || {
mkdir .ssh
chown $user .ssh
chmod go-w .ssh
}
[ -f .ssh/id_rsa.pub ] || {
ssh-keygen -q -N "" -C $user@`hostname` -f .ssh/id_rsa >&2
chown $user .ssh/id_rsa .ssh/id_rsa.pub
chmod go-rw .ssh/id_rsa
chmod go-w .ssh/id_rsa.pub
}
# create alice.pub
cat .ssh/id_rsa.pub > $pubkey
exit 0
else
die "needs to run as root"
fi

View file

@ -0,0 +1,108 @@
# F=password_access password access to gitolite
(a.k.a: turning real users into gitolite users)
## problems
This document solves several different problems. But first some names:
* `alice`: our user
* `server1`: a server on which alice has a shell account or the admin is
willing to give her one
* `git@server2`: the gitolite host (user@server). Server2 may be, but need
not be, the same as server1.
The problems it solves are:
1. Alice doesn't like ssh keys and wants to stick to password access (which
won't work with gitolite!), and she has or can get a real (unix) userid on
server1.
2. Alice is outside your corporate environment and needs to get in to server2
via server1.
It does this by making `alice@server1` act like a "proxy" for `git@server2`.
## what the 2 scripts actually do
* `gl-shell` will become the login shell for these users on server1. This
shell will forward git clone/fetch/push requests to "git" on server2.
This redirection is so transparent that I had to explicitly code a message
("forwarding to git@server") to make troubleshooting easier.
* `gl-shell-setup` sets things up. It needs to be run on server1, where it
changes the user's shell to `gl-shell` (full path), then sets up an RSA
key if needed.
## instructions
### server setup
**Server1**:
* Add the host key for server2 to `/etc/ssh/ssh_known_hosts` on server1.
And if it ever changes, update it.
ssh-keyscan -t rsa,dsa server2 >> /etc/ssh/ssh_known_hosts
* You will need to copy the 2 scripts supplied (in contrib/real-users) to
"/usr/local/bin" on server1 and customise them -- i.e., edit the files and
change stuff in the section clearly-marked "site-local changes".
**NOTE** on fixing the "chsh" function: this is OS-dependent. Use
whatever command the OS on server1 requires for this to work. The
supplied command is good for Fedora. (Server2's OS does not matter for
this customisation, even though the script is needed there also).
**Server2**: We assume gitolite is already installed on server2.
### per-user setup
There are 2 types of users.
1. Alice uses a password to access server1, or Alice uses a pubkey to access
server1 but does not use an agent or enable agent forwarding.
* login as root on server 1.
* make sure a file called 'alice.pub' does NOT exist in the current
directory.
* run `gl-shell-setup alice`. This will create a file called
'alice.pub' (since it does not exist).
2. alice uses a pubkey to access server1, and does agent forwarding.
* login as root on server 1.
* ask the user for the pubkey she uses or get it from the
`authorized_keys` file in her `$HOME/.ssh` on server1. Copy it as
'alice.pub' in the current directory.
* run `gl-shell-setup alice`. (It will not create the pub file, since
it already exists).
You can do this for several users in one shot, and collect all the the
pubkeys.
Once you have collected all of them, send them to server2 or to someone who
has push rights to the admin repo.
## some hints, notes and caveats
This doesn't mean all your users have to be like this. You can have normal
users also. In fact, you can have users who give you a pub key from their
workstation the normal way, as well as use this method. (I strongly suggest
that all such keys be placed in `keydir/indirect/` instead of in `keydir/`).
### security and trust discussion
For type 1 users, a default key *pair* (i.e., *including* a private key) must
be generated on server1 and given to the gitolite admin for inclusion as the
user's key for gitolite.
This means that the user must implicitly trust server1's administrators, since
they can impersonate her to server2. (If server1 and server2 are the same
machine or have the same administrators, this does not matter).
For a type 2 user, no keys need to be generated, and this type of user need
not trust server1 at all. In fact, she's only using this method due to
firewall issues, and the only thing that gl-shell-setup is doing is to run
'chsh'.

View file

@ -0,0 +1,10 @@
# F=vimsyntax_ Vim Syntax Highlight
[Vim][] Syntax highlight for `gitolite.conf` can be found from:
- [vim.org script page][vim.org] (Releases)
- [GitHub][] (Sources)
[Vim]: http://www.vim.org/
[vim.org]: http://www.vim.org/scripts/script.php?script_id=2900
[GitHub]: http://github.com/tmatilai/gitolite.vim

227
doc/1000-words.mkd Normal file
View file

@ -0,0 +1,227 @@
# F=pictures gitolite in pictures
Well, they say a picture speaks a thousand words, so here're a few!
**NOTE**: if you're viewing this file in raw text, please note that some
characters in text within a ditaa drawing may not be ASCII. This is due to a
ditaa flaw that treats even a single hyphen as a line drawing character, so I
had to use Unicode 2010 for it. I expect that I will have to resort to
similar tricks for colon, equals, and many others like it if and when I need
those in text within a ditaa diagram.
## installation and setup
Here's a picture showing the "non-root" install. We assume Alice is the
gitolite admin, and "git" is the hosting user on the server.
.aa
Gitolite install and setup sequence (non_root method, default values)
------------------------------------------------------------------------
/-----------------\ /----------------------\
| user "alice" | | user "git" |
|(on workstation) | | (on server) |
|cPNK | |cGRE |
\-----------------/ \----------------------/
/-----------------\ /----------------------\
|~/.ssh/id_rsa.pub|------->| alice.pub |-----\
\----------+------/ (1) \----------------------/ |
/----------------------\ |
/-------------->| ~/gitolite |-----+
| (2) /-----| | |
| | \----------------------/ |
| | /----------------------\ |
| | | ~/bin |-----+
| \---->| ~/share | |
| (3) \----------------------/ |
/----------+------\ /----------------------\ |
| (github) | | ~/.gitolite.rc | |
|cBLU | |~/.ssh/authorized_keys|<----/
\-----------------/ | ~/.gitolite | (4)
| ~/repositories |
\----------------------/
The files names are there **only for information**. You do **not** have to do
anything to them yourself; in fact you should not! You only need the command
for each step shown:
1. copy the admin's pubkey to the server as "alice.pub"
2. `git clone git://github.com/sitaramc/gitolite` or equivalent
3. `gitolite/src/gl-system-install`
4. `gl-setup -q alice.pub`
Note also that you only need ONE real user on the server. In our example it
is git. In particular, you do NOT create Unix userids for your gitolite
users.
## adding users to gitolite
Once you've done the install, here's how you add users.
.aa
Adding users to gitolite
------------------------------------------------------------------------
/-------------------\ /-----------------------\
| user "git" | | user "alice" |
| (on server) | | (on workstation) |
| cGRE | | cPNK |
\-------------------/ \-----------------------/
/-------------------\ /-----------------------\
| (gitolite) |----------->| ~/gitoliteadmin |
| | (1) | |
| | | |
| |<-----------| |
\-------------------/ (3) \-----------------------/
/-----------------------\
/-------------------\ | (alice@workstation) |
| bob cYEL |----\ |~/gitoliteadmin/keydir|
| ~/.ssh/id_rsa.pub | | |cPNK |
\-------------------/ | +-----------------------+
\------>| bobby.pub |
/-------------------\ (2) +-----------------------+
| carol cYEL |----------->| carol.pub |
| ~/.ssh/id_rsa.pub | +-----------------------+
\-------------------/ | (...) |
\-----------------------/
All this is done from the admin (Alice)'s workstation. The steps are:
1. `git clone git@server:gitolite-admin`
2. obtain pubkeys from each user. Email, USB, DHL, pigeon post, owl mail,
any method you like. Rename each received file to the name of the user,
add a ".pub" at the end, copy it into `keydir/` in the gitolite-admin repo
you cloned.
3. `git add keydir`, then `git commit`, then `git push`
You do NOT need to add Carol or Bob as *real* (Unix) users. You do NOT add
their keys directly anywhere on the server; you do it by cloning, adding keys,
and pushing.
## adding repos to gitolite
Adding a repo is even easier. It's so easy that you don't really need a
picture. OK maybe a small one:
.aa
Adding repos to gitolite
------------------------------------------------------------------------
/-------------------\ /-----------------------\
| user "git" | | user "alice" |
| (on server) | | (on workstation) |
| cGRE | | cPNK |
\-------------------/ \-----------------------/
/-------------------\ /-----------------------\
| (gitolite) |----------->| ~/gitoliteadmin |
| | (1) | |
| | | |
| |<-----------| |
\-------------------/ (3) \-----------------------/
/-----------------------------------\
| (alice@workstation) |
|~/gitoliteadmin/conf/gitolite.conf|
| cPNK |
+-----------------------------------+
| <config lines for repo foo> |
+-----------------------------------+
| <config lines for repo bar> |
+-----------------------------------+
| (...) |
\-----------------------------------/
Again, all this is done from the admin (Alice)'s workstation. Steps one and
three are the same as for adding users, but step 2 consists of adding config
lines for whatever repo you want too add.
1. `git clone git@server:gitolite-admin`
2. edit `conf/gitolite.conf` in the repo clone you just made. Add repo
paragraphs, maybe like this, and save the file:
repo foo
RW+ = alice
RW = bob
3. `git add conf/gitolite.conf`, then `git commit`, then `git push`
You do NOT add the repos directly anywhere on the server; you do it by
cloning, adding repo and access lines, and pushing.
## #flow gitolite flow
This is the overall flow of gitolite, showing how the various parts fit
together. This is particularly useful for an admin to see where his
site-local hooks fit, how they get called, and what stages of the process they
affect.
**Legend**: diamonds are decision boxes whose results can abort the operation.
Arrows are calls/invocations. Dashed lines just point to sub-parts of the
process on the left side. Blue processes are external to gitolite. Green is
gitolite code. Yellow is site-local code that you (the admin of your site)
can add, to influence gitolite's behaviour.
Authentication (**AUTHN**) is typically done by sshd, but could also be httpd
etc. This step invokes `gl-auth-command` (**GLAC**), which is the main entry
point for gitolite, passing it a username and a command. The most common
commands look like one of these:
git-upload-pack 'repo'
git-receive-pack 'repo'
GLAC first checks the command to see if it is a read or a write (**RW**)
operation (upload-pack is a read, receive-pack is a write).
At this point, GLAC knows the username, the reponame, and the type of
operation. It executes the first [level][2levels] access check (**AC1**),
which passes if the user has at least one ref for which the operation is
allowed.
<font color="gray">Note that neither "deny" rules nor the rule sequences are
taken into account for this step. For instance, user "alice" will pass this
step even with this configuration:
repo foo
- = alice
RW = alice
although some other user, say "bob", will not.</font>
.gv
"Gitolite Flow"
rankdir=LR
edge[dir=forward]
[b] AUTHN
[g] GLAC
[g] RW
<> [g] AC1
<> [y] PGH
[b] Git
[g] UP
<> [g] AC2
AUTHN .. GLAC
GLAC -- RW
GLAC -- AC1
GLAC .. PGH
GLAC .. Git .. UP
UP -- AC2
<> [y] SEC
UP .. SEC
Once AC1 has passed, GLAC calls the [`gl-pre-git`][pre-git] hook (**PGH**), if
it is present. This hook allows the admin to add his own checks at this first
stage and even abort the operation if needed.
If PGH passes, GLAC now invokes git itself, which runs the command specified.
If the command is `git-upload-pack`, nothing else happens and the process
completes.
However, if it is a push operation (`git-receive-pack`) then git calls the
update hook (**UP**), per `man githooks`. The update hook executes the second
[level][2levels] access check (**AC2**), and depending on its success, the
operation succeeds or fails.
The update hook calls a secondary hook, [`update.secondary`][hookchaining]
(**SEC**) if it exists. Similar to PGH, this allows the admin to add
site-local checks before allowing the update to complete.

325
doc/CHANGELOG Normal file
View file

@ -0,0 +1,325 @@
Major changes to gitolite, master branch only, most recent first, no dates but
the tags can help you position stuff approximately
[NYD = not yet documented due to lack of time...]
- v2.3.1
- fix bug where config statements would get ignored if you had a description
set. The biggest problem was that this would affect mirroring.
- v2.3
- added "M" permission to allow enforcing a "no-merges" policy.
- gl-setup is a little more puppet-friendly. To be specific, you can now
manage gitolite without having gitolite-admin as a repo, such as can
happen with puppet and such. The commit message for d08aca6 has details
and a script that may help you if you're doing this.
- v2.2.1 (caused by minor bug re not handling blank lines in authkeys file)
- ADCs gained new functions can_{read,write,create}, is_admin, and in_group.
- v2.2, gpg signed :-)
- major change to the online rendered docs to make them more readable
- (lots of nice documentation additions: gitolite in pictures, "user"
manual, gitolite.conf by example, ...)
- (minor backward compat breakage) if you're using svn or rsync support.
You can still use them but they work differently now.
- git-annex support via unrestricted ADC (i.e., whose arguments are NOT
checked by gitolite)
- eliminated almost all hardcoding in test suite. This helped create a
"playing with gitolite" mode for "try before install" scenarios.
- deny rules for the first level access check!
- v2.1
- 'symbolic-ref' ADC replaces and obsoletes 'set-head'
- BIG CHANGE: easy-install ('from-client' method) is now HISTORY from the
docs and code. Upgrade to one of the other methods. Look for a section
on "upgrading from from-client method to non-root method" in the docs
- nice new "password access" mode for people who can't grok ssh keys
- new program gl-dryrun, to be run in any admin repo clone, to check syntax
errors etc before you actually push
- very complex mirroring setups now have a helper script to setup
- behanw contributed a better converter for gitosis
- the mirroring stuff is just out of this world now...
- new 'subconf' feature to explicitly delegate; the implicit one is now
legacy, but is backward compatibly handled so you don't need to fix your
config
- delegation now allows includes in fragments -- no idea why we first
disallowed this
- fix accumulation and sequencing of git config lines; very important for
mirroring (coming up)
- v2.0.3
- gl-dont-panic replaced by more generic gl-admin-push
- v2.0.2
- self-service key management
- (from-client install method is now loosely deprecated)
- su-setperms and su-getperms, like su-expand -- allow an admin to get/set
perms on behalf of some user
- 'delete-branch' ADC -- this is the most practical way to allow someone to
delete a branch that they themselves created; see commit message for
89b68bf
- v2.0.1
- "hooklets" allow you to have any number of site-local 'update hook'
scripts play nicely with gitolite
- the "include" statement can now use globs -- makes some types of setups
sooooo easy!
- mirroring finally tested properly after v2
- 'hub' adc (pull requests etc)
- s3backup ADC contributed by Bremner
- warn() and die() are now logged
- v2.0
- gl-pre-git hook added
- 'hub' adc added
- v2.0rc2
*** V1.5.9.1 -- IMPORTANT SECURITY FIX; PLEASE UPGRADE IF YOU'RE USING
SOMETHING OLDER THAN THIS ONE
- supercool new 'git' adc; so cool it's disabled by default
- '-prune' added to find commands; makes a big diff if REPO_BASE is NFS/CIFS
mounted
- first python ADC contributed -- 'perms' makes setperms etc easier to do
- 'set-head' ADC added
- v2.0rc1; major refactor
- v1.5.9
- Nokia MeeGo team contributed ldap scripts
- large configs should now be twice as fast (except when gl-perms exists)
due to my finding and eliminating a wasted parse_acl
- major change: split the config file when in big-config mode
(includes a data format change)
- GL_ALL_READ_ALL to make things much, (MUCH!) faster for sites where all
can read all repos (like Fedora)
- rc file revamp -- was getting too big and unwieldy; now the documentation
is in a new file instead of inline
- allow gitolite to be used even when users have real IDs (thus $HOME is not
valid to find the rc file); allow /etc/gitolite/gitolite.rc then
- BIG one for adc writers -- full blown access checks (ref level) can be
done from an ADC now (though it has to be in perl, not shell)!
- allow full access checks from perl (shell can only do level 1 checks);
useful in hooks or ADCs
- v1.5.8
- Jeff from KDE contributed the watch ADC
- allow disabling gitolite (write access only) to take backups
- document how to move gitolite from one server to another
- custom permissions categories (default being READERS and WRITERS) for
setperms (thanks to Jeff from KDE for the idea/need)
- v1.5.7
- "help" adc can allow site local help text to be shown
- "who-pushed" adc to find out who pushed a given commit
- rmrepo adc replaced by lock/unlock/rm and trash/list-trash/restore
families of commands
- v1.5.6
- new method for passing usergroup info (minor backward compat breakage);
see commit message and doc/big-config.mkd for more on this
- added "sudo" (adc)
- added "gl-reflog" (adc) to get a fake reflog from the server!
- added support for a post-repo-create hook called "gl-post-init"
- (BIG ONE!) SMART HTTP SUPPORT!
- @all can now include gitweb/daemon also (default, not include)
- allow @groups in setperms
- gitweb/daemon now work within setperms
- log elapsed time (optional)
- more than one wildcard may match a repo, plus it can also be matched by a
normal repo line
- test suite has lots of new tests for the below
- (big change) all combinations of wild repos and big configs, including
daemon/gitweb/git-config settings, should work now!
- v1.5.5
- mirroring support
- setup_authkeys is now separate; can be called from outside also; useful
for people who want to maintain ssh keys via LDAP or something, and not
within gitolite
- (two months too late for towel day) gl-dont-panic!
[replaces the old "gl-emergency-addkey" program. It does more (including
recovering from a botched push, not just lost keys), is cleaner, and works
for all install methods]
- document on how to create a mob branch
- info command now takes a parameter to limit output; this is mandatory if
GL_BIG_CONFIG is on
- v1.5.4
- new RC variables: GL_NO_CREATE_REPOS and GL_NO_SETUP_AUTHKEYS (inspired by
the specific needs that Fedora have, but made as generic as possible)
- separating push branch rights from create branch rights changed to use the
same mechanism as the (older) mechanism for separating rewind from delete
- v1.5.3
- log file format changed; minor backward compat breakage if you've been
doing any automated log processing
- some small but important doc updates
- adc "fork" now much faster and more space-efficient (uses git clone -l)
- v1.5.2
- added test suite
- v1.5.1
- disallow creation of new refs if you want (while allowing push of the
same)
- adc "able" command added to contrib
- easy-install now takes a host-nickname parameter for convenience in
installing more than one gitolite server
- major doc revamp; contrib/autotoc added to make docs look nicer
- eliminate the need to run gl-setup on data version change, thus hopefully
obsoleting the upgrade note for v1.5 (just below).
- v1.5 -- IMPORTANT UPGRADE NOTES below
Upgrading to v1.5 from any version prior to v1.5 requires an extra step
for people who installed gitolite using the "system install / user setup"
method described in doc/0-INSTALL.mkd. For such installations, after the
administrator has upgraded gitolite system-wide, each "gitolite host" user
must run `gl-setup` once (this time without any arguments).
- "deny" rules should now work even in "big-config" due to previous change
- proper rule sequencing (required major format change)
- allow usergroup info to be passed in from outside, say via LDAP; see
doc/big-config.mkd for details
- (new) big-config is now part of mainline (old one had bitrotted); see
doc/big-config.mkd for details
- gl-system-install: help people simulate an RPM/DEB install by just running
that commmand with appropriate arguments; see doc/0-INSTALL.mkd
- admin-defined commands; see doc/admin-defined-commands.mkd
- v1.4.2 (prep for major refactor on rights queries
- v1.4.1 (security fix)
- REFUSE TO RUN ON SERVER GIT < 1.6.2 (do NOT upgrade gitolite to or beyond
this point if you are unable to upgrade git itself to at least 1.6.2)
- "D" must be combined with RW or RW+ (warning: minor backward compat breakage)
- v1.4
- recurse through keydir for pubkeys
- bypass update hook if GL_BYPASS_UPDATE_HOOK is available in ENV
- new server-side program "gl-tool", subcommand "shell-add"
- new "D" permission (makes RW+ no longer imply "D" if used)
- @all for repos is now a true @all
- allow setperms to specify @all
- post-update hook and gl-setup should be dash compat now
- workaround for a Data::Dumper crash; see 412a691
- both hooks chain to "<hookname>.secondary" now
- new style personal branches (see 2456cc1 for advantages)
- v1.3
- easier to move repos into gitolite
- pattern for expand is no longer anchored
- v1.2
- distro packaging support -- easy to install systemwide now
- v1.1
- contrib directory added
- expand now lists non-wildcard repos also
- refs also have groups now
- allow admins to get "info" for other users
- wildrepos merged
- getdesc and setdesc for wildrepos
- htpasswd subcommand
- access control for rsync
- v1.0
- sshkeys-lint program added, doc/6 revamped
- @SHELL in config changed to $SHELL_USERS in rc
- "include" mechanism
- delegation now uses NAME/ instead of branches
- PATH/ changed to NAME/
- @SHELL in config
- use of @all for repos also (see doc for caveat)
- config entries for repos
- deny rules (no more "rebel" branch!)
- PATH/
- specify gitweb owner
- v0.95
- easy install can run from msysgit also
- v0.90
- allow admin defined hooks
- specify gitweb desc
- v0.85
- emergency addkey program
- v0.80

View file

@ -1,12 +1,12 @@
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
@ -56,7 +56,7 @@ patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
@ -255,7 +255,7 @@ make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
NO WARRANTY
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
@ -276,3 +276,64 @@ TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) year name of author
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
`Gnomovision' (which makes passes at compilers) written by James Hacker.
<signature of Ty Coon>, 1 April 1989
Ty Coon, President of Vice
This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License.

View file

@ -0,0 +1,208 @@
# F=ADCs admin defined commands
## ADC background
The admin-defined commands (ADCs) feature allows controlled access to
specific, "safe", programs or scripts, without giving users full shell access.
**WARNING**: regardless of what you read below, the security of the code in
the commands or scripts you install as ADCs is **your responsibility**. The
sample ADCs shipped with gitolite (in `contrib/adc`) should be safe enough,
but an extra pair of eyes never hurt, so please review before use.
<font color="gray">Although this is a generic way to allow pretty much any
command to be run, most of the examples and sample ADCs pertain to allowing
users to manage their "own" repos. If that's your use case, please read
the [wildcard repositories][wild] doc before you continue here.</font>
## ADC details
### installing ADCs
ADCs can only be installed by someone with shell access to the server; merely
having push rights to the admin repo is not enough.
* edit `~/.gitolite.rc` and set `$GL_ADC_PATH` to a directory that is *not*
in `$PATH`.
* add your "safe" executables to this directory.
**Warning**: An ADC can hide (or override) gitolite's built-in commands like
'info', 'expand', 'setperms', or even 'git-receive-pack' or 'git-upload-pack'!
This is by design. So be careful what you name your scripts.
However, it is perfectly ok, and may even be necessary in some cases, to name
them after system executables (like 'rsync').
### user invocation
If you have a command called "foo" in that directory, then a user can invoke
it by saying:
ssh git@server foo argument list
### checking authorisation inside an ADC
Once an ADC is installed, *all* users can run it. But sometimes you want only
some people to be able to do so.
While you cannot prevent the ADC from running at all, you can *start* the ADC
with code that checks the user's access to *any* arbitrary repo. For example,
you can bail out if the user does not have "W" access to the "gitolite-admin"
repo, which is an easy way of making sure an ADC is only run by admins.
See the section on "the anatomy of a command" later for this and many more
details.
### checking arguments
Gitolite will call an ADC only if the arguments passed to it match a very
strict pattern (see `$ADC_CMD_ARGS_PATT` in `src/gitolite_rc.pm`). This
reduces the risk of various kinds of shell-meta related compromises.
### passing unchecked arguments
Some commands need arguments with a broader range of characters than
`$ADC_CMD_ARGS_PATT` will allow. As long as you are sure those commands are
doing their own argument checking and sanitisation, you can place such
commands in `$GL_ADC_PATH/ua` and they will be run with **no checks on the
arguments**.
The "ua" stand for "unchecked arguments". Consider this your last warning ;-)
## "fake" repos and access control for non-git programs
A "fake" repo is a repo that exists in the config file but is specially named
(starts with "EXTCMD/") so that gitolite will not create an actual repo on
disk for it. It serves as a place holder for different sets of rules.
If you install the 'rsync' ADC, you can use a fake repo called 'EXTCMD/rsync'
to collect a set of rules that specify what user is allowed to read/write what
files using the rsync command on his workstation. See `contrib/adc/rsync` for
more on this.
<font color="gray">*Any* non-git program can be similarly access controlled,
as long as the *command line* that the client attempts to execute on the
server has sufficient information to decide. Protocols where the command line
is just one word and everything else happens in the conversation later cannot
be helped by this mechanism.</font>
## anatomy of a command
You can do whatever you want in an ADC! It's upto you to check the
permissions of *each* repo that the user is manipulating using your ADC --
your code can `rm -rf $GL_REPO_BASE_ABS` if you like and gitolite wouldn't
stop you.
The current directory (`$PWD`) will be set to the `$HOME` of `git@server` (or
whatever id you're using). It won't be any specific repo, it won't even be
the base directory of all the repos.
Gitolite defines a few environment variables, as well as allows you to
directly query the ownership and access permissions of any repository.
The environment variables available are:
* `GL_USER` -- the name of the user invoking the command
* `GL_BINDIR` -- the directory containing all the binaries (in particular,
`gitolite.pm`, which is all we really care about here)
* `GL_REPO_BASE_ABS` -- the absolute path of the base directory containing
all the repos
There are a few other variables also available but the above are the only ones
you should rely on. Please treat any other variables you notice as being
internal/undocumented/subject to change.
[Implementation note: some of the distro packagers don't seem to like
`GL_BINDIR`. I have not tested this in those scenarios, but they probably put
`gitolite.pm` somewhere in perl's lib path anyway, so it ought to work].
You can query ownership and permissions for the current user (which may not
necessarily be the owner). This is done loosely as follows (don't use this
exact code yet though):
perl -I$GL_BINDIR -Mgitolite -e "cli_repo_rights('reponame')"
which will print two space-separated words: permissions and owner. Something
like `_____R__W u1` or maybe `____@R_@W <gitolite>`. (The `u1` indicates the
queried repo is a wildcard repo created by user `u1`; for meanings of the "@"
see doc/report-output.mkd)
But that's cumbersome. It's much nicer to use the convenient functions
defined in `contrib/adc/adc.common-functions`; see the comments in that file
for details, and any of the other samples for how to use them.
If you prefer perl, there is a nicely commented example in
`contrib/adc/get-rights-and-owner.in-perl`.
## example uses and sample commands in `contrib/adc`
### #fork the 'fork' ADC
A user would use the fork command like this:
ssh git@server fork from to
where "from" is a repo to which the user invoking the fork has "R" access, and
"to" is a repo that does not yet exist and to which he has "C" access.
(Reminder: these access checks are done by the "fork" script, **not** within
gitolite -- once again, you are responsible for making sure your scripts
maintain the security of the system!)
Strictly speaking this command is not really needed. Even without all this
"admin-defined commands" setup you could still do the following, purely from
the client side:
git clone git@server:from
cd from
git remote add new git@server:to
git push new refs/*:refs/*
or some such incantation.
### deleting/trashing repos
See the [repo-deletion document][wild_repodel] for details about this.
### #able enable/disable push access temporarily
If you want to disable push access to gitolite temporarily (maybe for
maintenance), anyone with write access to the gitolite-admin repo can do this:
ssh git@server able dis @all # able dis ==> dis able
To re-enable after the maint work is done:
ssh git@server able en @all # able en ==> en able
You can also do this for one or more individual repos; in place of `@all`,
just use a space separated list of reponames (exactly as they would appear in
the config file). Wildcards are not supported; patches welcome ;-)
Note: please see [this][disable] for more on this.
## how the ADC feature came about
<font color="gray">
Gitolite was named to be short for "gitosis-lite". Someone now wants to turn
it into a "github-lite" :-) and even had some code to start me off thinking.
Since my first impulse on being asked for a feature is to say no, I was
casting about for a reason when he gave me one: he first made some noises
about perl, then said something about rewriting it all in scheme. Nice... I
resisted the urge to point him to [this][xkcd224], told him that's a great
idea and he should go for it, mentally blessing him for letting me off the
hook on coding it ;-) [Laziness][lazy] *is* the first virtue you know!
[xkcd224]: http://xkcd.com/224/
[lazy]: http://c2.com/cgi/wiki?LazinessImpatienceHubris
And that was that. For a couple of days.
Soon, though, I realised that there could be a pretty big bonus in this for
tightly controlled setups, so I went and coded it all anyway. See the section
on "restricted admin" for what's really exciting about this for *me*.
</font>

330
doc/admin.mkd Normal file
View file

@ -0,0 +1,330 @@
# F=admin administering and running gitolite
## please read this first
Unless you know what you're doing, do not do **anything** manually on the
server (except when the documentation says you should, for example to add
custom hooks). In particular, adding new repositories or users or changing
the access control rules should not be done directly on the server. Things
will break. For example, if you manually create a repo on the server, it will
not have the required "update" hook, without which there is no access control
for pushes.
Most normal (day-to-day) gitolite admin work is done by cloning the
gitolite-admin repo from the server to your workstation, making changes to the
clone, and pushing those changes back.
The installation steps in the previous section include the steps to do this
clone, so you should already have one on your workstation, in
`~/gitolite-admin`. You can of course clone it anywhere else you want and use
that clone.
Either way, make sure you `cd` into this clone first.
*Note*: some of the paths in this document use variable names. Just refer to
`~/.gitolite.rc` for the correct values for *your* installation.
Once you've cloned it, you're ready to add users and repos.
## F=add adding users and repos
Do **NOT** add repos or users directly on the server! You MUST manage the
server by cloning the special 'gitolite-admin' repo on your workstation (`git
clone git@server:gitolite-admin`), making changes, and pushing them. This
section tells you how to add users and repos.
* ask each user who will get access to send you a public key. (Generating a
keypair is described somewhere in [this][gl_ssh] page).
* rename each public key according to the user's name, with a `.pub`
extension, like `sitaram.pub` or `john-smith.pub`. You can also use
periods and underscores
* copy all these `*.pub` files to `keydir` in your gitolite-admin repo
clone. You can also organise them into various subdirectories of `keydir`
if you wish, since the entire tree is searched.
* edit the config file (`conf/gitolite.conf` in your admin repo clone). See
the [gitolite.conf][conf] documentation for details on what goes in that
file, syntax, etc. Just add new repos as needed, and add new users and
give them permissions as required. The users names should be exactly the
same as their keyfile names, but without the `.pub` extension
* when done, commit your changes and push. Any new repos you specified will
automatically be created (empty, but clonable) and users' access will be
updated as needed.
[genpub]: http://sitaramc.github.com/0-installing/2-access-gitolite.html#generating_a_public_key
## F=hooks using hooks
### #customhooks custom hooks
You can supply your own, custom, hook scripts if you wish. Install gitolite
as usual, then:
* go to ~/.gitolite/hooks/common on the server and put your new hook there
* now run "gl-setup" again
You can use this procedure to install new hooks as well as to update hooks
that you had previously installed.
<font color="red">**IMPORTANT WARNINGS**</font>
* The `update` hook in `hooks/common` is what implements all the
branch-level permissions in gitolite. If you fiddle with the hooks
directory, please make sure you do not mess with this file accidentally,
or all your fancy per-branch permissions will stop working.
* Do not under any conditions put anything in `hooks/gitolite-admin` --
nothing in gitolite requires you to do anything here. Leave it alone!
### #hookchaining hook chaining
Sometimes you need to use git hooks for your own purposes (site-local
validations, CI integration, email notifications, or the ever popular "live
website update"!). However, the hooks you want to use may already be in use
by gitolite.
This section will tell you what to do in such cases. First, let's list the
hooks that gitolite uses:
* The `update` hook is used in all repos and is critical to gitolite's
access control!
* The `post-receive` hook is used in all repos but only if mirroring has
been enabled. Shipped as `post-receive.mirrorpush`, it is renamed to
'post-receive' and installed as part of the mirroring setup.
* The `post-update` hook is used in the `gitolite-admin` repo only, to
"compile" the configuration and so on.
To run your own 'update' hook, just put it in a file called `update.secondary`
and install it as a hook. Gitolite's update hook will automatically chain to
it, taking care to pass it the same 3 arguments the original update hook
received from git.
<font color="gray">
> Also see the document on [virtual refs][vref] for a way to add additional
> checks that you might need.
</font>
For `post-receive`, (if using mirroring) do the opposite. You're normally
expected to rename the shipped 'post-receive.mirrorpush' to 'post-receive',
but don't do this. Instead, simply run `hooks/post-receive.mirrorpush` at the
end of *your* hook code. Do not worry about replicating STDIN (the documented
way in which a post-receive hook receives its input) because the mirroring
code does not use it.
To run your own `post-update` hook on normal repos, just install a hook called
'post-update' the usual way. It'll be installed on all normal repos but not
on the special gitolite-admin repo. If you need that for the gitolite-admin
repo, you'll have to call it `post-update.secondary`.
Finally, these names ('update.secondary' and 'post-update.secondary') are
merely the defaults. You can change them to anything you want; look in
conf/example.gitolite.rc for details.
### environment variables available to hooks
The following environment variables are set, and may be useful for any custom
processing you wish to do in your hook code:
* `GL_USER` -- the user doing the push
* `GL_REPO` -- the reponame
* `GL_REPO_BASE_ABS` -- the absolute base path where all the repos are kept
The following variables are also set, but are generally less useful:
* `GL_BINDIR` -- where all the binaries live
* `GL_ADMINDIR` -- common directory for many gitolite things
### "gl-post-init" hook
Sometimes it is necessary to do something whenever a new repo is created. If
you need this functionality, just supply a hook called "gl-post-init" with
whatever code you want in it.
### #pre-git "gl-pre-git" hook
Although git has lots of nice hooks you can tap into, they all run only on a
push. There's nothing that runs on a fetch or a clone, and there's no way to
run something *before* git-receive-pack or git-upload-pack, (as the case may
be) are invoked.
That's what the `gl-pre-git` hook is for. If an executable hook called
`gl-pre-git` is present, it will be invoked with the current directory set to
`repo.git`, and with a single argument which will be either `R` or `W`
depending on what the client is trying to do. The environment variables
`GL_USER` and `GL_REPO` are available. STDOUT will be forced to STDERR before
it is called, to avoid confusing the client.
If the code returns anything other than 0, gitolite will terminate the
operation (i.e., not run git at all), just like many git hooks do, so make
sure you end with `exit 0` or equivalent.
## other features
### F=moverepos moving pre-existing repos into gitolite
It's best to split this into different use cases.
**Case 1 -- few repos**: This is for moving one or two repos at a time, when
you have a copy of the repo on your workstation. It is also the *only* way if
you have push rights to the admin repo but no *shell* privileges on the
server.
* let gitolite create it as a brand new repo as described in the section on
"adding users and repos" at the top
* cd to the clone on your workstation. Make sure all the branches are
correct and no extra stuff, "temp" branches, etc., are present
* now run these two commands
git push --all git@server:reponame
git push --tags git@server:reponame
* (You could also use "git push --mirror" instead of separately doing
branches and tags, but that will carry across *your* remote refs also, and
typically you may not want that. Anyway please do a `git ls-remote
git@server:repo` to make sure all the stuff you want went through, and is
named correctly).
**Case 2 -- many repos**: This is when you have many existing repos to add,
and they're all bare (as good little server repos should be) and you have
shell access on the server. Here's how to do it; please note the order is
important here:
* make doubly sure they're *bare* repos ;-)
* log on to the server and copy the repos to `$REPO_BASE` (which defaults to
`~/repositories`), making sure that the directory names end in ".git".
* back on your workstation, add each repo (without the `.git` suffix) to
`conf/gitolite.conf` in your gitolite-admin repo clone. Give *some* user
(even a non-existent one like "DUMMY" is fine) at least "R" access to
these repos. Then add, commit, push.
**Case 3 -- far too many repos** (or your initials are JH ;-): This is when
you're like Case 2, except you have *so many* repos that step 3 becomes too
cumbersome (even with a script doing it for you).
Assuming you can group your repo names into various patterns, and can use
similar access control lines within each such group, you can use gitolite's
"wildcard repos" feature.
First read the [wildcard repositories][wild] document, or at least skim
through it, to understand the basic concept. Then do this:
* do step 1 just like step 1 in Case 2 above
* ditto for step 2
* for each repo, determine who the owner should be and create files called
`gl-creater` (note spelling!) in each repo. The file should contain
exactly one line with the owner name.
* run `gl-setup` again (you don't need to supply a pub key filename)
* finally add the repos to the conf, maybe something like this, (in this
example, the owner name was the second component of the repo path), and
add/commit/push:
repo pub/CREATOR/..*
C = @developers
RW+ = CREATOR
RW = WRITERS
R = READERS
**Details**
<font color="gray">
* why is the order of steps different in case 1 and case 2?
Because in case 2, the actual data is coming from an OS 'cp' (copy)
command, not via a normal push like in case 1. Since that happens outside
gitolite, it's easier to do it first, then tell gitolite about the repo so
it can add hooks. (If you tell gitolite first, it will create an empty
repo as soon as you push, then your 'cp' will have to overwrite those
files, but you'll then lose gitolite's hooks, etc. A bit more messy).
* what's with the `gl-creater` file in case 3?
What the [wildcard repositories][wild] document does not explain is how
ownership is *recorded* in gitolite: the `gl-creater` file contains the
owner name. If you want to "pretend" these repos were created by some
user, you need to add that in. That user then gets whatever access you
gave to "CREATOR" in the access rules (in our example, that was `RW+`).
* why does case 3 need the `gl-setup` command?
An admin push only checks hooks on normal (non-wildcard) repos. It would
be too timetaking otherwise. Running `gl-setup` forces it to do this more
aggressively than an admin push, looking at wildcard repos as well as
normal ones.
</font>
In the end, it all boils down to (a) making sure the `update` hook is correct
on all repos, wild or normal, and (b) making sure `gl-creater` contains the
owner name for wild repos. The rest of the setup is in the conf file.
### F=moveserver moving the whole thing from one server to another
[**NOTE**: I would appreciate help testing these instructions]
Here's the simplest set of instructions, assuming the destination is a recent
gitolite (has the 'gl-admin-push' command). Unless specified, all steps are
on the *new* server.
* **install** gitolite. Don't worry about the pubkey used in the gl-setup
step -- for example this will do fine:
ssh-keygen -q -N '' -f dummy
gl-setup -q dummy.pub
* **edit** the rc file to have similar settings to the old one.
Do not copy the entire file outright -- some of the variables (notably
`GL_PACKAGE_CONF` and `GL_PACKAGE_HOOKS`) are installation dependent and
should not be touched! Do a diff or a vimdiff and copy across only what
you know *you* changed on the old server.
* **disable** the old server so your users will not push any changes to it.
There are several ways to do this, but the simplest is to insert this line
at the top of `~/.gitolite.rc` on the old server:
exit 1;
* **copy** the contents of `$REPO_BASE` in the old server to `$REPO_BASE` on
the new server. By default, as you know, these are both
`$HOME/repositories`.
* **`chown -R`** the files to the correct user if you copied using root.
* **fix up** the hooks
gl-setup
* **trigger** a push to the admin repo
git clone repositories/gitolite-admin.git /tmp/gitolite-admin
cd /tmp/gitolite-admin
git commit --allow-empty -m 'trigger compile on new server'
gl-admin-push -f
Done.
### custom git config
The custom hooks feature is a blunt instrument -- all repos get the hook you
specified and will run it. You can of course install hooks manually on the
server, but sometimes that's cumbersome.
Instead, you could set your hooks to only work if a certain "gitconfig"
variable was set. See [this][rsgc] for a way to specify "git config"
settings on a per repository basis.

89
doc/auth.mkd Normal file
View file

@ -0,0 +1,89 @@
# F=auth authentication versus authorisation
This document will explain why an "ssh issue" is almost never a "gitolite
issue", and, indirectly, why I dont get too excited about the former.
Note: for actual ssh troubleshooting see [this][sts].
Here is a fundamental point: <font color="red">**Gitolite does not do
authentication. It only does authorisation**.</font>
So first, let's loosely define these words:
> **Authentication** is the process of verifying that you are who you claim
> to be. An authentication system will establish that I am the user
> "sitaram" on my work system. The one behind gmail will similarly
> establish that I am "sitaramc". And so on...
> **Authorisation** is the process of asking what you want to do and
> deciding if you're allowed to do it or not.
Now, if you managed to read about [gitolite and ssh][gl_ssh], you know that
gitolite is meant to be invoked as:
/full/path/to/gl-auth-command some-authenticated-gitolite-username
(where the "gitolite username" is a "virtual" username; it does not have to
be, and usually *isn't*, an actual *unix* username).
As you can see, authentication happens before gitolite is called.
## but... but... you have all that ssh stuff in there!
The default mode of using gitolite does use ssh keys, but all it's doing is
helping you **setup** ssh-based authentication **as a convenience to you**.
You don't have to use it, though. And many people don't. The examples I know
are [smart http][http], and ldap-backed sshd. In both cases, gitolite has no
role to play in creating users, setting up their passwords/keys, etc. There's
even a `GL_NO_SETUP_AUTHKEYS` option to make sure gitolite doesn't meddle with
the authkeys file in such installations.
## so you're basically saying you won't support "X"
(where "X" is some ssh related behaviour change or feature)
Well, if it's not a security issue I *probably* won't. I'm willing to change
my mind if enough people convince me they need it. (There's a mailing list if
you want to find others who also need the same thing.)
While we're on the subject, locking someone out is *not* a security issue.
Even if you locked yourself (the admin) out, the docs tell you how to recover
from such errors. You do need some password based method to get a shell
command line on the server, of course.
## appendix: how to use other authentication systems with gitolite
The bottom line in terms of how to invoke gitolite has been described above,
and as long as you manage to do that gitolite won't even know how the
authentication was done. Which in turn means you can use whatever
authentication scheme you want.
It also expects the `SSH_ORIGINAL_COMMAND` environment variable to contain the
full command (typically starting with git-receive-pack or git-upload-pack)
that the client sent. Also, when using [smart http][http], things are somewhat
different: gitolite uses certain environment variables that it expects httpd
to have set up. Even the user name comes from the `REMOTE_USER` environment
variable instead of as a command line argument in this case.
However, it has to be an authentication system that is compatible with sshd or
httpd in some form. Why? Because the git *client* accessing the server only
knows those 2 protocols to "speak git". (Well, the `git://` protocol is
unauthenticated, and `file://` doesn't really apply to this discussion, so
we're ignoring those).
For example, let's say you have an LDAP-based authentication system somewhere.
It is possible to make apache use that to authenticate users, so when a user
accesses a git url using `http://sitaram:password@git.example.com/repo`, it is
LDAP that does the actual authentication. [I wouldn't know how to do it but I
know it is possible. Patches to this doc explaining how are welcome!]
There are also ssh daemons that use LDAP to store the authorised keys (instead
of putting them all in `~/.ssh/authorized_keys`). The clients will still need
to generate keypairs and send them to the admin, but they can be more
centrally stored and perhaps used by other programs or tools simultaneously,
which can be useful.
Finally, gitolite allows you to store *group* information externally too. See
[here][ldap] for more on this.

399
doc/big-config.mkd Normal file
View file

@ -0,0 +1,399 @@
# F=bc what is a "big-config"
This document is just background info; you don't actually need to read the
whole thing if you don't care. All you need to do is set `BIG_CONFIG` to 1 in
the rc file and you're done. If you have no use for gitweb, git-daemon, or
[git config][rsgc], you can save even more time by setting
`GL_NO_DAEMON_NO_GITWEB`.
Finally, if you're *really* an expert (or your initials are "JK"), you can
even set `GL_NO_CREATE_REPOS` and `GL_NO_SETUP_AUTHKEYS`. However, be warned
that if you're not sufficiently clueful, those last 2 variables could have a
[security impact][rcsecurity].
## when/why do we need it?
A "big config" is anything that has a few thousand users and a few thousand
repos, resulting in a very large 'compiled' config file.
### the problem
To understand the problem, consider what happens if you have something like
this in your gitolite conf file:
@wbr = lynx firefox
@devs = alice bob
repo @wbr
RW+ next = @devs
RW master = @devs
Without the 'big config' setting, gitolite internally translates this to:
repo lynx firefox
RW+ next = alice bob
RW master = alice bob
and then generates the actual config rules once for each user-repo-ref
combination (there are 8 combinations above); the compiled config file looks
somewhat like [this][bigno_].
Of course, the output is the same whether you used groups (like `@wbr` and
`@devs` in the example above) or listed the repos directly on the 'repo'
lines.
Anyway, you can imagine what that does when you have 10,000 users and 10,000
repos. Let's just say it's not pretty :)
## how do we use it?
Just set
$GL_BIG_CONFIG = 1;
in the `~/.gitolite.rc` file on the server (see next section for more
variables). When you do that, and push this configuration, one of two things
happens.
### access rules for groups
If you used group names in the 'repo' lines (as in `repo @wbr`), then the
compiled config looks like [this][bigyes_].
That's a lot smaller, and allows orders of magintude more repos and groups to
be supported.
### access rules for individual repos (split config)
If, on the other hand, you had the repos listed individually, (as in `repo
lynx firefox`), then the main config file would now look like this:
%repos = ();
%split_conf = (
'firefox' => 1,
'lynx' => 1
);
And each individual repo's configuration would go its own directory. For
instance, `~/repositories/lynx.git/gl-conf` would look like this:
%one_repo = (
'lynx' => {
'R' => {
'alice' => 1,
'bob' => 1
},
'W' => {
'alice' => 1,
'bob' => 1
},
'alice' => [
[
0,
'refs/heads/next',
'RW+'
],
[
4,
'refs/heads/master',
'RW'
]
],
'bob' => [
[
1,
'refs/heads/next',
'RW+'
],
[
5,
'refs/heads/master',
'RW'
]
]
}
);
That does not reduce the overall size of the repo config (because you did not
group the repos), but the main repo config is now even smaller!
## what are the downsides?
There are some downsides.
The following apply if individual ("split") conf files are written, which in
turn only happens if you used repo names instead of group names on the `repo`
lines:
* the compile (gitolite-admin push) is now slower, because it potentially
has to write a few thousand small files instead of one large one. Since
the compile should be relatively infrequent compared to developer access,
this is ok -- the main config file is parsed much faster now, so every hit
to the server will benefit.
* we can no longer distinguish 'repo not found on disk' from 'you dont have
access'. They both now look like 'you dont have access'.
## other optimisations
### disabling various defaults
The default RC file contains the following lines (we've already discussed the
first one):
$GL_BIG_CONFIG = 0;
$GL_NO_DAEMON_NO_GITWEB = 0;
$GL_NO_CREATE_REPOS = 0;
$GL_NO_SETUP_AUTHKEYS = 0;
`GL_NO_DAEMON_NO_GITWEB` is a very useful optimisation that you *must* enable
if you *do* have a large number of repositories, and do *not* use gitolite's
support for gitweb or git-daemon access (see "[this][gwd]" for details). This
will save a lot of time when you push the gitolite-admin repo with changes.
This variable also controls whether "git config" lines (such as `config
hooks.emailprefix = "[gitolite]"`) will be processed or not.
You should be a lot more careful with `GL_NO_CREATE_REPOS` and
`GL_NO_SETUP_AUTHKEYS`. These are meant for installations where some backend
system already exists that does all the actual repo creation, (including
setting up the proper hooks -- very important for access control), and all the
authentication setup (ssh auth keys), respectively.
Summary: Please **leave those two variables alone** unless you're initials are
"JK" ;-)
### #authkeyopt optimising the authkeys file
Sshd does a linear scan of the `~/.ssh/authorized_keys` file when an incoming
connection shows up. This means that keys found near the top get served
faster than keys near the bottom. On my laptop, it takes about 2500 keys
before I notice the delay; on a typical server it could be double that, so
don't worry about all this unless your user-count is in that range.
One way to deal with 5000+ keys is to use customised, database-backed ssh
daemons, but many people are uncomfortable with taking non-standard versions
of such a critical piece of the security infrastructure. In addition, most
distributions do not make it painless to use them.
So what do you do?
The following trick uses the Pareto principle (a.k.a the "80-20 rule")
to get an immediate boost in response for the most frequent or prolific
developers. It can allow you to ignore the problem until the next big
increase in your user counts!
Here's how:
* create subdirectories of keydir/ called 0, 1, (maybe 2, 3, etc., also),
and 9.
* in 0/, put in the pubkeys of the most frequent users
* in 1/, add the next most important set of users, and so on for 2, 3, etc.
* finally, put all the rest in 9/
Make sure "9" contains at least 70-90% of the total number of pubkeys,
otherwise this doesn't really help.
You can easily determine who your top users are by runnning something like
this (note the clever date command that always gets you last months log file!)
cat .gitolite/logs/gitolite-`date +%Y-%m -d -30days`.log |
cut -f2 | sort | uniq -c | sort -n -r
## F=ldap storing usergroup information outside gitolite (like in LDAP)
[Please NOTE: this is all about *user* groups, not *repo* groups]
[WARNING: the earlier method of doing this has been discontinued; please see
the commit message for details]
Gitolite now allows usergroup information to be stored outside its own config
file. We'll see "why" first, then the "how".
### #ldapwhy_ why
Large sites often have LDAP servers that already contain user and group
information, including group membership details. Such sites may prefer that
gitolite just pick up that info instead of having to redundantly put it in
gitolite's config file.
Consider this example config for one repo:
repo foo
RW+ = @lead_devs
RW = @devs
R = @interns
Normally, you would also need to specify:
@lead_devs = dilbert alice
@devs = wally
@interns = ashok
However, if the corporate LDAP server already tags these people correctly, and
if there is some way of getting that information out **at run time**, that
would be cool.
### #ldaphow_ how
All you need is a script that, given a username, queries your LDAP or similar
server, and returns a space-separated list of all the groups she is a member
of. If an invalid user name is sent in, or the user is valid but is not part
of any groups, it should print nothing.
This script will probably be specific to your site. (See contrib/ldap for some
example scripts that were contributed by the Nokia MeeGo team.)
Then set the `$GL_GET_MEMBERSHIPS_PGM` variable in the rc file to the full
path of this program, set `$GL_BIG_CONFIG` to 1, and that will be that.
## implementation notes
To understand how big-config works (at least when you're using grouped repos),
we'll first look at how it works without this setting. Think back to the
example at the top, and assume 'alice' is accessing the 'lynx' repo. The
various rights are governed by the following hash elements:
# for the first level checks
$repos{'lynx'}{'R'}{'alice'} = 1
$repos{'lynx'}{'W'}{'alice'} = 1
# for the second level checks
$repos{'lynx'}{'alice'}{'refs/heads/master'} = 'RW';
$repos{'lynx'}{'alice'}{'refs/heads/next'} = 'RW+';
Those elements are explicitly specified in the compiled hash, as you can see
(you don't need to know perl too much to read a hash; just make some educated
guesses if needed!)
Now look at the compiled hash produced when `GL_BIG_CONFIG` is set. In place
of both 'firefox' and 'lynx' you have '@wbr', and similarly '@devs' for both
'alice' and 'bob'. In addition, there is a group hash at the bottom that
lists each group and its members.
When 'alice' tries to access the 'lynx' repo, gitolite collects all the group
names that these names belong to, so '@devs' is added to the list of 'user'
names that 'alice' inherits permissions from, and '@wbr' is added to the list
of 'repo' names that 'lynx' inherits from. This means that the final access
inherits all permissions pertaining to the following combinations:
alice, lynx
alice, @wbr
@devs, lynx
@devs, @wbr
(Actually there are 3 more... try and guess what they may be!)
Anyway, all ACL rules for these combinations are clubbed together to make the
composite set of rules that 'alice' accessing 'lynx' is subject to.
## config listings
### F=bigno_ compiled config with big-config disabled
%repos = (
'firefox' => {
'R' => {
'alice' => 1,
'bob' => 1
},
'W' => {
'alice' => 1,
'bob' => 1
},
'alice' => [
[
0,
'refs/heads/next',
'RW+'
],
[
4,
'refs/heads/master',
'RW'
]
],
'bob' => [
[
1,
'refs/heads/next',
'RW+'
],
[
5,
'refs/heads/master',
'RW'
]
]
},
'lynx' => {
'R' => {
'alice' => 1,
'bob' => 1
},
'W' => {
'alice' => 1,
'bob' => 1
},
'alice' => [
[
2,
'refs/heads/next',
'RW+'
],
[
6,
'refs/heads/master',
'RW'
]
],
'bob' => [
[
3,
'refs/heads/next',
'RW+'
],
[
7,
'refs/heads/master',
'RW'
]
]
}
);
Phew!
### F=bigyes_ compiled config with big-config enabled
%repos = (
'@wbr' => {
'@devs' => [
[
0,
'refs/heads/next',
'RW+'
],
[
1,
'refs/heads/master',
'RW'
]
],
'R' => {
'@devs' => 1
},
'W' => {
'@devs' => 1
}
}
);
%groups = (
'@devs' => {
'alice' => 'master',
'bob' => 'master'
},
'@wbr' => {
'firefox' => 'master',
'lynx' => 'master'
}
);

139
doc/delegation.mkd Normal file
View file

@ -0,0 +1,139 @@
# F=deleg delegating access control responsibilities
----
## lots of repos, lots of users
Gitolite tries to make it easy to manage access to lots of users and repos,
exploiting commonalities wherever possible. It lets you specify bits and
pieces of the access control separately -- i.e., *all* the access specs for a
certain repo need not be together; they can be scattered, which makes it
easier to manage the sort of slice and dice needed in that example.
But eventually the config file will become too big. If you let only one
person have control, he could become a bottleneck. If you give it to multiple
people, they might make mistakes or stomp on each others' work accidentally.
The best way is to divide up the config file and give parts of it to different
people.
Ideally, we would delegate authority for *groups* of repos, not individual
repos, otherwise it doesn't scale. It would also be nice if we could prevent
an admin from creating access rules for *any* repo in the system -- i.e., set
limits on what repos he can control. This would be a nice "security" feature.
Delegation offers a way to do all that. You can allow access control rules
for a set of repos to be specified in a **subconf** file and allow someone (a
**sub-admin**) to make changes within that file. (Note: sub-admins cannot
create or remove users).
## how to use delegation
First, you group your repos however you want. In the example below, I'm
considering firefox and lynx (projects at the root of the gitolite server) as
well as *any* repo inside the `browsers` subdirectory, as members of the
`webbrowsers` group. Similarly for the others.
@webbrowsers = firefox lynx browsers/..*
@webservers = apache nginx servers/..*
@malwares = conficker storm ms/..*
# side note: if anyone objects, we claim ms stands for "metasploit" ;-)
Each of these groups is called a **subconf** from here on.
Then you designate a **sub-admin** to manage each subconf, and you ensure
(using [this][NAME] gitolite feature) that a sub-admin can make changes only
to her subconf file and nothing else.
For example, Alice is in charge of all web browser development projects.
Similarly, Bob takes care of web servers, and Mallory, as [tradition][abe]
dictates, is in charge of malware ;-)
[abe]: http://en.wikipedia.org/wiki/Alice_and_Bob#List_of_characters
# the admin repo access was probably like this to start with:
repo gitolite-admin
RW+ = sitaram
# now add these lines to the config for the admin repo
RW = alice bob mallory
RW+ NAME/ = sitaram
RW NAME/conf/subs/webbrowsers = alice
RW NAME/conf/subs/webservers = bob
RW NAME/conf/subs/malwares = mallory
Finally, you tell gitolite to pull in these files using the "subconf" command
subconf "subs/*.conf"
You can put this command anywhere in the main gitolite.conf file, but it's
best to put it at the end.
Now alice can clone the admin repo, add a file called `conf/subs/webbrowsers`
with whatever access rules she wants for the repositories under her control,
commit and push.
And that's really all there is to it.
### #subconf the subconf command
This command is much like the "include" command, but in addition it checks
that a subconf does not contain ACL rules for repos that are outside its
purview.
In the above example, the `webbrowsers` subconf file can only have access
control lines for firefox, lynx, and anything under "browsers/" because those
are the elements of the `@webbrowsers` group. (This is checked using a regex
match, which is why "anything under browsers/" is written `browsers/..*`)
In more precise terms:
* the subconf name is simply the basename of the conf file, without the
.conf extension, and
* the elements of an `@` group of the same name are then used to limit what
repos the subconf can have ACL lines for.
(Additional notes: it can also contain lines for an actual repo called
`webbrowsers`, or, in big-config mode, for a group called `@webbrowsers`).
### backward compatibility
For backward compatibility, if no `subconf` commands have been seen at the end
of processing the main config file, gitolite pretends you appended
subconf "conf/fragments/*.conf"
to the end of the file.
## security notes
### group names
You can use "@group"s defined in the main config file but do not attempt to
redefine or extend them in your own subconf file. If you must extend a group
(say `@foo`) defined in the main config file, do this:
@myfoo = @foo
# now do whatever you want with @myfoo
Group names you define in your subconf will not clash even if the exact same
name is used in another subconf file, so you need not worry about that.
### delegating pubkeys
Short answer: not gonna happen.
The delegation feature is meant only for access control rules, not pubkeys.
Adding/removing pubkeys is a much more significant event than changing branch
level permissions for people already on staff, and only the main admin should
be allowed to do it.
Gitolite's "userids" all live in the same namespace. This is unlikely to
change, so please don't ask -- it gets real complicated to do otherwise.
Allowing sub-admins to add users means username collisions, which also means
security problems (admin-A creates a pubkey for Admin-B, thus gaining access
to all of Admin-B's stuff).
If you feel the need to delegate even that, please just go the whole hog and
give them separate gitolite instances! It's pretty easy to setup the
*software* itself system-wide, so that many users can use it; see the root
install method in the install document.

151
doc/developer-notes.mkd Normal file
View file

@ -0,0 +1,151 @@
# F=dev_notes developer/patch maintainer notes
## general stuff
* all scripts and libraries must be in the same directory. However, RPM/DEB
packagers can put the libraries where they want, as long as they can be
found in perl's default `@INC`.
* gl-auth-command **requires** an actual `~/.gitolite.rc` (except if your
initials are "JK" or "DG", in which case `/etc/gitolite/gitolite.rc` also
works!) It knows how to look around and set env vars etc correctly
* all programs except gl-auth-command **require** the environment variables
`GL_RC` and `GL_BINDIR` set properly. Your best bet is to run them *via*
gl-auth-command, like so:
path/to/gl-auth-command -e other_program other_program_arguments
In any case none of these programs are meant to be run manually -- pretty
much all of them are run via gl-auth-command or from something that was
forked from it so the variables *will* exist during normal operation.
## the rc file
The 'rc' file has one major change from v1: any new values in the rc file need
to be added to the @EXPORT list in `src/gitolite_rc.pm`.
## modules
There are 3 "modules" (`gitolite_rc`, `gitolite_env`, and `gitolite` itself).
Their purposes should be fairly obvious.
## that 'bindir' thing
The importance of `GL_BINDIR` is that the command= argument in
`~/.ssh/authorized_keys` must be a full path, ideally, and the compile script
gets this from `GL_BINDIR`.
### from perl
* for frequently run perl programs, I prefer my method
* gl-auth-command -- this is invoked with a full path
* gl-mirror-shell -- same as above
* gl-time -- same as above
* "their" ideal is "FindBin". I will use it only on manually or
infrequently run programs
* gl-setup-authkeys (external shim to compile keys separately from PTA)
### from shell
* a perl program called gl-query-rc finds its own BINDIR (using my perl
method, not FindBin). This is suitable for calling from shell scripts
as `${0%/*}/gl-query-rc GL_BINDIR`
* gl-setup
* gl-tool
* gl-mirror-push
### OUTLIER!
* gl-admin-push is an outlier. For some silly reason I have the notion that
even if it runs from /tmp it should get the right values, so it is the
only one that interrogates `~/.ssh/authorized_keys` to get the actual
BINDIR in use!
## special types of setups
### Fedora
Fedora has a very special setup, as follows:
* each user has his own userid and login
* his/her ~/.ssh/authkeys file (containing only his/her key) has a
"command=" clause invoking gl-auth-command
* trusted users have "gl-auth-command -s" meaning they can get a shell if
they want to
* actual git repos are under "git" (or some such), and include the chmod g+s
(git init --shared) unix perms tricks for shared access. (<font
color="gray">Starting with git 1.7.something, you would also need to
explicitly delete the new receive.denyNonFastForwards setting that git
seems to default to when you use --shared</font>).
* but since they're coming through gl-auth, branch-level acls are in effect
* the gitolite config file is generated from some database and compiled (all
via cron)
* the keydir/ is empty; in fact they probably don't use the admin repo at
all, AFAIK
The most important implication of this setup is that **the RC file is no
longer is `$HOME` of the 'git' user**. They keep it in
`/etc/gitolite/gitolite.rc`. This means that a properly setup rc file must
already be present in `/etc/gitolite/gitolite.rc` before doing any such
installs.
There are also some other "impedance mismatches" that may show up. For
example, the gl-setup triggered by detecting a change in `$data_version`
following an RPM update once caused problems. This auto-update is designed to
run on the next "hit" of any kind (which arguably makes things very easy in
normal installations), but in Fedora's case it also means it runs *as that
user*. Who may not have "write" access to `$GL_ADMINDIR`! So the compile
fails, and you now have new code trying to work with old format data.
The solution is to explicitly run a compile, from a properly privileged
userid, as soon as you do an RPM upgrade.
# **Why v2?**
I went onto `#perl` to ask some question about setpriority() and got yelled at
for writing "horrible code". And that was one of the kinder comments; my
rather fragile ego is trying to forget the rest ;-)
They also gave me a link to a PDF book, "Modern Perl" by 'chromatic'. Nice
book; one of the first things you learn from it is that you should not go to
`#perl` for general help.
Anyway, the summary of the collective angst of `#perl` (well 2 people anyway)
was: use Getopt::Long, FindBin, 'use lib', a library for HTTP stuff, stop
prefixing subs with '&', and get rid of the huge number of 'our' declarations.
That last item is the only one I totally agree with, because it was on my long
term todo list anyway. And 'use lib' sorta goes with it, so that's fine too.
And as soon as I found that vim colors the sub names differently if you take
out the '&' I decided I'd do that too :-) [But honestly, if `&sub` is so bad
shouldn't "man perlsub" at least say something negative about it, other than
"disables prototype checking", which doesn't matter here since I'm not using
prototypes?]
As for the rest, FindBin brings in a good 1000+ lines for something that I do
in a line or two (since I don't care about all the pathological edge cases).
Getopt::Long is 2649 lines to replace the code below [note that there *is*
only one possible option to this command, and it is *never* run manually
either, so I don't need any fancy features]:
my $shell_allowed = 0;
if (@ARGV and $ARGV[0] eq '-s') {
$shell_allowed = 1;
shift;
}
Apparently TMTOWTDI has given way to TOOWTDI.
Anyway, I spent a few hours refactoring it. And I do thank them for pushing
me to stop being lazy on the "our" business.
[gw]: https://github.com/sitaramc/gitolite/blob/pu/doc/3-faq-tips-etc.mkd#_easier_to_link_gitweb_authorisation_with_gitolite

145
doc/gitolite-and-ssh.mkd Normal file
View file

@ -0,0 +1,145 @@
# F=gl_ssh how gitolite uses ssh
Although other forms of authentications exist (see the document on
[authentication versus authorisation][auth]), ssh is the one that most git
users use.
***Therefore, gitolite is (usually) heavily dependent on ssh***.
Most people didn't realise this, and even if they did they don't know ssh
well enough to help themselves. If you don't understand how ssh public key
authentication works, or how the `~/.ssh/authorized_keys` file can be used to
restrict users, etc., you will have endless amounts of trouble getting
gitolite to work, because you'll be attacking the wrong problem.
So please please please understand this before tearing your hair out and
blaming ***git/gitolite*** for whatever is going wrong with your setup :-)
## ssh basics
Let's start with some basics, focusing *only* on the pieces relevant to
`gitolite`. If this is not detailed enough, please use google and learn more
from somewhere, or maybe buy the OReilly ssh book.
* You can login to an ssh server by typing a password, but ssh can also use
***public-private keys*** (also called "key pairs") for authentication.
`gitolite` *requires* you to use this mechanism for your users -- they
cannot log in using passwords. Hopefully by the time you finish reading
this document you will understand why :-)
The way you set this up is you generate a key pair on your workstation,
and give the server the public key. (I need not add that the "private"
key must be, well, kept *private*!)
* **generating a key pair on your workstation** is done by running the
command `ssh-keygen -t rsa`. This produces two files in `~/.ssh`. One is
`id_rsa`; this is the **private** key -- ***never*** let it out of your
machine. The other is `id_rsa.pub`, which is the corresponding public
key. This public key is usually just one long line of text.
* on Windows machines with msysgit installed, you should do this from
within a "git bash" window. The command will report the full path where
the files have been written; make a note of this, and use those files in
any of the description that follows
* **adding your public key to the server**'s `~/.ssh/authorized_keys`
file is how ssh uses pubkeys to authenticate users. Let's say
sita@work.station is trying to log in as git@serv.er. What you have to do
is take the `~/.ssh/id_rsa.pub` file for user sita on work.station and
append its contents (remember it's only one line) to
`~/.ssh/authorized_keys` for user git on serv.er.
The `authorized_keys` file can have multiple public keys (from many
different people) added to it so any of them can log in to git@serv.er.
In the normal case (not gitolite, but your normal everyday shell access),
there's a command that does this, `ssh-copy-id`, which also fixes up
permissions etc., as needed, since sshd is a little picky about allowing
pubkey access if permissions on the server are loose. Or you can do it
manually, as long as you know what you're doing and you're careful not to
erase or overwrite the existing contents of `~/.ssh/authorized_keys` on
the server!
But in the gitolite case, it's different; we'll get to that in a minute.
* **troubleshooting pubkey authentication failures**: if you are unable to
get ssh access to the server after doing all this, you'll have to look
in `/var/log/secure` or `/var/log/auth.log` or some such file on the
server to see what specific error `sshd` is complaining about.
* **restricting users to specific commands** is very important for gitolite.
If you read `man sshd` and look for `authorized_keys file format`, you'll
see a lot of options you can add to the public key line, which restrict
the incoming user in various ways. In particular, note the `command=`
option, which means "regardless of what the incoming user is asking to do,
forcibly run this command instead".
Also note that when there are many public keys (i.e., lines) in the
`authorized_keys` file, each line can have a *different* set of options
and `command=` values.
Without this `command=` option, the ssh daemon will simply give you a
shell, which is not what we want for our gitolite keys (although we may
well have other keys which we use to get a shell).
**This is the backbone of what makes gitolite work; please make sure you
understand this**.
## how does gitolite use all this ssh magic?
These are two different questions you ought to be having by now:
* how does it distinguish between me and someone else, since we're all
logging in as the same remote user "git"
* how does it restrict what I can do within a repository
### restricting shell access/distinguishing one user from another
The answer to the first question is the `command=` we talked about before. If
you look in the `authorized_keys` file, you'll see entries like this (I chopped
off the ends of course; they're pretty long lines):
command="[path]/gl-auth-command sitaram",[more options] ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEA18S2t...
command="[path]/gl-auth-command usertwo",[more options] ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEArXtCT...
First, it finds out which of the public keys in this file match the incoming
login. That's crypto stuff, and I won't go into it. Once the match has been
found, it will run the command given on that line; e.g., if I logged in, it
would run `[path]/gl-auth-command sitaram`. So the first thing to note is
that such users do not get "shell access", which is good!
Before running the command, however, sshd sets up an environment variable
called `SSH_ORIGINAL_COMMAND` which contains the actual git command that your
workstation sent out. This is the command that *would have run* if you did
not have the `command=` part in the authorised keys file.
When `gl-auth-command` gets control, it looks at the first argument
("sitaram", "usertwo", etc) to determine who you are. It then looks at the
`SSH_ORIGINAL_COMMAND` variable to find out which repository you want to
access, and whether you're reading or writing.
Now that it has a user, repository, and access requested (read/write), gitolite looks
at its config file, and either allows or rejects the request.
But this cannot differentiate between different branches within a repo; that
has to be done separately.
### restricting branch level actions
[If you look inside the git source tree, there's a file among the "howto"s in
there called `update-hook-example.txt`, which was the inspiration for this
part of gitolite.]
Git allows you to specify many "hooks", which get control as various events
happen -- see `git help hooks` for details. One of those hooks is the
`update` hook, which, if it is present, is invoked just before a branch or a
tag is about to be updated. The hook is passed the name of the branch or tag,
the old SHA1 value, and the new SHA1 value, as arguments. Hooks that are
called *before* an action happens are allowed to prevent that action from
happening by returning an error code.
When gitolite is told to create a new repository (by the admin), it installs
a special update hook. This hook takes all the information presented, looks
at the config file, and decides to allow or reject the update.
And that's basically it.

View file

@ -0,0 +1,257 @@
# F=ggshb how to set up gitolite+gitweb+ssh+http-backend
## NAME
gitolite-gitweb-http-backend
## DESCRIPTION
You've been tasked with rolling out gitolite and git-web in your
corporate environment and your requirements are as follows:
1. git access must be via both ssh and http[s]
2. browsable via git-web
3. your web server must run as a user different from that of the git user
4. The repository has its own virtual host
Note that these instructions are geared toward OpenSuSE 11.4. Feel
free to modify the examples below to your environment.
## EXAMPLE ENVIRONMENT
The following assumptions are made for the purposes of example:
* The server name will be git.example.com
* Repositories are located in `/srv/git` and are owned by the `git` user
* Apache 2.2.\* running as `wwwrun:www` will be used as the web server
* gitolite has been installed via package management (yum, zypper, apt-get,
etc)
* gitweb browsing is via http://git.example.com/
* The repositories can be cloned from the following URLs:
* git@git.example.com:&lt;repo-name&gt;
* http://git.example.com/&lt;repo-name&gt;.git
* HTTP authentication is handled via a local htpasswd file
* http://git.example.com will be a virtual host
* Two git repositories will be created:
* engineering
* operations
## GITOLITE SETUP
Install gitolite via your package management tools. Under OpenSuSE, this will
install repositories in `/srv/git`. Follow the instructions found
[here][install] for initial set up.
### gitolite.rc
You will need to tell gitolite.rc about some additional keys that will
be needed for each repository. Make sure the following config option
is set in `/srv/git/.gitolite.rc`:
$GL_GITCONFIG_KEYS = "gitweb.url receive.denyNonFastforwards receive.denyDeletes";
These options tell gitolite to allow the user to set these values in
`gitolite.conf`, which in turn will be propagated to each
repositories git config.
### gitolite.conf
For the purposes of example, we assume that we have two groups accessing each repository: engineering and operations. So, our `gitolite.conf` file will look something like this:
#
# Group Definitions
#
@engineering = daniel erik alex jose mark
@operations = james chris long bora dmitriy
@gladmin = james chris
#
# Repository Definitions
#
# Note that we give access to the daemon user, thus enabling
# git-daemon-export-ok (see
# https://github.com/sitaramc/gitolite/blob/pu/doc/2-admin.mkd#gwd)
repo gitolite-admin
RW = @sysops daemon
R = @all
repo engineering
RW = @engineering @gladmin daemon
R = @all
config gitweb.url = git@git.example.com:engineering
config receive.denyNonFastforwards = true
config receive.denyDeletes = true
repo operations
RW = @operations @engineering @gladmin daemon
R = @all
config gitweb.url = git@git.example.com:operations
config receive.denyNonFastforwards = true
config receive.denyDeletes = true
repo @all
R = daemon gitweb
# additional configuration ...
Save, commit, and push your changes to the gitolite-admin repo as
described [here][conf].
## APACHE SETUP
Under OpenSuSE 11.4, Apache runs as user `wwwrun` group `www` (see `/etc/apache2/uid.conf`). But wait! How can Apache running as `wwwrun` commit to git repositories, which are owned by `git`?
### suexec
Enter SuExec. This is an apache module that allows apache to run
under the auspicious of a different user. For this to work, we need
to do some setup ahead of time. First, we need to make sure the
`suexec` program has the right permissions:
# OpenSuSE 11.4 puts the suexec program under /usr/sbin/suexec2
$ chgrp www /usr/sbin/suexec2
$ chmod 4750 /usr/sbin/suexec2
# Verify permissions
$ ls -al /usr/sbin/suexec2
-rwsr-x--- 1 root www 14944 Feb 18 20:53 /usr/sbin/suexec2
Next, we need to create a wrapper script for the suexec program and
place that under the correct directory. To find out the where to
place the wrapper script, do the following:
$ /usr/sbin/suexec2 -V
-D AP_DOC_ROOT="/srv/www"
-D AP_GID_MIN=96
-D AP_HTTPD_USER="wwwrun"
-D AP_LOG_EXEC="/var/log/apache2/suexec.log"
-D AP_SAFE_PATH="/usr/local/bin:/usr/bin:/bin"
-D AP_UID_MIN=96
-D AP_USERDIR_SUFFIX="public_html"
The variable we are interested in is `AP_DOC_ROOT` which is
`/srv/www`. So, we place the wrapper script in
`/srv/www/bin/gitolite-suexec-wrapper.sh` with the following contents:
#!/bin/bash
#
# Wrapper for gl-auth-command
#
USER=$1
export GIT_PROJECT_ROOT="/srv/git/projects"
export GITOLITE_HTTP_HOME="/srv/git"
# OpenSuSE gitolite RPM places gl-auth-command in /usr/bin
exec /usr/bin/gl-auth-command $USER
# End
_For security purposes, this file MUST exist under `/srv/www`!_
Finally, make sure Apache loads the suexec module. Under OpenSuSE,
this would mean adding "suexec" to `APACHE_MODULES` in
`/etc/sysconfig/apache2`.
### Gitweb
As gitweb will now be run under the `git` user, all files must be
under `/srv/www` as well.
# Under OpenSuSe, git-web installs in /usr/share/gitweb
$ cp -r /usr/share/gitweb /srv/www
$ chown -R git.git /srv/www/gitweb
Do not forget to point `$projectroot` in `/etc/gitweb.conf` to
`/srv/git/projects`!
### Virtual Host
Configure your virtual host as follows:
<VirtualHost git.example.com:80>
ServerName git.example.com
ServerAlias git
# By default, use gitweb
DocumentRoot /srv/www/gitweb
# Suexec setup
SuexecUserGroup git git
# Set up appropriate GIT environments
SetEnv GIT_PROJECT_ROOT /srv/git/projects
SetEnv GIT_HTTP_EXPORT_ALL
# Set up appropriate gitolite environment
SetEnv GITOLITE_HTTP_HOME /srv/git
# To serve gitweb at the same url, use a ScriptAliasMatch to
# only those URLs that git http-backend can handle, and
# forward the rest to gitweb:
ScriptAliasMatch \
"(?x)^/(.*/(HEAD | \
info/refs | \
objects/(info/[^/]+ | \
[0-9a-f]{2}/[0-9a-f]{38} | \
pack/pack-[0-9a-f]{40}\.(pack|idx)) | \
git-(upload|receive)-pack))$" \
/srv/www/bin/gitolite-suexec-wrapper.sh/$1
# Make sure we can execute gitweb okay
<Directory "/srv/www/gitweb">
Options ExecCGI
AllowOverride None
AddHandler cgi-script .cgi
DirectoryIndex gitweb.cgi
Order allow,deny
Allow from all
</Directory>
# We need gl-auth-command executable
<Directory "/srv/www/gitbin">
<Files "gitolite-suexec-wrapper.sh">
Order allow,deny
Allow from all
</Files>
</Directory>
# Set up authentication to taste
<Location />
AuthType Basic
AuthName "Private Git Access"
Require valid-user
AuthUserFile /srv/git/passfile
</Location>
</VirtualHost>
## VALIDATION
Once apache has been restarted, verify your configuration:
- Repository browsable via gitweb
- Check out repository via ssh
- Check out repository via http
- Commit over ssh git@git.example.com
- Commit over http
## ADDITIONAL RESOURCES
- [http://httpd.apache.org/docs/2.2/suexec.html](http://httpd.apache.org/docs/2.2/suexec.html) Apache suexec
documentation
- [http://www.kernel.org/pub/software/scm/git/docs/git-http-backend.html](http://www.kernel.org/pub/software/scm/git/docs/git-http-backend.html)
git-http-backend(1) documentation
- [https://git.wiki.kernel.org/index.php/Gitweb](https://git.wiki.kernel.org/index.php/Gitweb) git-web documentaiton
- [http://sitaramc.github.com/gitolite/doc/http-backend.html](http://sitaramc.github.com/gitolite/doc/http-backend.html) gitolite http backend documentation
## AUTHOR
Christopher M. Fuhrman << cfuhrman at panix dot com >>

View file

@ -0,0 +1,221 @@
# F=conf_examples gitolite.conf -- by example
I hate people who make statements like "I dont have time to learn". People
with that sort of attitude shouldn't use gitolite at all, and I refuse to
spoon-feed them or be their personal tutor.
However, it's possible that even with the right attitude and willingness to
learn, some people just get a mental block about something, and so I figure
this might help.
(Side note: followup questions not welcome from people in the former category;
you know who you are).
**WARNING 1**: in case of conflict between this document and reality, reality
wins. For conflict between this document and the [main document][conf], the
main document wins. In any case, please bring such issues to my notice.
**WARNING 2**: this document has examples only for the most commonly used
features. If you don't find a feature here, look in the main document before
asking me.
**WARNING 3**: Read the WHOLE document. I can't keep saying, for instance,
that "rewind" actually means any of 3 different things so I'll say it only
once. It's upto you to have read that part also.
## general notes
Git branch/tag name **recap**: branches look like refs/heads/something, tags
look like refs/tags/something. When there is no ambiguity, we leave out the
refs/heads/ and the refs/tags/.
A "rewind" means any of 3 things: force-push a branch (make it go backward,
using 'git push -f' or equivalent), delete a branch, or update a tag. The
first two are clearly information-losing operations so it is wise to require
special rights to do them. The third is in the same category because tags are
supposed to be "write once" so **re**-writing a tag is considered abnormal.
These examples are only for the more complex parts of the conf file. We're
not going to discuss things like what characters are allowed in a username or
reponame, how to write a comment line, how to write continuation lines (you
can't), include files, and all such *lexical* issues.
## F=regexov extremely brief regex overview
Regexes are powerful. Gitolite uses that power as much as it can. If you
can't handle that power, hire someone who can and become a manager.
That said, here's a very quick overview of the highlights.
`^` and `$` are called "anchors". They anchor the match to the beginning and
end of the string respectively.
^foo matches any string starting with 'foo'
foo$ matches any string ending with 'foo'
^foo$ matches exact string 'foo'.
To be precise, the last one is "any string starting and ending with *the same*
'foo'". "foofoo" does not match.
`[0-9]` is an example of a character class; it matches any single digit.
`[a-z]` matches any lower case alpha, and `[0-9a-f]` is the range of hex
characters. You should now guess what `[a-zA-Z0-9_]` does.
`.` (the period) is special -- it matches any character. If you want to match
an actual period, you need to say `\.`.
`*`, `?`, and `+` are quantifiers. They apply to the previous token. `a*`
means "zero or more 'a' characters". Similarly `a+` means "one or more", and
`a?` means "zero or one".
As a result, `.*` means "any number (including zero) of any character".
The previous token need not be a single character; you can use parens to make
it longer. `(foo)+` matches one or more "foo", (like "foo", "foofoo",
"foofoofoo", etc.)
## F=exbac basic access control
repo gitolite-admin
RW+ = sitaram
# this is equivalent to:
RW+ refs/.* = sitaram
Sitaram is the only admin. He can push, create, delete, or rewind any branch
or tag in the gitolite-admin repo.
repo testing
RW+ = @all
The 'testing' repo is a play area for everyone. Anyone can do anything to any
branch or tag on it.
repo foo
RW+ = sitaram dilbert
RW = alice ashok
R = wally
Wally can only read the repo. Alice and Ashok can push but not rewind; only
Sitaram and Dilbert can do that.
And now, a common misunderstanding:
R master = wally # WILL NOT DO WHAT YOU THINK IT DOES!!
This won't work. Please see [here][rpr_] for more on this.
repo foo
RW master$ = dilbert alice
# this is equivalent to:
RW refs/heads/master$ = dilbert alice
The reason for treating "master$" as "refs/heads/master$" is that matching
branches is the most common use so the syntax is optimised to make that
simpler to write and easier to read. Anything *not* starting with `refs/`
(<font color="gray">or `NAME/`, but that is out of scope for this
document</font>), is implicitly prefixed with `refs/heads/`.
The `master$` is called a "refex" (a regex that matches a ref).
Dilbert and Alice can push to the "master" branch. Unless some other rule
allows it, they cannot push to, say, "master1", "masterfull" etc., due to the
`$` at the end of the refex.
Refexes are *prefix matched*; i.e., treated as if they have a `^` at the
start. (This means `^refs/heads/master` in this case, not `^master`, in case
you forgot!)
This rule therefore does not match "headmaster", or even
"refs/heads/refs/heads/master" (<font color="gray">yes, it is possible to
confuse yourself by pushing a branch like that in git</font>).
RW+ pu = dilbert
# again, remember this is equivalent to:
RW+ refs/heads/pu = dilbert
Dilbert can push any branch whose name starts with "pu". This includes "pu1",
"pupu", "pu/up", and so on, not just "pu". This is because there is no `$` at
the end.
RW junk/ = wally
Wally can push any branch under "junk/", which means "junk/foo", "junk/bar",
are ok but not "junk1" or even "junk".
RW tmp/ = @all
Similar to above, but for *any* authenticated user.
RW refs/tags/v[0-9] = ashok # the QA guy
Ashok is allowed to push version tags. He can push any tag whose name starts
with a "v", then a digit, like "v1", "v1.0", "v2.0rc1", etc., but not "v-1",
"ver1".
## F=exaac advanced access control
### "deny" rules
**Warning**: When using deny rules, the order of your rules matters, where
earlier it did not.
PROCESSING LOGIC:
> The first matching refex that has the permission you're looking for (`W`
> or `+`) **or a minus (`-`)**, results in success **or failure,
> respectively**. A fallthrough **also** results in failure.
RW refs/tags/v[0-9] = ashok
- refs/tags/v[0-9] = @staff
RW refs/tags = @staff
This allows only Ashok to write "version tags" (see rule for Ashok the QA guy
somewhere above). The others can write any tags they want, *except* version
tags. To understand this, try and match each rule *in sequence* with the name
of the tag being pushed, and the user doing it, applying the logic described
earlier.
* for non-version tags, only the 3rd rule matches, so anyone on staff can
push them
* for version tags by ashok, the first rule matches so he can push them
* for version tags by staffers *other than ashok*, the second rule matches
before the third one, and it has a `-` as the permission, so the push
fails
### #ruleaccum2 rule accumulation
Rules accumulate. Even when separated by rules for other repos. They
accumulate intuitively. For example:
repo foo
RW+ = alice
repo bar
RW+ = dilbert
repo @all
RW dev/USER/ = @staff
repo foo
RW+ tmp/ = @staff
has the **effective** ruleset, for repo foo, of
repo foo
RW+ = alice
RW dev/USER/ = @staff
RW+ tmp/ = @staff
Just remember that if you use [deny rules][deny] anywhere then the *order of the
rules matters*!
### gitweb and daemon
Gitolite does NOT do anything for gitweb and daemon access **except**
* for daemon, create the file `git-daemon-export-ok` in the repository
* for gitweb, add the repo (plus owner name, if given) to the list of
projects to be served by gitweb (see the config file variable
`$PROJECTS_LIST`, which should have the same value you specified for
`$projects_list` when setting up gitweb)
* put the description, if given, in `$repo/description`

663
doc/gitolite.conf.mkd Normal file
View file

@ -0,0 +1,663 @@
# F=conf the access control file `gitolite.conf`
Gitolite has an advanced access control language that is designed to be
powerful but easy to use. Other objectives were that it should be even easier
to read, review and audit the rules, and it should scale to thousands of repos
and users. There was also, in the author's mind, a desperate need to create
something as different as possible from the brain-dead, nausea-inducing
"Windows INI" style syntax that some other popular tools seem to favour.
## #confrecap a quick summary of the conf file
This is a quick *recap* of the *most common* elements in a typical config
file. (In the following description, the angle brackets are not part of the
syntax).
* (optional) [group][groups] definitions
@<groupname> = <list of users and groups>
* repo access rules
repo <reponame>
# one or more access rules like
<permission> <optional refex> = <list of users and groups>
The [most common][bac] permissions are:
* R, for read only
* RW, for push existing ref or create new ref
* RW+, for "push -f" or ref deletion allowed (i.e., destroy
information)
* `-` (the minus sign), to [deny][] access
If no [refex][] is supplied, the access rule applies to all refs.
Later, gitolite [acquired][rwcd]
* RWC and RW+C, for ref creation. Once you use this in a repo, a normal
RW/RW+ can no longer create a ref; it can only push existing refs
(<font color="gray">Note also that a standalone "C" permission is
something [completely different][wild]</font>)
* RWD and RW+D, for ref deletion. Once you use this in a repo, a normal
RW+ can no longer delete a ref; it can only rewind
* (RWCD and RW+CD, which is just a combination of these two)
Even later, [gitolite acquired][mergecheck], if you want to enforce a
rebase-only workflow.
The following sections dig deeper into the syntax and semantics of the access
control rules and other configuration directives in the `gitolite.conf` file.
If narrative descriptions scare you, or your English is not upto scratch, try
[gitolite config by example][conf_examples] also.
## F=syntax lexical syntax
In general, everything is **space separated**; there are no commas,
semicolons, etc., in the syntax.
**Comments** are in the usual shell-ish style.
**User names** and **repo names** are as simple as possible; they must start
with an alphanumeric, but after that they can also contain `.`, `_`, or `-`.
Usernames can optionally be followed by an `@` and a domainname containing at
least one `.` (this allows you to use an email address as someone's username).
Reponames can contain `/` characters (this allows you to put your repos in a
tree-structure for convenience)
### continuation lines
There are no continuation lines -- gitolite does not process C-style
backslash-escaped newlines as anything special. However, the section on
"groups" will tell you how you can break up large lists of names in a group
definition into multiple lines.
### include files
Gitolite allows you to break up the configuration into multiple files and
include them in the main file for convenience.
include "foo.conf"
will include the contents of the file "foo.conf".
Details:
* You can also use a glob (`include "*.conf"`), or put your include files
into subdirectories of "conf" (`include "foo/bar.conf"`), or both
(`include "repos/*.conf"`).
* Included files are always searched from the gitolite-admin repo's "conf/"
directory, unless you supplied an absolute path. (Note: in the interests
of cloning the admin-repo sanely you should avoid absolute paths!)
* If you ended up recursing, files that have been already processed once are
skipped, with a warning.
<font color="gray">Advanced users: `subconf`, a command that is very closely
related to `include`, is documented [here][subconf].</font>
## F=bac basic access control
Here's a very basic set of rules:
repo gitolite-admin
RW+ = sitaram
repo testing
RW+ = @all
repo gitolite simplicity
RW+ = sitaram dilbert
RW = alice ashok
R = wally
It should be easy to guess what most of this means:
* `R` means "read" permission
* `RW` means "read and write", but no rewind
* `RW+` means "read and write", with rewind allowed
A "rewind" is more often called a "non-fast forward push"; see git docs for
what that is. The `+` was chosen because it is part of the "git push" syntax
for non-ff pushes.
Note that *tags* are generally considered "write once", so they are treated
specially by gitolite: you need rewind permission (`RW+`) to *overwrite* a
tag with a new value.
In a later section you'll see some more advanced permissions.
### how rules are matched
It's important to understand that there're two levels at which access control
happens. Please see [this][2levels] for details, especially about the first level
check. Much of the complexity applies only to the second level check, so that
is all we will be discussing here. This check is done by the update hook, and
determines whether the push succeeds or fails.
For basic permissions like this, matching is simple. Gitolite already knows:
* the user
* the repo
* the branch or tag ("ref") being pushed
* whether it is a normal (ff) push or a rewind (non-ff) push.
Gitolite goes down the list of rules matching the user, repo, and the ref.
The first matching rule that has the permission you're looking for (`W` or
`+`), results in success. A fallthrough results in failure.
### #refex branches, tags, and specifying "refex"es
One of the original goals of gitolite was to allow access control at the
branch/tag (aka "ref") level. The git source code contains a sample update
hook that has the following in it:
# from Documentation/howto/update-hook-example.txt:
refs/heads/master junio
+refs/heads/pu junio
refs/heads/cogito$ pasky
refs/heads/bw/.* linus
refs/heads/tmp/.* .*
refs/tags/v[0-9].* junio
If you did this in gitolite, this is what the equivalents would be:
repo git
RW master$ = junio # line 1
RW+ pu$ = junio # line 2
RW cogito$ = pasky # line 3
RW bw/ = linus # line 4
RW tmp/ = @all # line 5
RW refs/tags/v[0-9] = junio # line 6
The following points will help you understand these rules. (Git recap:
branches and tags together are called "ref"s in git. A branch ref usually
looks like "refs/heads/foo", while a tag ref looks like "refs/tags/bar")
* the general syntax of a paragraph of rules is:
# start line:
repo [one or more repos and/or repo groups]
# followed by one or more permissions lines:
[permission] [zero or more refexes] = [one or more users]
* a **refex** is a *perl regex* that matches a ref. When you try to push a
commit to a branch or a tag, that "ref" is matched against the refex part
of each rule.
* if the refex does not start with `refs/`, gitolite assumes a prefix of
`refs/heads/`. This is useful because *branch* matching is the most
common case, as you can see this applies to lines 1 through 5 here.
* if no refex appears, the rule applies to all refs in that repo (as if you
had specified `refs/.*` as the refex).
* refexes are prefix-matched (they are internally anchored with `^` before
being used). This means only the beginning of the actual ref needs to
match the refex, unless the refex has an explicit `$` meta-character at
the end (like the first 3 lines in our example do).
Line 5, for instance, allows anyone to push a branch inside the "tmp/"
namespace, while line 6 provides the ability to push version tags; "v1",
"v1.0", "v2.0rc1", all match the criterion specified by `v[0-9]` because
this is a prefix match only.
### #groups groups
Gitolite allows you to define **groups** of repos. users, or even refexes. A
group is semantically (but *not* syntactically) like a `#define` in C. Here
is an example of each kind:
@oss_repos = gitolite linux git perl rakudo entrans vkc
@staff = sitaram some_dev another-dev
@important = master$ QA_done refs/tags/v[0-9]
The syntax of a group definition is simply:
@groupname = [one or more names]
A group can *accumulate* values. For example:
@staff = sitaram some_dev another-dev
@staff = au.thor
is the same as
@staff = sitaram some_dev another-dev au.thor
This is more convenient than continuation lines, because it allows you to add
to a group anywhere. Many people generate their gitolite.conf itself from
some *other* database, and it is very useful to be able to do this sort of
thing.
Groups can include other groups, and the included group will be expanded to
whatever value it *currently* has:
@staff = sitaram some_dev another-dev # line 1
@staff = au.thor # line 2
@interns = indy james # line 3
@alldevs = bob @interns @staff # line 4
"@alldevs" expands to 7 names now. However, remember that the config file is
parsed in a single-pass, so later *additions* to a group name cannot affect
earlier *uses* of it. If you moved line 2 to the end, "@alldevs" would only
have 6 names in it.
#### the special `@all` group
There's a special group called `@all` that includes all authenticated users
when used as a username; you've seen examples of it earlier.
Advanced users: also see the entry for `GL_ALL_INCLUDES_SPECIAL` in the
documentation for [`~/.gitolite.rc`][rc].
When used as a reponame, it includes all repos physically present in
`~/repositories` (or whatever is the value of `$REPO_BASE`).
### F=rpr_ side note: "R" permissions for refs
You can control "read" access only at the repo level, not at the branch level.
For example, this **won't** limit Wally to reading only the master branch:
repo foo
R master = wally # WILL NOT DO WHAT YOU THINK IT DOES!!
and this **won't** prevent him from reading it:
repo foo
- master = wally # WILL NOT DO WHAT YOU THINK IT DOES!!
R = wally
This (inability to distinguish one ref from another during a read operation)
is a git issue, not a gitolite issue.
There are 3 ways around this, though:
* switch to gerrit, which has its own git stack, its own sshd, and God knows
what else. All written in Java, the COBOL of the internet era ;-)
* bug the git people to add this feature in ;-)
* use a separate repo for Wally.
Using separate repos is not that hard with gitolite. Here's how to maintain a
[partial copy][partialcopy] of the main repo and keep it synced (while not
allowing the secret branches into it).
## F=aac advanced access control
The previous section is sufficient for most common needs, but gitolite can go
a lot further than that.
### #NAME restricting pushes by dir/file name using NAME/
Here's a hopefully self-explanatory example. Assume the project has the
following contents at the top level: a README, a "doc/" directory, and an
"src/" directory.
repo foo
RW+ = lead_dev # rule 1
RW = dev1 dev2 dev3 dev4 # rule 2
RW NAME/ = lead_dev # rule 3
RW NAME/doc/ = dev1 dev2 # rule 4
RW NAME/src/ = dev1 dev2 dev3 dev4 # rule 5
Notes:
* the "NAME/" is part of the syntax; think of it as a keyword if you like.
The rest of it is treated as a refex to match against each file being
touched (see "SPECIFYING AND USING A REFEX" above for details)
* file/dir NAME-based restrictions are *in addition* to normal (branch-name
based) restrictions; they are not a *replacement* for them. This is why
rule #2 (or something like it, maybe with a more specific branch-name) is
needed; without it, dev1/2/3/4 cannot push any branches.
* if a repo has *any* NAME/ rules, then NAME-based restrictions are checked
for *all* users. This is why rule 3 is needed, even though we don't
actually have any NAME-based restrictions on lead_dev. Notice the pattern
on rule 3.
* *each* file touched by the commits being pushed is checked against those
rules. So, lead_dev can push changes to any files, dev1/2 can push
changes to files in "doc/" and "src/" (but not the top level README), and
dev3/4 can only push changes to files in "src/".
### #deny "deny" rules
#### warnings and required reading
Gitolite performs access checks at 2 levels. The first check is performed for
both read *and* write operations, while the second one happens only for write
operations.
**Required reading**: [this section][2levels] of the documentation.
**Warning**: When using deny rules, the order of your rules matters, where
earlier it did not. If you're just starting to add a deny rule to an existing
ruleset, it's a good idea to review the entire ruleset once, to make sure
you're doing it right.
#### "deny" rules for refs in a repo
You can use "deny" rules for the second check, to prevent people pushing
branches or tags that they should not be allowed to.
Take a look at the following snippet, which *seems* to say that "bruce" can
write versioned tags (anything containing `refs/tags/v[0-9]`), but the other
staffers can't:
@staff = bruce whitfield martin
[... and later ...]
RW refs/tags/v[0-9] = bruce
RW refs/tags = @staff
But that's not how the matching works. As long as any refex matches the
refname being updated, it's a "yes". Since the second refex (which says
"anything containing `refs/tags`") is a superset of the first one, it lets
anyone on `@staff` create versioned tags, not just Bruce.
So how do we say "these people can create any tags except tags that look like
this pattern"?
One way to fix this is to allow "deny" rules. We make a small addition to the
permissions syntax, and define a more rigorous, ordered, interpretation.
Let's recap the **existing semantics**:
> The first matching refex that has the permission you're looking for (`W`
> or `+`), results in success. A fallthrough results in failure.
Here are the **new semantics**, with changes from the "main" one in bold:
> The first matching refex that has the permission you're looking for (`W`
> or `+`) **or a minus (`-`)**, results in success **or failure,
> respectively**. A fallthrough **also** results in failure.
So the example we started with becomes, if you use "deny" rules:
RW refs/tags/v[0-9] = bruce
- refs/tags/v[0-9] = @staff
RW refs/tags = @staff
And here's how it works:
* for non-version tags, only the 3rd rule matches, so anyone on staff can
push them
* for version tags by bruce, the first rule matches so he can push them
* for version tags by staffers *other than bruce*, the second rule matches
before the third one, and it has a `-` as the permission, so the push
fails
#### "deny" rules for the entire repo
The previous section described deny rules for the second check, which is a
fairly common need. However, gitolite does not process deny rules for the
first check -- it's usually simple enough to make sure your config file does
not allow the particular user/repo combindation at all.
But there's one case where this becomes cumbersome: when you want to create
exceptions to uses of `@all`.
For example, if you want gitweb to show `@all` repos except the special
'gitolite-admin' repo, you must manually (and laboriously) maintain a list of
all your repos (except gitolite-admin) and use that in place of `@all`. Oh
joy...
But now you can do this:
repo gitolite-admin
- = gitweb daemon
[... other access rules ...]
config gitolite-options.deny-repo = 1
repo @all
R = gitweb daemon
Here are some notes on how/why this works:
* The order is important -- the deny rule must come first.
* The 'config' line is what tells git to behave differently, i.e., apply
deny rules for the first check also.
* Since there is no "ref" to match against the refexes in the rules,
gitolite just ignores the refexes, and simply looks at the permission (R,
RW, "-", etc) and the user list.
### #rwcd creating and deleting branches
Since the beginning of gitolite, `RW` gave the ability, not only to update,
but to *create* a branch (that matched the refex). Similarly, `RW+` meant
being able to not only rewind, but also delete a ref. Conceptually, a rewind
is almost the same as a delete+push (the only difference I can see is if you
had core.logAllRefUpdates set, which is *not* a default setting).
However, there seem to be cases where it is useful to distinguish these cases.
Arguments can be made on all sides if you're dealing with new users, so
gitolite now supports that (in a backward compatible way).
We'll look at the delete/rewind case in detail first:
* if the rules for a repo do not contain a `D` anywhere, then `RW+` will
allow both rewind and delete operations. Apart from being more convenient
if you don't need this separation, this also ensures backward
compatibility for setups created before this separation feature was added
to gitolite).
* if, however, *any* of the rules for a repo contains a `D` (example: `RWD`,
`RW+D`, etc) then `RW+` by itself will permit only a rewind, not a delete
The same thing applies to create/push, where if you have permissions like
`RWC` or `RW+C` anywhere in that repo, a simple `RW` or `RW+` can no longer
*create* a new ref.
You can combine the `C` and `D` also. Thus, the set of permissions you now
know about are, in regex syntax: `R|RW+?C?D?`. See a later section for the
full set of permissions possible.
Some usage hints:
* if you find that `RW+` no longer allows creation/deletion but you can't
see a `C`/`D` permission in the rules, remember that gitolite allows a
repo config to be specified in multiple places for convenience, included
delegated or included files. Be sure to search everywhere :)
* a quick way to make this the default for *all* your repos is:
repo @all
RWCD dummy-branch = foo
where foo can be either the administrator, or if you can ignore the
warning message when you push, a non-existant user.
### #mergecheck enforcing a no-merges policy
Some people want to enforce a no-merges policy for various reasons. The new
"M" qualifier can be used to specify who is allowed to push merge commits.
This works just like "C" and "D" in the previous section, so **please read
that for a more detailed description** and apply the same ideas, (including
the `@all` trick!) to "M" for "allow merge commits".
The only other thing to remember is that this qualifier, if used, goes at the
end of any permission starting with `RW` (i.e., all of them except `R`). For
example, `RW` becomes `RWM`, `RW+` becomes `RW+M`, `RW+CD` becomes `RW+CDM`.
## summary: permissions
The full set of permissions, in regex syntax: `-|R|RW+?C?D?M?`. This expands
to one of `-`, `R`, `RW`, `RW+`, `RWC`, `RW+C`, `RWD`, `RW+D`, `RWCD`, or
`RW+CD`, all but the first one optionally followed by an `M`. And by now you
know what they all mean.
[Side note: There is one more very important permission to be dealt with --
the standalone `C`, which is not really a "ref" level permission and can be
found in doc/wildcard-repositories.mkd.]
## F=confother_ other tips
### personal branches
Gitolite lets you define a "personal" or "scratch" namespace prefix for each
developer. See [here][pers] for details.
### #ruleaccum rule accumulation
(Also see [this][ruleaccum2] for a different example that may be more
intuitive for some people).
Gitolite lets you specify access rules for a repo in bits and pieces, and
accumulates them in the same sequence they were given. This is very
convenient. Let's say you have a mix of open source and closed source
projects, and "bosses" should have read access to all projects, and everyone
should have read access to open source projects. Assuming the appropriate
group definitions, this would work:
# all bosses have read access to all projects
repo @open @closed @topsecret
R = @bosses
# everyone has read access to "open" projects
repo @open
R = @bosses @devs @interns
If you notice that `@bosses` are given read access to `@open` via both rules,
don't worry that this causes some duplication or inefficiency. It doesn't :-)
Elsewhere in the file, you would specify access for individual repos (like RW,
RW+, etc). Gitolite combines all of these access rules, maintaining the
textual order in which they occur, when authorising a push.
And although this example used groups, you can use reponames as well, or mix
and match them. You can even distribute rulesets across multiple "include"
files if you wish.
Just remember that if you use [deny rules][deny] anywhere then the *order of the
rules matters*!
This feature also helps people who generate their gitolite.conf itself from
some *other* database -- it allows them much more flexibility in how they
generate rules.
### #gwd specifying gitweb and daemon access
Gitolite allows you to specify access for git-daemon and gitweb. This is a
feature that I personally do not use (corporate environments don't like
unauthenticated access of any kind to any repo!), but someone wanted it, so
here goes.
Gitolite has two pre-defined, "special", usernames: `daemon` and `gitweb`.
To make a repo or repo group accessible via "git daemon", just give read
permission to the special user "daemon". Similarly, give read permission to
`gitweb` to allow the gitweb CGI to show the repo. Something like this:
repo foo bar baz
R = gitweb daemon
This gives you a quick way to offer multiple repos up for gitweb and/or daemon
access.
However, **setting a description** for the project also enables gitweb
permissions so you can do it that way if you want. Of course in this case you
have to deal with each repo separately. Add lines like this to gitolite.conf:
foo = "some description"
bar = "some other description"
baz = "yet another description"
You can also **specify an owner** for gitweb to show, if you like; for example
I might use:
gitolite "Sitaram Chamarty" = "fast, secure, fine-grained, access control for git"
These lines are standalone, so you can add them anywhere in the conf file.
Note that gitolite does **not** install or configure gitweb/git-daemon -- that
is a one-time setup you must do separately. All gitolite does is:
* for daemon, create the file `git-daemon-export-ok` in the repository
* for gitweb, add the repo (plus owner name, if given) to the list of
projects to be served by gitweb (see the config file variable
`$PROJECTS_LIST`, which should have the same value you specified for
`$projects_list` when setting up gitweb)
* put the description, if given, in `$repo/description`
The "compile" script will keep these files consistent with the config settings
-- this includes removing such settings/files if you remove "read" permissions
for the special usernames or remove the description line.
Please **note** that giving permissions to these special users via `@all`
(that is, using either `repo @all` or `R = @all`), will not work unless you
set the rc-file variable `$GL_ALL_INCLUDES_SPECIAL` to `1`. Also, **NOTE**
that giving them read access to `repo @all` means the `gitolite-admin` repo is
also accessible. **It is upto you to decide if that is OK in your
environment**.
### #rsgc repo specific `git config` commands
(Thanks to teemu dot matilainen at iki dot fi)
> ----
> **Note**: this won't work unless the rc file has the right settings;
> please see `$GL_GITCONFIG_KEYS` in the [rc file doc][rc] for details and
> security information.
> ----
Sometimes you want to specify `git config` settings for your repos.
For example, say you have a custom post-receive hook that sends an email when
a push happens, and this hook looks in the config for whom to send the email
to, etc.
You can set these git config values within a "repo" paragraph:
repo gitolite
config hooks.mailinglist = gitolite-commits@example.tld
config hooks.emailprefix = "[gitolite] "
config foo.bar = ""
config foo.baz =
The syntax is simple:
config sectionname.keyname = [optional value_string]
This does either a plain "git config section.key value" (for the first 3
examples above) or "git config --unset-all section.key" (for the last
example). Other forms of the `git config` command (`--add`, the
`value_regex`, etc) are not supported.
> ----
> **WARNING**: simply deleting the config line from the `conf/gitolite.conf`
> file will *not* delete the variable from `repo.git/config`. The syntax in
> the last example is the *only* way to make gitolite execute a
> `--unset-all` operation on the given key.
> ----
You can repeat the 'config' line as many times as you like, and the last
occurrence will be the one in effect. This allows you to override settings
just for one project, as in this example:
repo @all
config gitolite.mirror.master = "frodo"
config gitolite.mirror.slaves = "sam gollum"
repo top-secret-project
# only sam, because we don't trust gollum
config gitolite.mirror.slaves = "sam"
The "delete config variable" syntax can also be used, if you wish:
repo highlander # there can be only one!
config gitolite.mirror.master =
config gitolite.mirror.slaves =
As you can see, the general idea is to place the most generic ones (`repo
@all`, or repo patterns like `repo foo.*`) first, and place more specific ones
later to override the generic settings.

361
doc/gitolite.rc.mkd Normal file
View file

@ -0,0 +1,361 @@
# F=rc configuring gitolite's advanced features -- the `.gitolite.rc` file
This is the documentation for the contents of the "rc" file
(`$HOME/.gitolite.rc`) on the server. Until now this documentation was
inline, within the rc file itself, but it has grown too large, too unwieldy,
and too difficult to grok for people new to gitolite.
The documentation follows approximately the same order as the sample variables
in the (now reorganised) example "rc" file.
[Note: in perl, there is no actual boolean. The undefined value, the number
'0', and the empty string, are all 'false'. Everything else is 'true'. It is
thus common to use just 0/1 for false/true].
## variables that should not be touched at all
The first section does not need too much elaboration. Let's just say bad
things happen if you change them.
## most often used/changed variables
* `$GL_WILDREPOS`, boolean, default 0
Setting this variable lets your users create repositories based on wild
cards, hand out R and RW permissions to other users to collaborate, etc.
See [this][wild] for lots of info on this.
* `$PROJECTS_LIST`, filename, default `~/projects.list`
This is for gitweb users only. Gitweb setup has a variable called
`$projects_list` (please see gitweb docs for more on this). Set this to
the same value as that one.
* `$GITWEB_URI_ESCAPE`, boolean, default undef
Apparently gitweb expects project names to be URI-escaped (but seems to
work fine even if you don't). If you need strict compatibility with
gitweb, add/uncomment this variable and set it to 1.
* `$REPO_UMASK`, octal, default `0077`
The default UMASK that gitolite uses makes all the repos and their
contents have `rwx------` permissions. People who want to run gitweb
realise that this will not do.
The correct way to deal with this is to give this variable a value like
`0027` (note the syntax: the leading 0 is required), and then make the
user running the webserver (apache, www-data, whatever) a member of the
'git' group.
If you've already installed gitolite then existing files will have to be
fixed up manually (for a umask or 0027, that would be `chmod -R g+rX`).
This is because umask only affects permissions on newly created files, not
existing ones.
## variables with an efficiency/performance impact
* `$GL_BIG_CONFIG`, boolean, default 0
This is the most common setting for efficiency in handling large repo/user
groups. This is a very powerful setting; please read [this][bc] if you
need details.
There are 3 other settings related to big configs. They are changed only
in rare cases, however, so are described later.
* `$GL_NO_DAEMON_NO_GITWEB`, boolean, default 0
If you have *lots* of repos, and you're *not* using gitweb or daemon, you
should probably set this on for efficiency. Despite the name, it also
blocks repo config settings. Please read [this][bc] for more details.
**WARNING**: if your description files are maintained by some other means
than via the gitolite config file, make sure you set this variable to 1.
* `$GL_NICE_VALUE`, boolean, default undef
The nice value to run under. Applicable only if it is greater than 0.
Please do NOT set it if your bits/resource.h does not define PRIO_PROCESS
is 0. For Linux this is true...
* `$BIG_INFO_CAP`, number, default 20
See [using patterns to limit output][limitoutput] for details.
## #rcsecurity variables with a security impact
**IMPORTANT NOTE**
This section describes variables that, if not carefully used, can cause
security issues. It also includes variables which I personally do not use and
do not have the ability to test thoroughly
Using non-default value for these variables voids the security reward in the
README. This does *not* mean they are less important or that I will ignore
problems; it just means *my* ability to catch problems may be limited by my
test suite, my actual production use, my time, and sometimes (LDAP comes to
mind) even my skill or resources available to me, and that therefore I depend
on feedback from my users to find or fix issues.
* `$GL_ALL_READ_ALL`, boolean, default undef
Eliminates the access control check for read access. Makes things much
(**much**!) faster when you have 10,000 projects and the compiled conf
file is more than 20MB in size! **Double check with your boss or have a
new job lined up before setting this on!**
* `$GIT_PATH`, string, default empty
If git on your server is on a standard path (that is `ssh git@server git
--version` works), leave this setting as is. Otherwise, find out where it
is and use that value here, for example `GIT_PATH="/opt/bin/";`
* `$GL_GITCONFIG_KEYS`, string, default empty
This setting allows the repo admin to define acceptable gitconfig keys.
Gitolite allows you to set git repo options using the "config" keyword;
see the section on "repo specific git config commands" in the
[gitolite.conf][conf] documentation for details and syntax.
However, if you are in an installation where the repo admin does not (and
should not) have shell access to the server, then allowing him to set
arbitrary repo config options *may* be a security risk -- some config
settings allow executing arbitrary commands!
You have 3 choices. By default `$GL_GITCONFIG_KEYS` is left empty, which
completely disables this feature (meaning you cannot set git configs via
the repo config).
The second choice is to give it a space separated list of settings you
consider safe. (These are actually treated as a set of perl regular
expression patterns, and any one of them must match).
For example, this allows repo admins to set one of those 3 config
keys (yes, that second pattern matches two settings from "man
git-config", if you look):
$GL_GITCONFIG_KEYS = 'core\.logAllRefUpdates core\..*compression';
Each pattern should match the *whole* key (in other words, there
is an implicit `^` at the start of each pattern, and a `$` at the
end).
Note: if you don't know regex syntax, please learn, but briefly, a
literal period must be escaped with a backslash, as you can see in the
example above. In addition, if you use double quotes instead of single
quotes, you will need to escape the backslash itself, like `"foo\\..*"`.
It's probably simplest to stick to single quotes.
The third choice (which you may have guessed already if you're familiar
with regular expressions) is to allow anything and everything:
`$GL_GITCONFIG_KEYS = '.*';`
* `$GL_NO_CREATE_REPOS`, boolean, default 0
DO NOT CHANGE THIS unless you have other means to create repos and
correctly populate them with the required hooks. No hooks, no access
control; you have been warned!
* `$GL_NO_SETUP_AUTHKEYS`, boolean, default 0
DO NOT CHANGE THIS unless you have other means to setup the authkeys file
(`~/.ssh/authorized_keys`). In an extreme case, if you switch this on
without also fixing up the authkeys file, users who you think you deleted
may still have access. All in all, please be careful, as with any change
that affects ssh.
* `$GL_WILDREPOS_DEFPERMS`, string, default undef
This sets default wildcard permissions for newly created wildcard repos.
If set, this value will be used as the default user-level permission rule
of new wildcard repositories. The user can change this value with the
setperms command as desired after repository creation; it is only a
default.
Example: `$GL_WILDREPOS_DEFPERMS = 'R @all';`
* `$HTPASSWD_FILE`, string, default empty
Gitolite can help users run the htpasswd command in a secure manner (since
gitolite has already identified them by an ssh key). If you want to
enable this, give the variable the absolute path to whatever file apache
(etc) expect to find the passwords in.
Look in the docs for [linking gitweb authorisation with
gitolite][gitwebauth] for more details on using this feature.
* `$RSYNC_BASE`, string, default empty
Gitolite can be used to allow fine grained control of the rsync command.
This setting enables the rsync external command helper, by specifying the
base path of all the files that are accessible via rsync. It must be an
absolute path, like `$RSYNC_BASE = "/home/git/up-down";`. Leave it
undefined or set to the empty string to disable the rsync helper.
When enabled, it runs rsync with specific arguments, all presumably filled
in correctly by the client-side rsync. However, I am not an expert on how
rsync may be abused, so if it breaks, you get to keep both pieces!
* `$SVNSERVE`, string, default empty
Gitolite can also be used to gate access (though not at a fine grained
level) to SVN if needed, passing authentication information on to
`svnserve`. This setting allows launching svnserve when requested by the
ssh client. This allows using the same SSH setup for both SVN and git
access. Leave it undefined or set to the empty string to disable svnserve
access.
The setting will look something like (where the %u is substituted with the
username):
$SVNSERVE = "/usr/bin/svnserve -r /var/svn/ -t --tunnel-user=%u";
* hook chaining
* `$UPDATE_CHAINS_TO`, string, default "hooks/update.secondary"
* `$ADMIN_POST_UPDATE_CHAINS_TO`, string, default
"hooks/post-update.secondary"
By default, the update hook in every repo chains to "update.secondary".
Similarly, the post-update hook in the admin repo chains to
"post-update.secondary". If you're fine with the defaults, there's no
need to do anything here. However, if you want to use different names or
paths, change these variables.
* `$GL_ADC_PATH`, string, default undef
This setting enables admin defined commands.
**WARNING**: Use this feature only if (a) you really know what you're
doing and (b) you really, **really**, know what you're doing! Please read
the [admin defined commands][ADCs] document for details. This is an
extremely powerful and flexible feature, and naturally anything that
flexible can be a security risk!
* `$GL_GET_MEMBERSHIPS_PGM`, string, default undef
Some sites would like to store group membership outside gitolite, because
they already have it in (usually) their LDAP server, and it doesn't make
sense to be forced to duplicate this information.
Set the following variable to the name of a script that, given a username
as argument, will return a list of groups that she is a member of. See
the [big config][bc] doc for more details.
Example: `$GL_GET_MEMBERSHIPS_PGM = "/usr/local/bin/expand-ldap-user-to-groups"`
* `$GL_HTTP_ANON_USER`, string, default undef
Analogous to running mob branches over ssh (as described in the [mob
branches][mob]), this variable -- combined with appropriate setup
described in [doc/http-backend.mkd][http] -- lets you pretend to gitolite
that unauthenticated HTTP users are actually authenticated as this user.
* `$GL_REF_OR_FILENAME_PATT`, string
Set of allowed characters in refnames (and, if you have `NAME/` rules, in
filenames as well). The default pattern is almost the same as
`$REPONAME_PATT` with some additions.
Although the current code is not at risk in any way even if we let in
names containing strings like `$(command)`, and although I intend to make
sure things stay that way, it's probably a good idea to trap weird
filenames early. Just to be safe.
You ought to be able to loosen the pattern by adding other characters to
it, if you really need to. If you do, at least avoid backquotes and the
dollar sign!
## less used/changed variables
* `$GL_ALL_INCLUDES_SPECIAL`, boolean, default undef
Giving access to @all users (as in `R = @all`) in the config normally
does *not* include the special users "gitweb" and "daemon". If you want
@all to include these two users, set this variable.
* `$GL_WILDREPOS_PERM_CATS`, string, default "READERS WRITERS"
Originally, we only allowed "R" and "RW" in the setperms command. Now we
allow the admin to define other roles as she wishes (example: MANAGERS,
TESTERS, etc).
This variable is a space-separated list of the allowed roles.
Please read the **[warning][rolenamewarn]** in the [wild][] document
before using this feature. This is a VERY powerful feature and if you're
not careful you could mess up the ACLs nicely.
This is the internal default if you don't set it (like if you didn't
update your ~/.gitolite.rc with new variables when you upgraded gitolite):
$GL_WILDREPOS_PERM_CATS = "READERS WRITERS";
You can use your own roles in addition to the standard ones; I suggest you
include READERS and WRITERS for backward compatbility though:
$GL_WILDREPOS_PERM_CATS = "READERS WRITERS MANAGERS TESTERS";
## rarely changed variables
* `$GL_LOGT`, string, default `$GL_ADMINDIR/logs/gitolite-%y-%m.log`
This is the template for location of the log files and format of their
names.
The default produces files like `~/.gitolite/logs/gitolite-2009-09.log`.
If you make up your own templates, **PLEASE MAKE SURE** the directory
exists and is writable; gitolite won't do that for you unless it is the
default, ("$GL_ADMINDIR/logs").
Note that `%d` is also available if you want.
* `$GL_PERFLOGT`, string, default undef
This gives the location of the performance log files. Uncomment and set
this variable if you want performance logging. Performance log files are
kept separate from access log files because they store different, usually
much shorter term, information.
See the previous variable (`GL_LOGT`) for template related info.
* `$GL_SITE_INFO`, string, default undef
Some installations would like to give their users customised information
(like a link to their own websites, for example) so that users have a
quick way to find some links or information.
If this variable is defined, the "info" command will print it at the end
of the listing.
* `$REPO_BASE`, string, default "repositories"
This is where all the repos go. If it's not an absolute path, it is
considered to be relative to $HOME. Moving all the repositories after the
install has completed is doable: just [disable writes][disable] to gitolite,
move `~/repositories/*`, change this variable, then re-enable writes.
## constants that aren't!
The source file `src/gitolite_rc.pm` defines a few "constants", for example:
$R_COMMANDS=qr/^(git[ -]upload-pack|git[ -]upload-archive)$/;
Let's say you want to disallow the archive feature, you would need to change
this constant.
As of this version, you can now change these "constants" also, simply by
defining a new value for any or all of them in your `~/.gitolite.rc` file.
If you use this to relax some of the patterns involved (for example, the value
of `ADC_CMD_ARGS_PATT`), please be sure you know what you're doing.
[dwr]: http://sitaramc.github.com/gitolite/doc/3-faq-tips-etc.html#_disabling_write_access_to_take_backups
[limit]: http://sitaramc.github.com/gitolite/doc/report-output.html#_using_patterns_to_limit_output
[gitconf]: http://sitaramc.github.com/gitolite/doc/gitolite.conf.html#_repo_specific_git_config_commands

163
doc/hook-propagation.mkd Normal file
View file

@ -0,0 +1,163 @@
# F=hook_prop hook propagation in gitolite
Some users like to know how hooks propagate, and when, and why there appear to
be two places to put them, and so on. I'll try and set out the logic here.
**Note**: You don't need to read all this just to add your own hooks or your
own functionality to a hook that gitolite uses. See [here][customhooks] and
[here][hookchaining].
## what hooks do repos get?
* `update`: all repos have this; it does the write-level access control
(per-branch permissions).
* `post-receive`: if you've enabled [mirroring][], all repos have this.
* `post-update`: only the special "gitolite-admin" repo has this; it acts
upon the changes you push to the admin repo.
* `gitolite-hooked`: not really a hook, but a special, zero-byte file; see
below.
* you can add any other hooks you want (see note at the top of this doc),
and gitolite will propagate them into all repos along with the others.
## when do repos "get" hooks?
* When the "compile" script runs, any repo that is named explicitly in the
conf file, and the "gitolite-hooked" file is not present, gets hooks.
* When someone runs `gl-setup` on the server, then *all* repos physically
present inside `$REPO_BASE` get them.
A repo does not have to be defined in the config for this to happen.
Also, this includes the initial install; if you already had some repos in
`REPO_BASE` they get the hooks.
* If `GL_WILDREPOS` is set, a repo gets hooks when a user creates a repo or
uses the "fork" command in "contrib/adc". <font color="gray">In the
latter case the hooks are explicitly copied from the source repo using the
"cp" command, not using the code internal to gitolite</font>.
## what exactly does "get hooks" mean?
The "hooks/" directory of the bare repo on the server will *forcibly* have
symlinks created for each of the hooks mentioned in the "what hooks..."
section.
Other files are left alone, so you *can* manually add a hook file to specific
repos, directly on the server, so long as there is no name clash.
## where do the symlinks point?
There are two places the symlinks can point.
* `$GL_PACKAGE_HOOKS/common` contains the "system" hooks.
`GL_PACKAGE_HOOKS` is defined in the rc file.
* `$HOME/.gitolite/hooks/common` has the "user" hooks.
<font color="gray">The special "post-update" hook for the equally special
"gitolite-admin" repo is not in either of those places. It's in
`../gitolite-admin` relative to them. Just don't worry about it, and don't
fiddle with it. **There is no spoon**.</font>
`GL_PACKAGE_HOOKS` is "/var/gitolite/hooks" or some such path for RPM/DEB or
"root" method, and "$HOME/share/gitolite/hooks" for the non-root method.
(<font color="gray">Basically, it's whatever you gave as the 3rd argument to
'gl-system-install' when you used the root or non-root methods, or whatever
the packager decided if you used the RPM/DEB method</font>).
## so where do I put my hooks?
Put them in the "user" location (`~/.gitolite/hooks/common`).
The "system" location hooks override the ones in the "user" location, as you
can see from the picture below. This can be useful to enforce site-wide hooks
when using the RPM/DEB or root install methods. (For the non-root install
it's useless, since both locations are under the control of the user).
.gv
edge [dir=forward color="blue"]
splines = false
glph [ shape = none label = <
<table>
<tr>
<td colspan="3" bgcolor="red">package/system hooks<br/>$GL_PACKAGE_HOOKS/common</td>
</tr><tr>
<td port="u" bgcolor="lightblue">update</td>
<td port="y1">yourhook1</td>
<td port="y2">yourhook2</td>
</tr>
</table>
>]
gladh [ shape = none label = <
<table>
<tr>
<td colspan="3" bgcolor="green">user hooks<br/>~/.gitolite/hooks/common</td>
</tr><tr>
<td port="u" bgcolor="lightblue">update</td>
<td port="y1">yourhook1</td>
<td port="y3">yourhook3</td>
</tr>
</table>
>]
rh [ shape = none label = <
<table>
<tr>
<td colspan="4" bgcolor="green">individual repo hooks<br/>$REPO_BASE/reponame.git/hooks</td>
</tr><tr>
<td port="u" bgcolor="lightblue">update</td>
<td port="y1">yourhook1</td>
<td port="y2">yourhook2</td>
<td port="y3">yourhook3</td>
</tr>
</table>
>]
rh:y3:n .. gladh:y3
rh:u:n .. glph:u:s
rh:y1:n .. glph:y1
rh:y2:n .. glph:y2
As you can see, when both locations have the same hook, the symlink points to
the "system" hook.
By default, the only reason you need to touch the "system" location is if you
want to modify the 'update' hook, but why would you fiddle with the most
important part of gitolite, huh? You're a good admin, and will use [hook
chaining][hookchaining] properly, right?
## why not just push a hook?
A question I often get is, why can't we simply push the hooks using the admin
repo, just like we push config changes.
To understand why, realise that in many sites, the "right to push the
gitolite-admin repo" is **not** the same as "right to get a command line on
the server and run arbitrary commands".
This means, gitolite tries its best to keep these two rights separated, and to
prevent someone who has the former right from trivially acquiring the latter.
And so we don't allow adding hooks by admin push.
That doesn't mean you can't do it yourself. Here's one possible way.
Using the simple instructions [here][customhooks], add a
`post-update.secondary` hook containing this code:
#!/bin/bash
cp $GL_ADMINDIR/local-hooks/* $GL_ADMINDIR/hooks/common
gl-setup
Now create a directory in your gitolite-admin clone called "local-hooks", put
all your hooks there, and add/commit/push.
That *should* do it. Test it and send me a patch for this document when you
do :-)

193
doc/http-backend.mkd Normal file
View file

@ -0,0 +1,193 @@
# F=http how to setup gitolite to use smart http mode
**Note**: "smart http" refers to the feature that came with git 1.6.6, late
2009 or so. The base documentation for this is `man git-http-backend`. Do
**NOT** read `Documentation/howto/setup-git-server-over-http.txt` and think
that is the same or even relevant -- that is from 2006 and is quite different
(and arguably obsolete).
## WARNINGS, plus stuff I need help with
* I have NOT converted the test suite to use this mode. Volunteers to
convert it to http access are welcome :-)
* I have no idea how to handle the password issue other than creating a
`~/.netrc` file and making it `chmod 600`. Anyway, http based access is
inherently less secure than pubkeys so not much point worrying about it.
* I have not tested any of the ancillary standalone programs (like
gl-admin-push) in this mode. They're most likely going to crash and burn
because `$HOME` is not defined or in the wrong place; manually set
`HOME=$GITOLITE_HTTP_HOME` and hope for the best. Luckily most of them
have to do with sshkeys so this may not matter. YMMV.
* tested on stock Fedora 14; if you test on other environments please let me
know how it worked out and if we need to adjust this document
* tested https with dummy certs and `GIT_SSL_NO_VERIFY`; no reason why it
shouldn't work on a proper setup with everything in place
* have not tried making repos available to both ssh *and* http mode clients;
(I'd guess it ought to work fine if the "apache" user was made login-able
and given a proper $HOME and `~/.ssh/authorized_keys` and all that). If
anyone has the energy to try that please let me know how that went.
## additional requirements
* requires `GIT_PROJECT_ROOT` (see "man git-http-backend" for what this is)
set explicitly (i.e., it is no longer optional). Please set it to some
place outside apache's `DOCUMENT_ROOT`.
## detailed instructions
I assume you've installed apache 2.x and git on the server.
I assume your httpd runs under the "apache" userid; adjust instructions below
if it does not. Similarly for "/var/www" and other file names/locations.
I assume you have read the "[please read this first][insttrouble]" section of the
main install document to get an idea of the general concepts and terminology
(just ignore anything that is specific to ssh).
### install gitolite under "apache"
Follow the "non-root" method, but since you can't even "su - apache", make the
following variations when doing this as root:
* `cd ~apache` first; this is `/var/www` on Fedora 14
* do this in the shell
mkdir gitolite-home
export GITOLITE_HTTP_HOME
GITOLITE_HTTP_HOME=/var/www/gitolite-home
PATH=$PATH:$GITOLITE_HTTP_HOME/bin
* now run the following commands. These are really the first 3 steps of the
"non-root" install (clone, mkdir, and gl-system-install), **except** you
substitute `GITOLITE_HTTP_HOME` in place of `HOME`. Note that you do NOT
run the gl-setup step yet.
cd gitolite-home
git clone /tmp/gitolite.git gitolite-source
# or wherever your local clone is, or directly from git://github.com/sitaramc/gitolite
cd gitolite-source
GHH=$GITOLITE_HTTP_HOME # just for convenience in next 2 commands
mkdir -p $GHH/bin $GHH/share/gitolite/conf $GHH/share/gitolite/hooks
src/gl-system-install $GHH/bin $GHH/share/gitolite/conf $GHH/share/gitolite/hooks
* after the gl-system-install step, add these to the **top** of
/var/www/gitolite-home/share/gitolite/conf/example.gitolite.rc
$ENV{GIT_HTTP_BACKEND} = "/usr/libexec/git-core/git-http-backend";
# or wherever you have that file; note: NO trailing slash
$ENV{PATH} .= ":$ENV{GITOLITE_HTTP_HOME}/bin";
# note the ".=" here, not "="
* run gl-setup with the name of your admin user
gl-setup sitaram
* IMPORTANT: fix up ownerships
chown -R apache.apache $GITOLITE_HTTP_HOME
### setup apache
You will need to setup certain values in the httpd conf, as given in `man
git-http-backend`. You can put all them into, for instance,
`/etc/httpd/conf.d/gitolite.conf` and apache [at least on Fedora 14] will pick
it up. These are the values to use; note that these are somewhat different
from those in the manpage cited above, plus we have one extra variable:
SetEnv GIT_PROJECT_ROOT /var/www/gitolite-home/repositories
SetEnv GIT_HTTP_EXPORT_ALL
# please see notes below on ssh+http access
ScriptAlias /git/ /var/www/gitolite-home/bin/gl-auth-command/
# note trailing slash
SetEnv GITOLITE_HTTP_HOME /var/www/gitolite-home
<Location /git>
AuthType Basic
AuthName "Private Git Access"
Require valid-user
AuthUserFile /path/to/some/passwdfile
</Location>
Now create/update the password file in `/path/to/some/passwdfile` using the
`htpasswd` command, and you're all done for the setup!
## usage
Git URLs look like `http://user:password@server/git/reponame.git`.
The custom commands, like "info", "expand" should be handled as follows. The
command name will come just after the `/git/`, followed by a `?`, followed by
the arguments, with `+` representing a space. Here are some examples:
# ssh git@server info
curl http://user:password@server/git/info
# ssh git@server info repopatt
curl http://user:password@server/git/info?repopatt
# ssh git@server info repopatt user1 user2
curl http://user:password@server/git/info?repopatt+user1+user2
It gets even more interesting for the `setperms` command, which expects STDIN.
I didn't want to get too much into the code here, so I found that the
following works and I'm leaving it at that:
(echo R user1 user2; echo RW user3 user4) |
curl --data-binary @- http://user:password@server/git/setperms?reponame.git
With a few nice shell aliases, you won't even notice the horrible convolutions
here ;-)
## allowing anonymous access
Like [mob branches][mob] with ssh, you can allow completely
**un**-authenticated users to still have some rights specified in gitolite.
Briefly, here's how:
* specify a ScriptAlias in apache config for unauthenticated access also. I
prefer something like
ScriptAlias /gitmob/ /var/www/gitolite-home/bin/gl-auth-command/
* set `$GL_HTTP_ANON_USER` to some name, like 'mob' or 'anon' in the rc file
* give rights to this user ('mob' or 'anon' or whatever you used) in the
gitolite config file and push the change
URLs (in this example) will then look like `http://server/gitmob/reponame.git`
-- we lose the userid:passwd part and change 'git' to 'gitmob'.
## ssh + http access and the `GIT_HTTP_EXPORT_ALL` variable
This document only talks about setting up access to a set of git repositories
purely via smart http. The `GIT_HTTP_EXPORT_ALL` variable must be set for
such environments.
However, it is possible to allow both ssh as well as http access, perhaps
using suexec to make the CGI run under the 'git' user [detailed documentation
patches welcome!] For those environments, this variable is not mandatory.
If you omit that variable, you can decide which repo is accessible via http by
setting `R = daemon` just for those repos.
Please note that there is no way to use "deny" rules for *read* access. Do
not try:
repo gitolite-admin
- = daemon
repo @all
R = daemon
to achieve the (possibly common) need for disallowing http access to the admin
repo.
----
Enjoy!

203
doc/index.mkd Normal file
View file

@ -0,0 +1,203 @@
# F=index Hosting git repositories
Gitolite allows you to setup git hosting on a central server, with
fine-grained access control and many (many!) more powerful features.
## #qi quick install
**If** you're comfortable with Unix and ssh, **and** you have a relatively
sane setup, the following steps should work:
* create a user called `git`. Login to this user.
* copy your ssh pubkey from your workstation. Rename it to `YourName.pub`.
* now run these commands:
git clone git://github.com/sitaramc/gitolite
gitolite/src/gl-system-install
gl-setup -q ~/YourName.pub
You're done. If it didn't work, well that's what the [install][] doc is for,
especially the [if you run into trouble][insttrouble] section.
**WARNING**: do **NOT** add repos or users directly on the server! You MUST
manage the server by cloning the special 'gitolite-admin' repo on your
workstation (`git clone git@server:gitolite-admin`), making changes, and
pushing them. Here's how to [add users and repos][add].
## #rtfm what to read...
Gitolite comes with a *lot* of documentation. The master TOC (see link above)
is the *only* comprehensive list of what is there, but here's an overview.
* understanding gitolite
* [what is gitolite][gl_what], and the rest of this document
* gitolite install and basic admin in [pictures][]
* gitolite and [ssh][gl_ssh]
* explaining gitolite to your *users*
* the [user][] document is all they need
* install and setup
* the "[install trouble?][insttrouble]" section, which links, among
other things, to arguably the most useful doc for a newbie: [ssh
troubleshooting][sts]!
* maybe even the complete [install][] document
* normal admin tasks done on the server
* [adding users and repos][add]
* [admin][]: add your own hooks, add existing repos into gitolite, etc.
* [rc][]: sett gitolite behaviour options (warning: some of the
variables have a security impact if you're careless)
* normal admin tasks done by changing [gitolite.conf][conf]
* basic access control
* advanced access control
* extras: personal branches, gitweb/git-daemon access, git config settings
* [gitolite.conf by example][conf_examples] may also help
* advanced use (experts only; you can shoot yourself in the foot nicely!)
* [ADCs][]: allow users to run specific shell commands (but not give them a shell)
* (also, [sample ADCs][shipped_ADCs] that come with gitolite)
* let [users create][wild] their own repos and assign permissions
* [delegate][deleg] admin rights
* [mirror][mirroring] your servers
* special installation scenarios:
* use smart-[http][] instead of ssh
* [migrate][migr] from gitosis
Finally, [tips][] has a lot of useful information.
## #gl_what what is gitolite?
Gitolite is an access control layer on top of git. Here's an "executive
summary":
* use a single unix user ("real" user) on the server
* provide access to many gitolite users
* they are not "real" users
* they do not get shell access
* control access to many git repositories
* read access controlled at the repo level
* write access controlled at the branch/tag/file/directory level,
including who can rewind, create, and delete branches/tags
* can be installed without root access, assuming git and perl are already
installed
* authentication is most commonly done using sshd, but you can also use
httpd if you prefer (this may require root access).
* several other neat features, too many to list here
## F=need_ why is gitolite needed?
Gitolite is separate from git, and needs to be installed and configured. So...
why do we bother?
Gitolite is useful in any server that is going to host multiple git
repositories, each with many developers, where some sort of access control is
required.
In theory, this can be done with plain old Unix permissions: each user is a
member of one or more groups, each group "owns" one or more repositories, and
using unix permissions (especially the setgid bit -- `chmod g+s`) you can
allow/disallow users access to repos.
But there are several disadvantages here:
* every user needs a userid and password on the server. This is usually a
killer, especially in tightly controlled environments
* adding/removing access rights involves complex `usermod -G ...` mumblings
which most admins would rather not deal with
* *viewing* (aka auditing) the current set of permissions requires running
multiple commands to list directories and their permissions/ownerships,
users and their group memberships, and then correlating all these manually
* auditing historical permissions or permission changes is pretty much
impossible without extraneous tools
* errors or omissions in setting the permissions exactly can cause problems
of either kind: false accepts or false rejects
* without going into ACLs it is not possible to give some people read-only
access while some others have read-write access to a repo (unless you make
it world-readable). Group access just doesn't have enough granularity
* it is absolutely impossible to restrict pushing by branch name or tag
name.
Gitolite does away with all this:
* it uses ssh magic to remove the need to give actual unix userids to
developers
* it uses a simple but powerful config file format to specify access rights
* access control changes are affected by modifying this file, adding or
removing user's public keys, and "compiling" the configuration
* this also makes auditing trivial -- all the data is in one place, and
changes to the configuration are also logged, so you can audit them.
* finally, the config file allows distinguishing between read-only and
read-write access, not only at the repository level, but at the branch
level within repositories.
## why did I write it?
The most important feature I needed was **per-branch permissions**. This is
pretty much mandatory in a corporate environment, and is almost the single
reason I started *thinking* about writing gitolite.
It's not just "read-only" versus "read-write". Rewinding a branch (aka "non
fast forward push") is potentially dangerous, but sometimes needed. So is
deleting a branch (which is really just an extreme form of rewind). I needed
something in between allowing anyone to do it (the default) and disabling it
completely (`receive.denyNonFastForwards` or `receive.denyDeletes`).
### F=morefeatures_ some more features
Here're some more features.
* simple, yet powerful, config file syntax, including specifying
gitweb/daemon access. You'll need this power if you manage lots of
users+repos+combinations of access
* apart from branch-name based restrictions, you can also restrict by
file/dir name changed (i.e., output of `git diff --name-only`)
* if your requirements are still too complex, you can split up the config
file and delegate authority over parts of it
* easy to specify gitweb owner, description and gitweb/daemon access
* easy to sync gitweb (http) authorisation with gitolite's access config
* comprehensive logging [aka: management does not think "blame" is just a
synonym for "annotate" :-)]
* "personal namespace" prefix for each dev
* migration guide and simple converter for gitosis conf file
* "exclude" (or "deny") rights at the branch/tag level
* specify repos using patterns (patterns may include creator's name)
* define powerful operations on the server side, even github-like forking
## security
Due to the environment in which this was created and the need it fills, I
consider this a "security" program, albeit a very modest one.
The first person to find a hole that allows a non-admin user to push a change
to a repository that he is not allowed to, will get a modest reward of 5000
INR. The hole should not require enabling any of the options listed as having
a [security impact][rcsecurity] in the rc file, nor obvious things like setting
the umask too loose, etc.
## F=license contact and license
The gitolite software is released under GPL v2. See COPYING for details.
The gitolite documentation is provided under a [Creative Commons
Attribution-NonCommercial-ShareAlike 3.0 Unported
License](http://creativecommons.org/licenses/by-nc-sa/3.0/).
* author: sitaramc@gmail.com, sitaram@atc.tcs.com
* mailing list: gitolite@googlegroups.com
* list subscribe address : gitolite+subscribe@googlegroups.com
* IRC: #git and #gitolite on freenode. Note that I live in India (UTC+0530
time zone).

455
doc/install.mkd Normal file
View file

@ -0,0 +1,455 @@
# F=install gitolite installation
(Note: git servers are most commonly used with ssh URLs, and this document
describes installing gitolite to support such usage. If your users prefer
http URLs, read [this][http] to install gitolite to support "smart http").
## installing and upgrading gitolite
This section tells you how to install/upgrade gitolite, without too much
background. Later sections have more details and troubleshooting info; please
read them before asking for help if you have problems.
A bare minimum gitolite setup has:
* a server
* a "hosting user" on the server (a real Unix userid; we use "git" in this
document, although RPM/DEB installs use "gitolite")
* a virtual "admin user" -- the user who sets up gitolite and configures it
* the admin user's client or workstation, from which he does all his work
Gitolite allows 3 methods of install. The two most common are (1) the
**package method**, used if you have a gitolite RPM or a DEB available, and
(2) the **non-root method** which is the preferred manual install mode. Less
commonly used is (3) the **root method**, which is useful if you plan to have
multiple "hosting users" on the same server.
These install methods are described in detail below. (*Once you finish the
install, read the [admin document][admin] to administer your gitolite
installation*).
### F=rpmdeb package method
(Unlike in the rest of this document, we use "gitolite" as the "hosting user"
instead of "git" here, because that is the user that both the Fedora and
Debian packages create. Your distro/OS may vary.)
On your *workstation*:
* copy your `~/.ssh/id_rsa.pub` file to `/tmp/YourName.pub` on the server.
(The name of this file determines your gitolite username, so if you leave
it as `id_rsa.pub`, your gitolite username will be `id_rsa`, which may not
be what you want).
On your *server*, as *root*:
yum install gitolite # or 'apt-get install gitolite', or whatever
# this is the only step you need to repeat when upgrading gitolite
# current RPM/DEB create a hosting user called "gitolite"
su - gitolite
# (now as gitolite)
gl-setup /tmp/YourName.pub
Note: please see appendix d for command line options for [gl-setup][].
On your *workstation*:
git clone gitolite@server:gitolite-admin
### F=nonroot non-root method
**IMPORTANT WARNING -- IGNORE AT YOUR PERIL**: if you want to use this method
you had better know the password to the hosting user on the server, or be able
to `su` to it from root, just in case you manage to lock yourself out by
messing with the keys.
**NOTE**: This method is exhaustively described in the [tutorial][tut], if
you're interested. (That tutorial is by someone else but it's nice enough for
me to link it here).
[tut]: http://sites.google.com/site/senawario/home/gitolite-tutorial
On your *workstation*:
* copy your `~/.ssh/id_rsa.pub` file to `/tmp/YourName.pub` on the server.
(The name of this file determines your gitolite username, so if you leave
it as `id_rsa.pub`, your gitolite username will be `id_rsa`, which may not
be what you want).
On your *server*, as *git* (the "hosting user"), first check if `$HOME/bin` is
on the default PATH. If not, fiddle with the `.bashrc` or `.bash_profile` or
similar files and add it somehow. Then:
git clone git://github.com/sitaramc/gitolite
gitolite/src/gl-system-install
# defaults to being the same as:
# gitolite/src/gl-system-install $HOME/bin $HOME/share/gitolite/conf $HOME/share/gitolite/hooks
# to upgrade gitolite, repeat the above commands. Make sure you use the
# same arguments for the last command each time.
gl-setup /tmp/YourName.pub
Note: please see appendix d for command line options for [gl-setup][].
On your *workstation*:
git clone git@server:gitolite-admin
#### F=upgrfromclient upgrading from from-client method to non-root method
Since the from-client method is now deprecated for reasons explained
elsewhere, some folks may want to do their next upgrade using the non-root
method.
There are many, many ways to skin this cat; here's one way:
* follow non-root install but stop after the gl-system-install step
* temporarily rename your `~/.gitolite.rc` file to something else
* now run the gl-setup step
(background: this will create a default rc file with default values, but
crucially, it will give you the correct values for two very critical
variables that are not used in the old from-client install method)
* edit `~/.gitolite.rc` and bring in any non-default settings you may have
had in your old rc file.
When you're done, the only difference between your old and current rc
files should be that the `$GL_PACKAGE_CONF` and the `$GL_PACKAGE_HOOKS`
variables are no longer commented out and look somewhat like this:
$GL_PACKAGE_CONF = '/home/git/share/gitolite/conf';
$GL_PACKAGE_HOOKS = '/home/git/share/gitolite/hooks';
Now save the file.
### F=root root method
On your *workstation*:
* copy your `~/.ssh/id_rsa.pub` file to `/tmp/YourName.pub` on the server.
(The name of this file determines your gitolite username, so if you leave
it as `id_rsa.pub`, your gitolite username will be `id_rsa`, which may not
be what you want).
On your *server*, as *root*:
git clone git://github.com/sitaramc/gitolite
gitolite/src/gl-system-install
# defaults to being the same as:
# gitolite/src/gl-system-install /usr/local/bin /var/gitolite/conf /var/gitolite/hooks
# to upgrade gitolite, repeat the above commands. Make sure you use the
# same arguments for the last command each time.
# now create your "hosting user" ('git' in our examples) using whatever
# command your distro expects you to use
# switch to the hosting user
su - git
# (now as git)
gl-setup /tmp/YourName.pub
Note: please see appendix d for command line options for [gl-setup][].
On your *workstation*:
git clone git@server:gitolite-admin
### #upgrade upgrading
Upgrading is easy; you just re-run some of the same commands used for install.
These commands are clearly noted in the install instructions above.
However, if you've added any new hooks, you must also run the next step (the
`gl-setup` command), although this time you don't need to supply a pubkey
filename as an argument.
## #insttrouble install trouble?
If you run into trouble, please read the following sections.
### common install problems
The most common problem is usually ssh. Here are three facts of ssh:
* ssh is a pain
* most people don't know ssh well enough
* even people who think they do, don't
Please read how [gitolite uses ssh][gl_ssh] and the [ssh
troubleshooting][sts] documents before asking for help.
If you've tried multiple methods of install, you may have multiple copies of
the sources lying around. This could be a problem; see [appendix a][instpath]
for how to detect and deal with this.
If none of this works read the rest of this document, understand it as much as
you can, then ask for help.
### #instnameconv naming conventions used
Throughout the documentation, we use "YourName" as the admin user, and his
workstation is called "client". The hosting user is "git", and the server is
called "server". **Please substitute your values as needed**.
**If you're using DEB or RPM**, the installer creates a user called
"gitolite", so substitute that for "git" anywhere in the docs where the
"hosting user" is mentioned as "git".
Also, we often say "the rc file". This means `~/.gitolite.rc` on the server.
And when we say the "access control rules", or "conf file", or "config file",
we mean `conf/gitolite.conf` on your gitolite-admin clone.
### F=instbg helpful background information
* gitolite runs as a single (real) user on a server, and is invoked via ssh.
Traditionally, this "hosting user" is "git", and thus all git URLs start
with `ssh://git@server` (or the equivalent shorter form `git@server:`).
* RPM/DEB create and use "gitolite" as the hosting user
* there is *usually* only one hosting user per server (machine), but
gitolite makes it trivial to have as many as you want. In fact, every
user on the server is a potential hosting user.
* using this single user and sshd (or httpd) authentication, gitolite allows
you to create any number of "virtual" users. Virtual user names only mean
something to gitolite, and they need not be the same as any real userid on
the server or any of the clients accessing it.
* the first such virtual user is the "admin user", created during the
install sequence.
* gitolite depends **heavily** on ssh pubkey (passwordless) access. Do not
assume you know all about ssh -- most people **don't**. If in doubt, use
a dedicated userid on both client and server for installation and
administration of gitolite.
To make matters worse, ssh problems in gitolite don't always look like ssh
problems. See the [ssh troubleshooting][sts] document for help.
* gitolite **does NOT** like it when people with shell access to the server
fiddle with files and directories it controls.
Apparently this was not obvious to some people.
It is possible to have the server and the client be the same machine, and even
the admin user be also the hosting user, (i.e., `sitaram@server` can install
and administer a gitolite setup running under `sitaram@server`, a situation
that is common with some hosting services). It's actually fairly easy and
**safe** to do, **as long as you have password access to the server** for
emergency use. However, I will not be documenting it because (a) if you know
ssh you'll know how to extrapolate my instructions to do this and (b) if you
don't know ssh it'll be a nightmare to support you.
### F=instrequire requirements
#### client/workstation
* git version 1.6.6 or greater
* even msysgit on Windows is fine; please don't ask me for help if
you're using putty, plink, puttygen, etc., for ssh; I recommend
msysgit for Windows and the openssh that comes with it
#### server
* any Unix system with a posix compatible "sh".
* people using "csh" or derivatives please don't ask me for help -- tell
your admin csh is not posix compatible
* git version 1.6.6 or later
* can be in a non-PATH location if you are unable to install it
normally; see the `$GIT_PATH` variable in the "rc" file
* perl 5.8 or later
* openssh or any ssh that can understand the `authorized_keys` file format
(probably optional if you're using the http backend)
* a Unix userid to be the hosting user, usually "git" but it can be any
user, even your own normal one. (If you're using an RPM/DEB the install
probably created one called "gitolite").
#### technical skills
* if you're installing gitolite, you're a "system admin", like it or not.
Ssh is therefore a necessary skill. Please take the time to learn at
least enough to get passwordless access working.
* you also need to be somewhat familiar with git itself. You cannot
administer a whole bunch of git repositories if you don't know the basics
of git.
* some familiarity with Unix and shells is probably required
* regular expressions are a big part of gitolite in many places but
familiarity is not necessary to do basic access control.
### F=getgl_ getting the gitolite software
You can get the latest version of gitolite from github or google code using
the 'git clone' command:
git clone git://github.com/sitaramc/gitolite.git
# (OR)
git clone https://code.google.com/p/gitolite/
#### getting a tar file from a clone
If you are on an internal network and cannot clone the gitolite repo, you can
do the clone on some other machine and create a tar file from it to use on the
internal network. Here's how:
git clone git://github.com/sitaramc/gitolite.git
# (OR)
git clone git://sitaramc.indefero.net/sitaramc/gitolite.git
cd gitolite
make master.tar
# or maybe "make pu.tar"
Please use the make command as shown, not a plain "git archive", because the
Makefile adds a file called `.GITOLITE-VERSION` that will help you identify
which version you are using.
## #instappendices_ appendixes
The following sections have some miscellaneous information that does not
cleanly to fit anywhere else.
### #instpath appendix a: PATH issues for gl-setup
If you've tried multiple methods of install, you may have multiple copies of
the sources lying around, and when you ran `gl-setup` it picked up the wrong
one. This might also happen if the directory you supplied as the first
argument to `gitolite/src/gl-system-install` is not even in the `$PATH`.
Run `su - git` then `which gl-setup` to see which it picked up. This is what
it should be for each method:
* RPM/DEB method: probably `/usr/bin`
* root method: the first argument to the `gitolite/src/gl-system-install` command (or
`/usr/local/bin` by default)
* non-root method: the first argument to the `gitolite/src/gl-system-install` command
(or `$HOME/bin` by default)
If this is not what you get, remove the partially installed or extraneous
sources, if any, and try again. Or fix your `$PATH`.
One situation that is not easy to solve is if the system admin installed
gitolite using the RPM/DEB or root methods, and you want to install a later
version using the non-root method. Since `/usr/bin` and `/usr/local/bin` are
usually earlier than `$HOME/bin` in the `$PATH`, you'll have to get creative.
Good luck.
### #clean appendix b: cleaning out a botched install
When people have trouble installing gitolite, they often try to change a bunch
of things manually on the server. Or sometimes they'll upgrade from one
install method to another without checking some things over properly. Or
they'll follow instructions meant for a much newer version of gitolite and
make a royal mess of the whole thing.
Here's how to clean up, without losing your actual repositories.
All this is on the server. Note that the instructions are so long because
they're generic enough to fit any situation.
* Clean out the existing install
* edit `~/.ssh/authorized_keys` and delete all lines between `# gitolite
start` and `# gitolite end` inclusive.
* look in `~/.gitolite.rc` for 2 variables starting with `GL_PACKAGE_`.
If they are defined (and not just commented out), you need to clean
out all gitolite related files and directories from those two paths.
Just for reference, the defaults for a non-root install are 'conf' and
'hooks' in `$HOME/share/gitolite`, while for an RPM/DEB or root
install they should be in `/var/gitolite/` or some such.
If those variables don't exist or are commented out, ignore this step.
* look in `$PATH` for any gitolite programs and delete them also. A
good way to hunt them down is `which gl-auth-command`, and in the path
you find, delete all "gl-*" programs (perhaps after checking the list,
if the path you find happens to be /usr/bin or such!!)
Repeat this step until there are no more. I know of people who mixed
different install methods and had two, or even three, versions lying
around.
* make some temp directory (say "old"), and **move** the following
files/directories into it: `~/.gitolite`, `~/.gitolite.rc` and
`~/repositories/gitolite-admin.git`. If there's nothing you need to
salvage from them you can delete them too.
* if you used an RPM/DEB install, remove the package also.
* Now install a fresh copy of gitolite using whatever method you prefer.
(If you used a different method earlier and did not clean things out
properly per the instructions given above, expect trouble).
* You now have a brand new "rc" file. If your old rc file had any
non-default settings you should **manually** pull them in to the new one.
However, **do NOT** change the two variables starting with `GL_PACKAGE_`
in the new rc file; even if the old one had something different leave them
alone.
* You also have a brand new gitolite-admin repo. Clone this to your
workstation, then use the saved copy of the old admin repo to salvage
whatever you need (entire revision history, or just the conf/key files,
whatever floats your boat).
Once you've got your admin repo looking how you want it, including 'repo'
statements for all your existing repos, just add/commit/push it.
* Go back to the server and run `gl-setup` once again (no arguments needed).
That should do it.
### #uninstall_ appendix c: uninstalling gitolite completely
To uninstall gitolite completely, first follow the "clean out..." steps in the
previous section.
If you have not really started using gitolite properly yet, you can remove all
of `~/repositories` also and be done.
But if you *do* need to preserve the other repos and wish to continue to use
them, remove all the `update` hooks that gitolite installs in each repository.
The easiest way is:
find ~/repositories -wholename "*.git/hooks/update" | xargs rm -f
but you can do it manually if you want to be careful.
### #gl-setup appendix d: command line options for gl-setup
After gl-system-install (or the RPM/DEB) have installed the *code*, gl-setup
sets up the actual gitolite instance. (Gitolite in [pictures][] may help
explain this better.)
In ssh mode, gl-setup expects a pubkey filename the first time it is run, and
will complain if you don't supply it. On subsequent runs it is optional; you
only need to supply it if you want to quickly and easily change the admin's
(or indeed anyone's!) pubkey without going through all the steps that
[gl-admin-push][adminpush] requires.
In http mode, gl-setup expects an "admin name" the first time it is run. On
subsequent runs, arguments are ignored.
gl-setup accepts the following command line options, which must appear
*before* the pubkey filename/admin name:
* `-q` -- quiet mode; suppress the editor that pops up to allow you to
change the rc file the first time. Meaningless/ignored on subseqent runs.
* `-q -q` -- extra quiet mode; suppress the editor as well as the
sshkeys-lint check at the end of the run. Old-timers who know ssh so well
that they still use protocol 1 keys *must* use this mode, because
sshkeys-lint will barf on them. Equivalent to `-q` in http mode.

126
doc/migrate.mkd Normal file
View file

@ -0,0 +1,126 @@
# F=migr migrating from gitosis to gitolite
HELP WANTED: these instructions have been revamped a bit recently
[2011-07-18], so if something doesn't work let me know.
[TODO: make the migration tool fix up gitweb and daemon control also...]
Migrating from gitosis to gitolite is fairly easy, because the basic design is
the same.
There's only one thing that might trip up people: the userid. Gitosis uses
`gitosis`. Gitolite can use any userid you want; most of the documentation
uses `git`, while DEB/RPM packages use `gitolite`.
Here are the steps on the server:
* (as 'gitosis' on the server) **Rename** `~/.ssh/authorized_keys` to
something else so that no one can accidentally push while you're doing
this.
* (as 'gitosis' on the server) For added safety, **delete** the post-update
hook that gitosis-admin installed
rm ~/repositories/gitosis-admin.git/hooks/post-update
or at least rename it to `.sample` like all the other hooks hanging
around, or edit it and comment out the line that calls `gitosis-run-hook
post-update`.
* (as 'gitosis' on the server) If you already use the `update` hook for some
reason, **rename** it (on each individual repository that has it) to
`update.secondary`. This is because gitolite uses the update hook for
checking write access.
* (as 'root' on the server) copy all of `~/repositories` to the gitolite
hosting user's home directory. Something like
cp -a /home/gitosis/repositories /home/git
chown -R git.git /home/git/repositories
* (as 'root' and/or 'git' on the server) Follow instructions to install
gitolite; see the [install document][install]. Make sure that you **don't**
change the default path for `$REPO_BASE` if you edit the config file!
This will give you a gitolite config that has the required entries for the
"gitolite-admin" repo.
Now, log off the server and get back to the client. All subsequent
instructions are to be read as "on gitolite admin's workstation".
* **clone** the new gitolite-admin repo to your workstation. (You already
have a clone of the gitosis-admin repo so now you have both).
* **convert** your gitosis config file and append it to your gitolite config
file. Substitute the path for your gitosis-admin clone in `$GSAC` below,
and similarly the path for your gito**lite**-admin clone in `$GLAC`.
(The gl-conf-convert program is a standalone program that you can bring
over from any gitolite clone; you don't have to install all of gitolite on
your workstation to use this):
./gl-conf-convert < $GSAC/gitosis.conf >> $GLAC/conf/gitolite.conf
Be sure to check the file to make sure it converted correctly. Then
remove the entry for the 'gitosis-admin' repo. You do not need it here
and it may cause confusion.
* **copy** the keys from gitosis's keydir (same meanings for GSAC and GLAC)
cp $GSAC/keydir/* $GLAC/keydir
If your gitosis-admin key was `you@machine.pub`, and you supplied the same
one to gitolite's gl-setup program as `you.pub` when you installed
gitolite, then you should remove `you@machine.pub` from the new keydir
now. Otherwise you will have 2 pubkey files (`you.pub` and
`you@machine.pub`) which are identical, which is *not* a good idea.
Similarly, you should replace all occurrences of `you@machine.pub` with
`you` in the `conf/gitolite.conf` file.
* **IMPORTANT**: if you have any users with names like `user@foo`, where the
part after the `@` does *not* have a `.` in it (i.e., does not look like
an email address), you need to change them, because gitolite uses that
syntax for [enabling multi keys][oldmultikeys].
You have two choices in how to fix this. You can change the gitolite
config so that all mention of `user@foo` is changed to just `user`.
Or you can change each occurrence of `user@foo` to, say, `user_foo` *and*
change the pubkey filename in keydir/ also the same way (`user_foo.pub`).
Just to repeat, you do NOT need to do this if the username was like
`user@foo.bar`, i.e., the part after the `@` had a `.` in it, because then
it looks like an email address.
[This][multikey] will tell you more about these nuances. If you can
understand it.
* **IMPORTANT: expand any multi-key files you may have**. [Here][multikey]'s an
explanation of what multi-keys are, how gitosis does them and how gitolite
does it differently.
You can split the keys manually, or use the following code (just
copy-paste it into your xterm after "cd"-ing to your gitolite-admin repo
clone):
wc -l keydir/*.pub | grep -v total | grep -v -w 1 | while read a b
do
i=1
cat $b|while read l
do
echo "$l" > ${b%.pub}@$i.pub
(( i++ ))
done
mv $b $b.done
done
This will split each multi-key file (say "sitaram.pub") into individual
files called "sitaram@1.pub", "sitaram@2.pub", etc., and rename the
original to "sitaram.pub.done" so gitolite won't pick it up.
At this point you can rename the split parts more appropriately, like
"sitaram@laptop.pub" and "sitaram@desktop.pub" or whatever. *Please check
the files to make sure this worked properly*
* Check all your changes to your gitolite-admin clone, commit, and push

720
doc/mirroring.mkd Normal file
View file

@ -0,0 +1,720 @@
# F=mirroring mirroring gitolite servers
Mirroring a repo is simple in git; you just need code like this in a
`post-receive` hook in each repo:
#!/bin/bash
git push --mirror slave_user@mirror.host:/path/to/repo.git
# if running gitolite, the $GL_REPO variable could be useful:
# git push --mirror slave_user@mirror.host:/repo/base/path/$GL_REPO.git
For a lot of people, though, mirroring is more than just 'backup', and their
needs are complex enough that setup is hard.
## #mirrwhy_ why
Gitolite's mirroring used to be very rigid -- one master, any number of
slaves, but the slaves are identical copies of the master. No variations
allowed.
It's now been reworked to be much more flexible, to cater to almost any kind
of setup you need. Here're some advantages:
* **faster reads for everyone**: host a slave in every city you have a
sizable number of developers in, and have them access their local server
instead of hitting the WAN, at least for 'fetch' operations.
* **faster writes for most devs**: one server doesn't have to be the master
for all repos! You can choose where a repo gets "mastered" based on where
the majority of that repo's users are.
This was the single biggest motivation for the re-work of gitolite's
mirroring; the rest of the cool stuff just happened as this feature took
shape.
* **transparent writes**: if all the mirrors are in your control (i.e., you
trust their authentication) pushes to a slave can be transparently
redirected to the master, so developers simply access their local server
for everything. They don't need to know where the master is, so they're
insulated from any changes you make behind the scenes.
This is as close to **active-active** mirroring as you can get without
worrying about race conditions and similar problems.
* **partial mirroring**: all repos don't have to go all mirrors. You can
choose not to mirror a repo at all, or mirror it only to certain servers.
This could be due to any reason: repo too big/high-traffic for the server,
repo has crypto code and the server is in a non-free country, repo has no
local users at that site, or (in autonomous setups) the server admin
simply doesn't want to mirror this specific repo.
* **late mirroring**: if you're ok with the lag, you can have some mirrors
updated only at certain times of the day, (with a simple command), instead
of every time a push happens.
* **autonomous mirrors**: finally, your mirrors don't have to be totally
under your control. They can be owned by someone else, and you negotiate
your mirroring with them.
As you can see, this is a bit more than a backup solution ;-)
## RULE NUMBER ONE!
**RULE OF GIT MIRRORING: users should push directly to only one server**! All
the other machines (the slaves) should be updated by the master server.
If a user pushes directly to one of the slaves, those changes will get wiped
out on the next mirror push from the real master server.
Corollary: if the primary went down and you effected a changeover, you must
make sure that the primary does not come up in a push-enabled mode when it
recovers.
**Getting around rule number one**: see the section on "redirecting pushes"
later.
## concepts and terminology
Servers can host 3 kinds of repos: master, slave, and local.
* A repo can be a **master** on one and only one server. A repo on its
"master" server is a **native** repo, on slaves it is "non-native".
* A **slave** repo cannot be pushed to by a user. It will only accept
pushes from a master server. (Exception: see the "redirecting pushes"
section later)
* A **local** repo is not involved in mirroring at all, in either direction.
## setting up mirroring
### F=mirrcautions IMPORTANT cautions
* For reasons given in the 'discussion' section later, the mirroring process
will never *create* a repo on the receiving side. It has to exist, and be
willing to accept pushes from the master.
In particular, this means that repositories created by end-users ("wild"
repos) *need to be explicitly created* on the mirror (preferably by the
same user, assuming his ssh key works there as well). Once the repo has
been created on the slave, subsequent pushes will be mirrored correctly.
* This process will *only* mirror your git repositories, using `git push
--mirror`. It will *not* mirror log files, and repo-specific files like
`gl-creater` and `gl-perms` files, or indeed anything that was manually
created or added (for example, custom config entries added manually
instead of via gitolite).
None of these affect actual repo contents of course, but they could be
important, (especially the gl-creator, although if your wildcard pattern
had "CREATOR" in it you can recreate those files easily enough anyway).
* This document has been tested using a 3-server setup, all installed using
the *non-root* method (see doc/1-INSTALL.mkd). However, the process is
probably not going to be very forgiving of human error -- like anything
that is this deep in "system admin" territory, errors are likely to be
costly. If you're the kind who hits enter first and then thinks about
what he typed, you're in for some fun times ;-)
On the plus side, everything we do is done using git commands, so things
are never *really* lost until you do a `git gc`.
* Mirroring has *not* been, and will not be, tested with gitolite installed
using the deprecated 'from-client' method. Please use one of the other
methods.
* Also, this has *not* been tested with smart-http. I'm not even sure it'll
work; http is very fiddly to get right. If you want mirroring, at least
your server-to-server comms should be over ssh.
* Finally, this method uses repo-specific `git config` variables to store
the mirroring information. Please read the **WARNING** in the
documentation on [git config commands][rsgc] if you wish to **delete** one
of those lines.
### F=mirrsetup setup and usage
#### server level setup
To start with, assign each server a short name. We will use 'frodo', 'sam',
and 'gollum' as examples here.
1. Generate ssh keys on each machine. Copy the `.pub` files to all other
machines with the appropriate names. I.e., frodo should have sam.pub and
gollum.pub, etc.
**Warning**: server keys are different from user keys. Do NOT attempt to
(re-)use a server key for normal gitolite operations, as if the server
were a normal "user"; it won't work.
2. Install gitolite on all servers, under some 'hosting user' (we'll use
`git` in our examples here). You need not use the same hosting user on
all machines.
It is not necessary to use the same "admin key" on all the machines.
However, if you do plan to mirror the gitolite-admin repo also, they will
eventually become the same anyway. In our example, frodo does mirror the
admin repo to sam, but not to gollum. (Can you really see frodo or sam
trusting gollum?)
3. Now copy `hooks/common/post-receive.mirrorpush` from the gitolite source,
and install it as a custom hook called `post-receive`; see
[here][customhooks] for instructions.
4. Edit `~/.gitolite.rc` on each machine and add/edit the following lines.
The `GL_HOSTNAME` variable **must** have the correct name for that host
(frodo, sam, or gollum), so that will definitely be different on each
server. The other line can be the same, or may have additional patterns
for other `git config` keys you have previously enabled. See [here][rsgc]
and the description for `GL_GITCONFIG_KEYS` in [this][rcsecurity] for details.
$GL_HOSTNAME = 'frodo'; # will be different on each server!
$GL_GITCONFIG_KEYS = "gitolite.mirror.*";
(Remember the "rc" file is NOT mirrored; it is meant to be site-local).
Note: if `GL_HOSTNAME` is undefined, you cannot push to repos which have
the 'gitolite.mirror.master' config variable set. (See 'details' section
below for more info on this variable).
<font color="gray">
> If you wish, you can also add this hostname information to the
> `GL_SITE_INFO` variable in the rc file. See the rc file documentation
> for more on that.
</font>
5. On each machine, add the keys for all other machines. For example, on
frodo you'd run these two commands:
gl-tool add-mirroring-peer sam.pub
gl-tool add-mirroring-peer gollum.pub
6. Create "host" aliases on each machine to refer to all other machines. See
[here][sshhostaliases] for what/why/how.
The host alias for a host (in other machines' `~/.ssh/config` files) MUST
be the same as the `GL_HOSTNAME` in the referred host's `~/.gitolite.rc`.
Gitolite mirroring **requires** this consistency in naming; things will
NOT work otherwise.
For example, if machine A's `~/.gitolite.rc` says `$GL_HOSTNAME =
'frodo';`, then all other machines must use a host alias of "frodo" in
their `~/.ssh/config` files to refer to machine A.
Once you've done this, each host should be able to reach the other hosts and
get a response back. For example, running this on sam:
ssh frodo info
should get you
Hello sam, I am frodo.
Check this command from *everywhere to everywhere else*, and make sure you get
expected results. **Do NOT proceed otherwise.**
#### repository level setup
Setting up mirroring at the repository level instead of at the "entire server"
level gives you a lot of flexibility (see "discussion" section below).
The basic idea is to use `git config` variables within each repo (gitolite
allows you to create them from within the gitolite.conf file so that's
convenient), and use these to specify which machine is the master and which
machines are slaves for the repo.
Let's say frodo and sam are internal servers, while gollum is an external (and
therefore less trusted) server that has agreed to help us out by mirroring one
of our high traffic repos. We want the following setup:
* the "gitolite-admin" repo, as well as an internal project repo called
"ip1", should be mastered on frodo and mirrored to sam.
* internal project "ip2" has almost all of its developers closer to sam, so
it should be mastered there, and mirrored on frodo.
* an open source project we manage, "os1", should be mastered on frodo and
mirrored on both sam and gollum.
So here's how our example would go:
1. Clone frodo's and sam's gitolite-admin repos to your workstation, then add
the following lines to both their gitolite.conf files:
repo ip1 gitolite-admin
config gitolite.mirror.master = "frodo"
config gitolite.mirror.slaves = "sam"
repo ip2
config gitolite.mirror.master = "sam"
config gitolite.mirror.slaves = "frodo"
You also need normal access control lines for ip1 and ip2; I'm assuming
you already have them elsewhere, at least on frodo. (What you have on sam
won't matter in a few minutes, as you will see!)
Commit and push these changes.
2. There are a couple of quirks to keep in mind when you make changes to the
gitolite-admin repo's config.
* the first push will create the `git config` entries required, but by
then it is too late to *act* on them; i.e., actually do the mirroring.
If there were any older values, like a different list of slaves
perhaps, then those would be in effect.
This is largely because git invokes post-receive before post-update.
In theory I can work around this but I do not intend to.
Anyway, this means that after the 2 pushes, you have to make a dummy
push from frodo:
git commit --allow-empty -m empty; git push
which gets you something like this amidst the other messages:
remote: (25158&) frodo ==== (gitolite-admin) ===> sam
telling you that frodo is sending gitolite-admin to sam in the
background.
* the second quirk is that your clone of server sam's gitolite-admin
repo is now completely out of date, since frodo has overwritten it on
the server. You have to 'cd' to that clone and do this:
git fetch
git reset --hard origin/master
2. That completes the setup of the gitolite-admin and the internal project
repos. We'll now setup things for the open source project, "os1".
On frodo's gitolite-admin clone, add the following lines to
`conf/gitolite.conf`, then commit and push:
repo os1
config gitolite.mirror.master = "frodo"
config gitolite.mirror.slaves = "sam gollum"
Also, send the same lines to gollum's administrator and ask him to add
them into his conf/gitolite.conf file, commit, and push.
### F=mirrsync commands to (re-)sync mirrors
You don't have to put all the slaves in `gitolite.mirror.slaves`. For
example, let's say you have some repos that are very active, and two of your
mirrors that are halfway across the world are getting pushed very frequently.
But you don't need those mirrors to be that closely updated, perhaps *because*
they are halfway across the world and those guys are asleep ;-)
Or maybe there was a network glitch and even the default slaves are now
lagging, so they need to be manually synced.
Or a slave realised that one of its repos is lagging for some reason, and
wants to request an immediate update.
Whatever the reason, you need ways to sync a repo from a command line. Here
are ways to do that:
1. On the master server, you can start a **background** job to mirror a repo.
The command/syntax is
gl-mirror-shell request-push reponame [list of keys/slaves]
The list at the end is optional, and can be a mix of slave names or your
own gitolite mirror config keys. (Yes, you can have any key, named
anything you like, as long as it starts with `gitolite.mirror.`).
If the list is not supplied, the `gitolite.mirror.slaves` key is used.
Keys can have values that in turn contain a list of keys/slaves. The list
is recursively *expanded* but recursion is not *detected*. Order is
preserved while duplicates are removed. If you didn't get that, see the
example :-)
**Warning**: the `gitolite.mirror.slaves` key should have only hosts, no
keys, in it.
The program exits with a return value of "1" if it found no slaves in the
list passed, otherwise it fires off the background job, prints an
informative message, and exits with a return value of "0".
We'll take an example. Let's say your gitolite config file has this:
repo ip1
config gitolite.mirror.master = "frodo"
config gitolite.mirror.slaves = "sam merry pippin"
config gitolite.mirror.hourly = "sam legolas"
config gitolite.mirror.nightly = "gitolite.mirror.hourly gimli"
config gitolite.mirror.all = "gitolite.mirror.nightly gitolite.mirror.hourly gitolite.mirror.slaves"
Then the following commands have the results described in comments:
gl-mirror-shell request-push ip1
# which is the same as:
gl-mirror-shell request-push ip1 gitolite.mirror.slaves
# pushes to sam, merry, pippin
gl-mirror-shell request-push ip1 gollum
# pushes only to gollum. Note that gollum is not a member of any of
# the slave lists we defined.
gl-mirror-shell request-push ip1 gitolite.mirror.slaves gollum
# pushes to sam, merry, pippin, gollum
gl-mirror-shell request-push ip1 gitolite.mirror.slaves gitolite.mirror.hourly
# pushes to sam, merry, pippin, legolas
gl-mirror-shell request-push ip1 gitolite.mirror.all
# pushes to sam, legolas, gimli, merry, pippin
The last two examples show recursive expansion with order-preserving
duplicate removal (hey there's now a published conference paper on
gitolite, so we have to use jargon *somewhere* or they won't accept
follow-on papers!).
If you do something like this:
config gitolite.mirror.nightly = "gimli gitolite.mirror.nightly"
or this:
config gitolite.mirror.nightly = "gimli gitolite.mirror.hourly"
config gitolite.mirror.hourly = "legolas gitolite.mirror.nightly"
you deserve what you get.
2. If you want to start a **foreground** job, the syntax is `gl-mirror-shell
request-push ip1 -fg gollum`. Foreground mode requires one (and only one)
slave name -- you cannot send to an implicit list, nor to more than one
slave.
3. Cronjobs and custom mirroring schemes are now very easy to do. Use either
of the command forms above and write a script around it. Appendix A
contains an example setup.
4. Once in a while a slave will realise it needs an update, and wants to ask
for one. It can run this command to do so:
ssh sam request-push ip2
If the requesting server is not one of the slaves listed in the config
variable gitolite.mirror.slaves on the master, it will be rejected.
This is always a foreground push, reflecting the fact that the slave may
want to know why their push errored out or didn't work last time or
whatever.
## #ad/m-dtls details
### F=mirrconf the `conf/gitolite.conf` file
One goal I have is to minimise the code changes to "core" gitolite due to
this, so all repo-specific mirror settings are stored as `git config`
variables (you know you can specify git config variables in the gitolite
config file right?). These are:
* `gitolite.mirror.master`
The name of the server which is the master for this repo. Each server
will compare this with `$GL_HOSTNAME` (from its own rc file) to
determine if it's the master or a slave. Here're the possible values:
* **undefined** or `local`: this repo is local to this server
* **same** as `$GL_HOSTNAME`: this server is the "master" for this
repo. (The repo is "native" to this server).
* **not same** as `$GL_HOSTNAME`: this server is a "slave" for the
repo. (The repo is a non-native on this server).
* `gitolite.mirror.slaves`
Ignored for non-native repos. For native repos, this is a space-separated
list of servers to push to from the `post-receive` hook.
Clearly, you can have different sets of slaves for different repos (again,
see "discussion" section later for more on this).
* `gitolite.mirror.redirectOK`
See the section on "redirecting pushes"
* In addition, you can create your own slave lists, named whatever you want,
except they have to start with `gitolite.mirror.`. The section on
"commands to (re-)sync mirrors" has some examples.
### F=mirrredirect redirecting pushes
**Please read carefully; there are security implications if you enable this
for mirrors NOT under your control**.
When a user pushes to a non-native repo, it is possible to transparently
redirect the push to the correct master server. This is a very neat feature,
because now all your users just use one URL (the mirror nearest to them).
They don't need to know where the actual master is, and more importantly, if
you and the other admins change it, they don't need to know it changed!
The `gitolite.mirror.redirectOK` config variable decides where this
redirection is OK. If it is set to 'true', any valid 'slave' can redirect an
incoming non-native push from a developer. Otherwise, it contains a list of
slaves that are permitted to redirect pushes (this might happen if you don't
trust some of your slaves enough to accept a redirected push from them).
**Warning**: like `gitolite.mirror.slaves`, this key also should have only
hosts, no keys, in it.
This check needs to pass on both the master and slave servers; both have a say
in deciding if this is allowed. (The master may have real reasons not to
allow this; see below. I cannot think of any real reason for the *slave* to
disable this, but it's there in case some admin doesn't like it).
There are some potential issues that you MUST consider before enabling this:
* (security) If the slave and master server are so different or autonomous
that a user, say "alice", on the slave is not guaranteed to be the same
one as "alice" on the master, then the master admin should NOT enable this
feature.
This is because, in this scheme, authentication happens on the slave, but
authorisation is on the master. The slave-authenticated userid (alice) is
passed to the master.
(If you know ssh well enough, you know that the ssh authentication has
already happened, so all we can do is ensure authorisation happens with
whatever username we know so far).
* If your slave is out of sync with the master for whatever reason, then the
user will get confusing results. A `git fetch` may say everything is
upto-date but the push fails saying it is not a fast-forward push. (Of
course there's a way to fix this; see the "commands to (re-)sync mirrors"
section above).
* We cannot redirect non-git commands like ADC, setperms, etc because we
don't really have a way of knowing what repo he's talking about (different
commands have different syntaxes, some have more than one reponame...).
Any user who needs to do that should access the end server directly. It
should be easy enough to write an ADC to do the forwarding, in case the
slave server is the only one that can reach the real master due to network
or firewall setup.
Ideally, I recommend that ad hoc repos not be mirrored at all. Keep
mirroring for "blessed" repos only.
## example setups
Here are some samples of what is possible.
### F=mirrnonauto non-autonomous
In this setup, the slave server is under the same "management" as the master.
All repos, including gitolite-admin are mirrored, and *each slave is an exact
replica of the master*. Since the admin repo is mirrored, authentication info
is identical across all servers, and it is safe to use redirected pushes.
(This was the only type of mirroring possible in the old mirroring code in
gitolite).
Install gitolite on all servers. Then add these lines to the top of all admin
repos and push them all. This sets up the config for mirroring all repos.
repo @all
config gitolite.mirror.master = "frodo"
config gitolite.mirror.slaves = "sam gollum"
Once they're all pushed, sync the admin repo once:
# on master server
gl-mirror-shell request-push gitolite-admin
Since authentication is also being mirrored, you can take advantage of
redirected pushing if you wish:
repo @all
config gitolite.mirror.redirectOK = "true"
### F=mirrnonautolocal non-autonomous with local repos
As above, but you want to allow each slave server to have some repos be
"local" to the server (not be mirrored), for whatever reason. Different slaves
may have different needs, so this really means that the same gitolite.conf
should behave differently on each server -- something which till now was
impossible.
Well what's life without a new feature once in a while? The string "HOSTNAME"
is now specially treated in an include filename. If it is seen without any
alphanumeric characters or underscores next to it on either side, it is
replaced by the value of `$GL_HOSTNAME`.
Setup the config as in the previous setup except that you shouldn't use repo
@all now; instead, you'll have to name the repos to be mirrored in some way.
Make sure gitolite-admin is in this list. Complete the mirror setup (including
the first-time sync command) like before.
Now add the line include "HOSTNAME.conf" to the end of conf/gitolite.conf, and
create new files, conf/frodo.conf, conf/sam.conf, etc., with appropriate
content.
That's it. When this config is pushed, each machine will have an effective
config that consists of the main file, with the correct HOSTNAME.conf included
(and all the others ignored) when the include statement is reached.
### F=mirrsemiauto semi-autonomous
So far, the "central" admin still has control over the gitolite.conf file and
all repos created. Sometimes it's easier to give control over parts of the
configuration to people at the mirror sites. To keep it simple, each admin
will be able to do whatever they want to directories within a subdirectory of
the same name as the hostname.
You can combine the "HOSTNAME" feature above with [delegation][deleg]. Let's
say the admin for sam is a user called "gamgee", and the admin for gollum is
"smeagol".
Add this to your conf file:
@sam = sam/..*
@gollum = gollum/..*
Then use NAME/ rules (see the delegation doc for details) and allow gamgee to
write only conf/sam.conf, and smeagol to write only conf/gollum.conf.
Now in the main config file, at the end (or wherever you wish), add one line:
subconf "HOSTNAME.conf"
### F=mirrauto autonomous
In many ways this is the simplest setup.
The slave server belongs to someone else. Their admin has final say on what
goes into their gitolite-admin repo and thus their server's config. The
gitolite-admin repo is NOT mirrored, and mirroring of individual repos (i.e.,
actual config lines included) is by negotiation/agreement between the admins.
Authentication info is not common. The master has no real control over who can
read the repos on the slave. Allowing redirected pushes is not a good idea,
unless you have other means of trust (administrative, contractual, legal,
etc.)
Best for open source projects with heavy "fetch" load compared to "push".
## F=mirrdisc discussion
### problems with the old mirroring model
The old mirroring model had a single server as the master for *all*
repositories. Slaves were effectively only for load-balancing reads, or for
failover if the master died.
This is not good enough for corporate setups where the developers are spread
fairly evenly across the world. Some repos need to be closer to some teams
(NUMA is a good analogy).
A model where different repos are "mastered" in different cities is much more
efficient here.
The old model had other rigidities too, though they're not really *problems*,
as such:
* the slaves are just slaves; they can't have any "local" repos.
* a slave had to carry *all* repos; it couldn't choose to carry just a
subset.
* it implicitly assumed all the mirrors were under the same admin, and that
the gitolite-admin repo was itself mirrored too.
### the new mirroring model
In the new model, servers can be much more independent and autonomous than in
the old model. (Don't miss the side note in the 'repository level setup'
section if you prefer the old model).
The new model has a few pros and cons. The pros come from the flexibility and
freedom that mirrors servers get, and the cons come from authorisation being
more rigorously checked (for example, a slave will only accept a push if *its*
configuration also says that the sending server is indeed the master for this
repo).
* A mirroring operation will not *create* a repo on the mirror; it has to
exist before a push happens on the master. Typically, the admin on the
slave must create the repo by adding the appropriate lines in his config.
If your setup is not autonomous (i.e., you're mirroring the admin repo as
well) then this happens automatically for normal repos. However,
*wildcard repos still won't work as seamlessly as in the old model*; see
the first bullet in the 'IMPORTANT cautions' section earlier.
* The gitolite-admin repo (and config) need not be mirrored. This allows
the slave server admin to create site-local repos, without forcing him to
create a second gitolite install for them.
(Site-local repos are useful for purely local projects that need
not/should not be mirrored for some reason, or ad-hoc personal repos that
developers create for themselves, etc.)
* Servers can choose to mirror a subset of the repos from one of the bigger
servers.
In the open source world, you can imagine more popular repos (or more
popular parts of huge projects like KDE) having more mirrors. Or
substitute "more popular" with "larger in size" if you wish
(FlightGear-data anyone?)
In the corporate world it could help with jurisdiction issues if the
mirror is in a different country with different laws.
I'm sure people will find other uses for this. And I'm *positive* the
pros will outweigh the cons. If you don't like it, follow the suggestion
in the side note somewhere up above, and just forget this feature exists
:-)
## appendices
### F=mirrcron appendix A: example cronjob based mirroring
Let's say you have some repos that are very active. You're pushing halfway
across the world every few seconds, but those slaves do not need to be that closely
updated, perhaps *because* they are halfway across the world and those guys
are asleep ;-)
You'd like to update them once an hour instead. Here's how you might do that.
First add this line to the configuration for those repos:
config gitolite.mirror.hourly = "slave1 slave2 slave3"
Then write a cron job that looks like this (untested).
#!/bin/bash
REPO_BASE=`${0%/*}/gl-query-rc REPO_BASE`
cd $REPO_BASE
find . -type d -name "*.git" -prune | while read r
do
# get reponame as gitolite knows it
r=${r:2}
r=${r%.git}
gl-mirror-shell request-push $r gitolite.mirror.hourly
# that command backgrounds the push, so you'd best wait a few seconds
# before hitting the next one, otherwise you'll have all your repos
# going out at once!
sleep 10
done
### F=mirrparanoia appendix B: efficiency versus paranoia
If you're paranoid enough to use mirrors, you should be paranoid enough to
use the `receive.fsckObjects` setting. However, informal tests indicate a
40-50% CPU overhead from this. If you're ok with that, make the appropriate
adjustments to `GL_GITCONFIG_KEYS` in the rc file, then add this to your
gitolite.conf file:
repo @all
config receive.fsckObjects = "true"
Personally, I just set `git config --global receive.fsckObjects true`, since
those servers aren't doing anything else anyway, and are idle for long
stretches of time. It's upto you what you want to do here.

76
doc/mob-branches.mkd Normal file
View file

@ -0,0 +1,76 @@
# F=mob mob branches in gitolite
WARNING: This is hairy stuff. But what's life without a little danger?
WARNING 2: girocco does mob branches quite differently; the controls on what a
mob branch can do are much more fundamental. Here we just trick gitolite into
accepting anonymous ssh connections and pretending they're from a mythical
user called "mob". **This means all the access control is -- as you might
expect -- in the gitolite.conf file, so make sure you don't give the `mob`
user too many rights!**
(tested on Fedora 14; assumes your gitolite server userid is "gitolite" and
install was "non-root" method; adjust according to your environment. If you
need more than this, you should not be enabling mob branches anyway ;-)
[hah! Easy way out of being badgered with questions!]
* create a file called `/tmp/mobshell` (put it somewhere more permanent if
you wish). This file should be `chmod +x` and contain
#!/bin/sh
shift
export SSH_ORIGINAL_COMMAND
SSH_ORIGINAL_COMMAND="$*"
/home/gitolite/bin/gl-auth-command mob
# see one of the lines in ~gitolite/.ssh/authorized_keys for the
# precise location of the gl-auth-command script
* create a user called mob. Give it the same UID number and `$HOME` as your
gitolite server userid, and set the login shell to be the script you just
created. Also delete the password.
id -u gitolite
# returns 503 or something...
useradd -d /home/gitolite -s /tmp/mobshell -u 503 -o mob
passwd -d mob
* make sure you have a recent enough sshd and put these lines at the bottom,
then restart sshd
Match user mob
PermitEmptyPasswords yes
That's it. Now you can add stuff to your gitolite.conf file. Here's some
examples:
* This allows the mob user to do anything to the "mob" branch:
repo foo
RW+ = alice bob
R = eve
RW+ mob$ = mob
# only the mob branch, nothing more
* This is the same, except it can be any branch under "mob/" so you get some
flexibility:
RW+ mob/ = mob
* Girocco allows pushing to the mob branch only if it already exists (that
is, the mob user cannot *create* the mob branch, but if it already exists
he can push to it). Here's how you'd do that in gitolite:
repo foo
RW+C = alice bob
R = eve
RW+ mob$ = mob
* This gives *every* repo a mob branch (be careful!)
repo @all
RW+ mob$ = mob
How do mob users access it? The URLs just look like: `mob@server:repo`
instead of `gitolite@server:repo` That's it!

266
doc/nagp.mkd Normal file
View file

@ -0,0 +1,266 @@
# F=nagp ...not a gitolite problem!
Subtitle: Unix, ssh, git, and gitolite -- recognising the boundaries
**Warning**: Most of this is technical, but some of it is definitely
subjective opinion.
More and more people are being tasked with creating a "server" environment for
git, (and thus being forcibly introduced to gitolite), before they've had a
chance to know git (or even Unix) well enough. As a result, I often get
questions that have **nothing** to do with gitolite, because people don't know
where the boundaries are.
Here're some facts about gitolite:
* gitolite runs on the server. To the client it looks just like any other
ssh-based URL that is using public keys.
* once you connect, it looks just like any other [bare repo][bare] on a
server for normal git operations (clone, fetch, push). Users won't even
*know* it exists if they do only what they're allowed to. (I.e., it
becomes visible only when it has to deny access for some operation)
* even "on disk", except for reserving the `update` hook for its own
purposes and possibly a couple of extra files, the contents of a
gitolite-managed bare repo are the same as a normal bare repo.
A good short term measure is to read this [simple git session][sgs] for
insights into the relationship between the "server" and the "client" in git.
That might help with some of the problems described in this document.
[bare]: http://sitaramc.github.com/concepts/0-terminology.html#working_tree_repository_bare_repository
[sgs]: http://sitaramc.github.com/1-basic-usage/simple-git-session.html
## ssh
Let's get this out of the way first. The *superstar* of the "not a gitolite
problem" category is actually ssh.
Surprised? It is so common that it has [its own document][auth] to tell
you why it is *not* a gitolite problem, while [another one][sts] tries to
help you anyway!
## PATH issues
This is actually related to ssh. When you run commands over ssh, they run
*non*-interactively, and depend on what your shell is, etc., certain shell
startup files are not executed, so some environment variables may be different
than what you see when you log in interactively.
Example: Redmine/Chiliproject using RVM and redmine git hosting can't run
post-receive hook.
[This](https://github.com/ericpaulbishop/redmine_git_hosting/issues/125) has
more info on this specific issue.
It is also useful to remember that *gitolite does not fiddle with the PATH
except for adding the `GIT_PATH` variable if you defined it*.
## git
* first push to a new repo
Even if the error message said "the remote end hung up unexpectedly", this
is not a gitolite problem. If you read the full message, it will probably
be something like this:
No refs in common and none specified; doing nothing.
Perhaps you should specify a branch such as 'master'.
fatal: The remote end hung up unexpectedly
error: failed to push some refs to '/tmp/tmp-repos/bare.git'
(To be fair, this message is still not clear enough; even a careful reader
might end up trying `git push master` instead of `git push origin
master`.)
* client-side versus server-side hooks
Just because you saw something in gitolite's docs about hooks, it doesn't
mean gitolite can make a "post-checkout" hook work!
You need to read man githooks again to understand the difference between
hooks that run on your local repo in response to local commands (like
commit, checkout), and remote hooks that run on the server in response to
a client-initiated operation (like push).
An equally wrong expectation is that a clone or a pull will also bring
along hooks from the server! Sorry, but there is nothing in git that
allows the *server* to do something on the *client* -- if there were, it
would be a huge security issue.
What you can do in gitolite is to write yourself a hook that checks every
commit in a push for compliance and rejects the push (*all* the commits in
the push, mind!) if it doesn't comply. However, other than telling you
how to get your hook to trigger, the actual code in that hook is out of
scope for me.
* Jenkins integration
I don't know much about CI systems, but I'm pretty sure they run off of
some hook or other, but gitolite may already be using those hooks. The
section on hook chaining [here][hookchaining] shows you how to run your own
hooks.
In short, CI integration is no more a gitolite problem than any other
purpose for which git's hooks can be used.
## windows
I'm *interested* in making sure it works fine with Windows, simply because I
have colleagues at work who use it. But that doesn't mean I can help you; I
just don't know enough to help you. (And if you even breathe the words
"putty" or "plink" I will totally tune you out!)
I do have people using it happily on Windows with Eclipse, Visual Studio, and
God alone knows what else, so I know it *can* (be made to) work.
So, hang in there... it'll all work out eventually.
## apple
Weirdly enough, this is the one thing that Steve Ballmer and I probably agree
on, so I won't elaborate on that ;-)
It seems to me though, that many recent reports of "weird" behaviour reported
have come from Macs. Yet another reason for me to back off with an apology.
## just say NO!
These are the things I won't do, for various reasons, mostly technical, with a
smattering of some subjective stuff. If you've been hit by one of these, and
disagree with me -- well that's why gitolite is GPL. As long as you satisfy
the GPL, you can simply "fork off" ;-)
* using a database backend
Someone wanted help rewriting gitolite to use a database instead of a perl
hash.
I think that's a very bad idea. Show me an install where gitolite does
not scale and I'll fix it without using a damn database. I *chose* the
perl hash very deliberately and for very specific reasons; using a DB
would kill all that.
* symlinks within `REPO_BASE`
Someone wanted to put the actual repos somewhere else and create a symlink
to them inside gitolite's `REPO_BASE`.
No. Gitolite assumes all of `REPO_BASE` is in its control, and nothing
else outside is (barring the admin directory `~/.gitolite` and the rc file
`~/.gitolite.rc`).
You could argue that this is secure enough if the admin knows what he is
doing, but I'm not buying that. Some odd screwup involving symlinks will
show up some day and gitolite will get blamed.
* deleting environment variables copied from client session
This same guy wanted me to add code to delete certain environment
variables at startup because "the openssh servers in the linux
distribution that [he] use[s], are configured to copy `GIT_*` variables to
the remote session".
This is wrong on so many levels it's almost plonk-able!
* using `cp` instead of `ln`
Guy has an NTFS file system mounted on Linux. So... no symlinks (an NTFS
file system on Windows works fine because msysgit/cygwin manage to
*simulate* them. NTFS mounted on Linux won't do that!)
He wanted all the symlink stuff to be replaced by copies.
No. Way.
* non-bare repos on the server
Some guy gave me a complicated spiel about git-svn not liking bare repos
or whatever. I tuned off at the first mention of those 3 letters so I
don't really know what the actual problem was.
But it doesn't matter. Even if someone (Ralf H) had not chipped in with a
workable solution, I still would not do it. A server repo should be bare.
Period.
### behind my back
Some of the "Just say NO" items are from situations where someone or something
changes stuff behind gitolite's back. I am particularly unsympathetic to this
sort of thing.
* incomplete ownership of `REPO_BASE`
This guy had a repo-base directory where not all of the files were owned
by the git user. As a result, some of the hooks did not get created. He
claimed my code should detect OS-permissions issues while it's doing its
stuff.
No. I refuse to have the code constantly look over its shoulder making
sure fundamental assumptions are being met.
* empty template directory
(See man git-init for what a template directory is).
The same guy with the symlinks and the environment variables (so this is
his third appearance in this list!) had an empty template directory
because he "does not like to have sample hooks in every repository". So
naturally, the hooks directory does not get created when you run a `git
init`. He expects gitolite to compensate for it.
Granted, it's only a 1-line change. But again, this falls under
"constantly looking over your shoulder to double check fundamental
assumptions". Where does it end?
* per-repo umask setting
Some people believe that gitolite should have code to compensate for a
"potential" failure of gitweb or httpd to restrict access as designed.
Sorry. Not in "core" at least. This is yet another of those "constantly
looking over your shoulder" examples. Why should it be gitolite's problem
to compensate if gitweb or httpd misbehave? Yes, I know all about layers
of security and defense in depth, thank you but no.
<font color="gray">
> I gave them a solution that involves adding `config
> core.sharedRepository = 0750` in the gitolite.conf file for those
> repos, then doing a one-time fixup for newly created repos. But they
> decided they needed to patch gitolite and they're going with it.
> I'm not too worried. These guys ran a HEAVILY patched gitosis for
> years; they're probably used to it.
</font>
## that's outrageous
This section is for really outrageous stuff.
* clearing out a repo the long/wrong way
This guy tells me he often needs to clear out a repo. How? He first
manually deletes the repo on the server, then pushes a dummy commit to the
admin repo to let gitolite re-create it.
Of course, this second step is "annoying" so he mailed me and told me
there has to be a better way!
I asked him why he didn't try `git push -f` and he said he didn't know he
could do that. I'm NOT joking. I wish I were! (Yes, we all know that
`git push -f` is not the same as delete/re-create, but the point is that
he really didn't need it; he just assumed that's the only way to force his
commits to the remote.)
* can connect to github, can't connect to gitolite
So this mac-fan went on about his great IDE (CodeX or something) and how
it works with github but no luck with gitolite. Therefore he comes and
asks on \#gitolite. (I give him bonus points for telling people on TWO
channels that it is "THE best ide").
The actual error? "host unreachable".
In usenet terms... `*PLONK*`!

53
doc/packaging.mkd Normal file
View file

@ -0,0 +1,53 @@
# F=packaging packaging gitolite
Here's how you'd package gitolite. In the following description, location "X"
can be, say, `/usr/share/gitolite/conf` or some such, and similarly location
"Y" can be perhaps `/usr/share/gitolite/hooks`. It's upto your distro
policies where they are.
**Step 1**: Clone the gitolite repo and run the make command inside the clone
git clone git://github.com/sitaramc/gitolite.git
cd gitolite
make pu.tar # or "make master.tar" or "make v1.2.tar" etc
Then you explode the tar file in some temporary location.
*Alternatively, you can `git checkout` the tag or branch you want, and run
this command in the clone directly*:
git describe --tags --long > conf/VERSION
**Step 2**: Now make the following changes (no trailing slashes in the
location values please):
* `src/gl-setup` should have the following line:
GL_PACKAGE_CONF="X"
* `conf/example.gitolite.rc` should have the following lines:
$GL_PACKAGE_CONF="X";
$GL_PACKAGE_HOOKS="Y";
**Step 3**: Move (or arrange to move) the files to their proper locations as
given below:
* everything in "src" goes somewhere on the PATH
* everything in "conf" goes to location "X"
* everything in "hooks" goes to location "Y"
**Step 4**: There is no step 4. Unless you count telling your users to run
`gl-setup` as a step :)
On the initial install (urpmi, yum install, or apt-get install), you could
also choose to setup a userid called "gitolite", and run "gl-setup" as that
user; however I do not know how you would come up with the initial pubkey that
is needed. Anyway, the point is that the "gitolite" user is no more special
than any other in terms of hosting gitolite. Any user can host gitolite on
his userid by just running "gl-setup".
When you upgrade, just overwrite all the files; it'll all just work. In fact,
other than the initial "gl-setup" run, the only time a gitolite hosting user
has to actually do anything is to edit their own `~/.gitolite.rc` file if they
want to enable or disable specific features.

173
doc/progit-article.mkd Normal file
View file

@ -0,0 +1,173 @@
# F=progit (master copy of progit chapter on gitolite)
## Gitolite ##
Note: the latest copy of this section of the ProGit book is always available within the [gitolite documentation][progit]. The author would also like to humbly state that, while this section is accurate, and *can* (and often *has*) been used to install gitolite without reading any other documentation, it is of necessity not complete, and cannot completely replace the enormous amount of documentation that gitolite comes with.
Git has started to become very popular in corporate environments, which tend to have some additional requirements in terms of access control. Gitolite was originally created to help with those requirements, but it turns out that it's equally useful in the open source world: the Fedora Project controls access to their package management repositories (over 10,000 of them!) using gitolite, and this is probably the largest gitolite installation anywhere too.
Gitolite allows you to specify permissions not just by repository, but also by branch or tag names within each repository. That is, you can specify that certain people (or groups of people) can only push certain "refs" (branches or tags) but not others.
### Installing ###
Installing Gitolite is very easy, even if you don't read the extensive documentation that comes with it. You need an account on a Unix server of some kind; various Linux flavours, and Solaris 10, have been tested. You do not need root access, assuming git, perl, and an openssh compatible ssh server are already installed. In the examples below, we will use the `gitolite` account on a host called `gitserver`.
Gitolite is somewhat unusual as far as "server" software goes -- access is via ssh, and so every userid on the server is a potential "gitolite host". As a result, there is a notion of "installing" the software itself, and then "setting up" a user as a "gitolite host".
Gitolite has 3 methods of installation. People using Fedora or Debian systems can obtain an RPM or a DEB and install that. People with root access can install it manually. In these two methods, any user on the system can then become a "hosting user". However, most people don't want/need to install gitolite system-wide -- they can just install it within one specific hosting user.
We will describe this last method in this article; for the other methods please see the documentation.
To begin, create a user called `git` on your server and login to this user. Copy your ssh pubkey (a file called `~/.ssh/id_rsa.pub` if you did a plain `ssh-keygen` with all the defaults) from your workstation, renaiming it to `YourName.pub`. Then run these commands:
git clone git://github.com/sitaramc/gitolite
gitolite/src/gl-system-install
gl-setup -q ~/YourName.pub
# for example, I would run 'gl-setup -q ~/sitaram.pub'
Finally, back on your workstation, run `git clone git@server:gitolite-admin`.
And you're done! Gitolite has now been installed on the server, and you now have a brand new repository called `gitolite-admin` in your workstation. You administer your gitolite setup by making changes to this repository and pushing. See [adding users and repos][add] to start with.
### Customising the Install ###
While the default, quick, install works for most people, there are some ways to customise the install if you need to. If you omit the `-q` argument, an editor pops up with a file for you to edit, so you can change certain server-side parameters, such as the location of the actual repositories. This "rc" file is documented in [doc/gitolite.rc.mkd][rc] so you should be able to make any changes you need quite easily, save it, and continue.
### Config File and Access Control Rules ###
Once the install is done, you switch to the `gitolite-admin` repository (placed in your HOME directory) and poke around to see what you got:
$ cd ~/gitolite-admin/
$ ls
conf/ keydir/
$ find conf keydir -type f
conf/gitolite.conf
keydir/sitaram.pub
$ cat conf/gitolite.conf
#gitolite conf
# please see doc/gitolite.conf.mkd for details on syntax and features
repo gitolite-admin
RW+ = sitaram
repo testing
RW+ = @all
Notice that "sitaram" (the name of the pubkey in the gl-setup command you used earlier) has read-write permissions on the `gitolite-admin` repository as well as a public key file of the same name.
The config file syntax for gitolite is documented in [doc/gitolite.conf.mkd][conf] so we'll only mention some highlights here.
You can group users or repos for convenience. The group names are just like macros; when defining them, it doesn't even matter whether they are projects or users; that distinction is only made when you *use* the "macro".
@oss_repos = linux perl rakudo git gitolite
@secret_repos = fenestra pear
@admins = scott # Adams, not Chacon, sorry :)
@interns = ashok # get the spelling right, Scott!
@engineers = sitaram dilbert wally alice
@staff = @admins @engineers @interns
You can control permissions at the "ref" level. In the following example, interns can only push the "int" branch. Engineers can push any branch whose name starts with "eng-", and tags that start with "rc" followed by a digit. And the admins can do anything (including rewind) to any ref.
repo @oss_repos
RW int$ = @interns
RW eng- = @engineers
RW refs/tags/rc[0-9] = @engineers
RW+ = @admins
The expression after the `RW` or `RW+` is a regular expression (regex) that the refname (ref) being pushed is matched against. So we call it a "refex"! Of course, a refex can be far more powerful than shown here, so don't overdo it if you're not comfortable with perl regexes.
Also, as you probably guessed, Gitolite prefixes `refs/heads/` as a syntactic convenience if the refex does not begin with `refs/`.
An important feature of the config file's syntax is that all the rules for a repository need not be in one place. You can keep all the common stuff together, like the rules for all `oss_repos` shown above, then add specific rules for specific cases later on, like so:
repo gitolite
RW+ = sitaram
That rule will just get added to the ruleset for the `gitolite` repository.
At this point you might be wondering how the access control rules are actually applied, so let's go over that briefly.
There are two levels of access control in gitolite. The first is at the repository level; if you have read (or write) access to *any* ref in the repository, then you have read (or write) access to the repository.
The second level, applicable only to "write" access, is by branch or tag within a repository. The username, the access being attempted (`W` or `+`), and the refname being updated are known. The access rules are checked in order of appearance in the config file, looking for a match for this combination (but remember that the refname is regex-matched, not merely string-matched). If a match is found, the push succeeds. A fallthrough results in access being denied.
### Advanced Access Control with "deny" rules ###
So far, we've only seen permissions to be one or `R`, `RW`, or `RW+`. However, gitolite allows another permission: `-`, standing for "deny". This gives you a lot more power, at the expense of some complexity, because now fallthrough is not the *only* way for access to be denied, so the *order of the rules now matters*!
Let us say, in the situation above, we want engineers to be able to rewind any branch *except* master and integ. Here's how to do that:
RW master integ = @engineers
- master integ = @engineers
RW+ = @engineers
Again, you simply follow the rules top down until you hit a match for your access mode, or a deny. Non-rewind push to master or integ is allowed by the first rule. A rewind push to those refs does not match the first rule, drops down to the second, and is therefore denied. Any push (rewind or non-rewind) to refs other than master or integ won't match the first two rules anyway, and the third rule allows it.
You can also use deny rules to hide specific repos from people (or gitweb, or git-daemon, etc.), when you have otherwise allowed them access to *all* repos. For example, a server containing open source repos may nevertheless wish to hide the special 'gitolite-admin' repo from gitweb, even though all the other repos can be made visible:
repo gitolite-admin
- = gitweb daemon
[... other access rules ...]
config gitolite-options.deny-repo = 1
repo @all
R = gitweb daemon
See the documentation for more on this.
### Restricting pushes by files changed ###
In addition to restricting what branches a user can push changes to, you can also restrict what files they are allowed to touch. For example, perhaps the Makefile (or some other program) is really not supposed to be changed by just anyone, because a lot of things depend on it or would break if the changes are not done *just right*. You can tell gitolite:
repo foo
RW = @junior_devs @senior_devs
RW NAME/ = @senior_devs
- NAME/Makefile = @junior_devs
RW NAME/ = @junior_devs
This powerful feature is documented in `conf/example.conf`.
### Personal Branches ###
Gitolite also has a feature called "personal branches" (or rather, "personal branch namespace") that can be very useful in a corporate environment.
A lot of code exchange in the git world happens by "please pull" requests. In a corporate environment, however, unauthenticated access is a no-no, and a developer workstation cannot do authentication, so you have to push to the central server and ask someone to pull from there.
This would normally cause the same branch name clutter as in a centralised VCS, plus setting up permissions for this becomes a chore for the admin.
Gitolite lets you define a "personal" or "scratch" namespace prefix for each developer (for example, `refs/personal/<devname>/*`); see the "personal branches" section in `doc/3-faq-tips-etc.mkd` for details.
### "Wildcard" repositories ###
Gitolite allows you to specify repositories with wildcards (actually perl regexes), like, for example `assignments/s[0-9][0-9]/a[0-9][0-9]`, to pick a random example. This is a *very* powerful feature, which has to be enabled by setting `$GL_WILDREPOS = 1;` in the rc file. It allows you to assign a new permission mode ("C") which allows users to create repositories based on such wild cards, automatically assigns ownership to the specific user who created it, allows him/her to hand out R and RW permissions to other users to collaborate, etc. This feature is documented in `doc/wildcard-repositories.mkd`.
### Other Features ###
We'll round off this discussion with a sampling of other features, all of which, and many more, are described in great detail in the "faqs, tips, etc" and other documents.
**Logging**: Gitolite logs all successful accesses. If you were somewhat relaxed about giving people rewind permissions (`RW+`) and some kid blew away "master", the log file is a life saver, in terms of easily and quickly finding the SHA that got hosed.
**Git outside normal PATH**: One extremely useful convenience feature in gitolite is support for git installed outside the normal `$PATH` (this is more common than you think; some corporate environments or even some hosting providers refuse to install things system-wide and you end up putting them in your own directories). Normally, you are forced to make the *client-side* git aware of this non-standard location of the git binaries in some way. With gitolite, just choose a verbose install and set `$GIT_PATH` in the "rc" files. No client-side changes are required after that :-)
**Access rights reporting**: Another convenient feature is what happens when you try and just ssh to the server. Gitolite shows you what repos you have access to, and what that access may be. Here's an example:
hello sitaram, the gitolite version here is v1.5.4-19-ga3397d4
the gitolite config gives you the following access:
R anu-wsd
R entrans
R W git-notes
R W gitolite
R W gitolite-admin
R indic_web_input
R shreelipi_converter
**Delegation**: For really large installations, you can delegate responsibility for groups of repositories to various people and have them manage those pieces independently. This reduces the load on the main admin, and makes him less of a bottleneck. This feature has its own documentation file in the `doc/` directory.
**Gitweb support**: Gitolite supports gitweb in several ways. You can specify which repos are visible via gitweb. You can set the "owner" and "description" for gitweb from the gitolite config file. Gitweb has a mechanism for you to implement access control based on HTTP authentication, so you can make it use the "compiled" config file that gitolite produces, which means the same access control rules (for read access) apply for gitweb and gitolite.
**Mirroring**: Gitolite can help you maintain multiple mirrors, and switch between them easily if the primary server goes down.
[rcdoc]:http://sitaramc.github.com/gitolite/doc/gitolite.rc.html
[confdoc]:http://sitaramc.github.com/gitolite/doc/gitolite.conf.html

124
doc/report-output.mkd Normal file
View file

@ -0,0 +1,124 @@
# F=info_expand output of the "info" and "expand" commands
Running "ssh git@server info" or "ssh git@server expand" gives you certain
output. This doclet describes the output; you're welcome to help me make it
clearer :)
## #info the "info" command
Usage:
ssh git@server info [optional_pattern [list of users]]
The "info" command shows you all the repos (and repo patterns) in the config
file that you have been given any kind of access to. If you supply an
optional pattern the output will be limited to repos matching that pattern.
If you're an admin you can append a list of users to see their permissions
instead of your own; in this mode the pattern is mandatory, even if you just
use `.` to cheat.
Here is a sample output of the info command. There are 3 columns of
permissions (create, read, and write) in the output, although the first column
is often blank.
$ ssh git@server info
hello sitaram, this is gitolite v2.1-29-g5a125fa running on git 1.7.4.4
the gitolite config gives you the following access:
R SecureBrowse
R W anu-wsd
R W entrans
@R W git-notes
@R W gitolite
R W gitolite-admin
R W indic_web_input
@C R W private/sitaram/[\w.-]+
R W proxy
@C @R W public/sitaram/[\w.-]+
@R_ @W_ testing
R W vkc
### interpreting the output
The meaning of C, R, and W are self-explanatory, but they may be prefixed or
suffixed by a symbol:
* an `@` prefix means "@all" users have been given this permission
repo foo
R = @all
* a `#` prefix means this user is a "superuser" (think root's shell prompt)
and so has access to `@all` repos. Which means you'll see this prefix
(or, in some cases, an `&`; see next bullet) for *all* the repos, or none
of them
repo @all
R = sitaram
* an `&` prefix means both of the above are true
The `_` suffix is special. This says the user has only implicit access (due
to one of the `@all` uses), but no explicit access.
### #limitoutput using patterns to limit output
Here are a couple of samples with optional patterns:
$ ssh git@server info git
hello sitaram, this is gitolite v2.1-29-g5a125fa running on git 1.7.4.4
the gitolite config gives you the following access:
@R W git-notes
@R W gitolite
R W gitolite-admin
$ ssh git@server info admin
hello sitaram, this is gitolite v2.1-29-g5a125fa running on git 1.7.4.4
the gitolite config gives you the following access:
R W gitolite-admin
In "big-config" mode (i.e., when `GL_BIG_CONFIG` is set) the pattern is
**mandatory**. You can try and cheat the system by passing in a "." but
gitolite truncates the output after 20 results to prevent a DOS. (This limit
can be changed; see `$BIG_INFO_CAP` in the documentation for
[`~/.gitolite.rc`][rc]).
The pattern is also mandatory when an admin wants to find out what access some
*other* user has, which you may have guessed from the syntax in the "usage"
line above.
### #openssh5.6 side note: openssh 5.6
It used to be that the gitolite documentation would say "just use `ssh
git@server`" in the past, because gitolite defaults to the "info" command if
no command is passed.
However, starting with [openssh 5.6][openssh56], this won't work. The ssh
client will now "Kill channel when pty allocation requests fail". This means
that gitolite is not even invoked; you only get a message about pty allocation
failure, followed by "connection closed".
So now you have to use an explicit "info" command, (`ssh git@server info`) or
add the `-T` option to ssh (`ssh -T git@server`).
[openssh56]: http://www.openssh.org/txt/release-5.6
## #expand the "expand" command
Usage:
ssh git@server expand [optional_pattern]
The "expand" command trawls through all the repositories on the server,
limiting to repos matching the pattern you provide (default is all repos
found).
For each repo found, it searches for it in the config -- either the actual
repo entry (when the repo is not a wildcard repo), or an entry for the
wildcard that matches it -- and reports permissions. It also takes into
account extra permissions enabled by the `setperms` command (see
doc/wildcard-repositories.mkd). It shows you the "creator" of the repo as
an additional column, defaulting to `<gitolite>` if it was not a wildcard
repo.
The limit of number of repos shown in big-config mode (20, by default)
described earlier applies to the "expand" command also.

427
doc/ssh-troubleshooting.mkd Normal file
View file

@ -0,0 +1,427 @@
# F=sts ssh troubleshooting
**This document must be read in full the first time. If you start from some
nice looking section in the middle it may not help you unless you're already
an expert at ssh**.
This document should help you troubleshoot ssh-related problems in installing
and accessing gitolite.
## IMPORTANT -- READ THIS FIRST
### caveats
* Before reading this document, it is **mandatory** to read and **completely
understand** [this][gl_ssh], which is a very detailed look at how gitolite
uses ssh's features on the server side. Don't assume you know all that;
if you knew it, you wouldn't be needing *this* document either!
* This document, and others linked from this, together comprise all the help
I can give you in terms of the ssh aspect of using gitolite. If you're
installing gitolite, you're a "system admin", like it or not. Ssh is
therefore a necessary skill. Please take the time to learn at least
enough to get passwordless access working.
* Please note that authentication is not really gitolite's job at all. I'd
rather spend time on actual gitolite features, code, and documentation
than authentication (i.e., ssh, in the common case).
Surprised? [This][auth] might help explain better.
### naming conventions used
* Your workstation is the **client**. Your userid on the client does not
matter, and it has no relation to your gitolite username.
* the server is called **server** and the "hosting user" is **git**. If
this is an RPM/DEB install, the hosting user is probably called
"gitolite", however we will use "git" in this document.
### taking stock -- relevant files and directories
* the client has a `~/.ssh` containing a few keypairs. It may also have a
`config` file.
* the client also has a clone of the "gitolite-admin" repo, which contains a
bunch of `*.pub` files in `keydir`. We assume this clone is in `$HOME`;
if it is not, adjust instructions accordingly when needed.
* The git user on the server has a `~/.ssh/authorized_keys` file that the
ssh daemon uses to authenticate incoming users. We often call this file
**authkeys** to save typing, and it always means the one on the server
(we're not interested in this file on the client side).
* the server also has a `~/.gitolite/keydir` which contains a bunch of
`*.pub` files.
### normal gitolite key handling
Here's how normal gitolite key handling works:
* (on client) pub key changes like adding new ones, deleting old ones, etc.,
are done in the `keydir` directory in the gitolite-admin repo clone. Then
the admin `git add`s and `git commit`s those changes, then `git push`es
them to the server.
* (on server) a successful push from the client makes git invoke the
post-update hook in the gitolite-admin repo. This hook is installed by
gitolite, and it does a bunch of things which are quite transparent to
the admin, but we'll describe briefly here:
* the pubkey files from this push are checked-out into
`~/.gitolite/keydir` (and similarly the config files into
`~/.gitolite/conf`)
* the "compile" script then runs, which uses these files to populate
`~/.ssh/authorized_keys` on the server
The authkeys file may have other, (non-gitolite) keys also. Those
lines are preserved. Gitolite only touches lines that are found
between gitolite's "marker" lines (`# gitolite start` and `# gitolite
end`).
## (Other resources)
People who think installing gitolite is too hard should take a look at this
[tutorial][tut] to **see how simple it *actually* is**.
## common ssh problems
Since I'm pretty sure at least some of you didn't bother to read the
"IMPORTANT: PLEASE READ FIRST" section above, let me take a minute to point
you there again. Especially the first bullet.
Done? OK, read on...
The following problem(s) indicate that pubkey access is not working at all, so
you should start with [appendix 1][stsapp1_]. If that doesn't fix the problem, continue
with the other appendices in sequence.
* running any git clone/fetch/ls-remote or just `ssh git@server info` asks
you for a password.
The following problem(s) indicate that your pubkey is bypassing gitolite and
going straight to a shell. You should start with [appendix 2][stsapp2_] and continue with
the rest in sequence. [Appendix 5][stsapp5_] has some background info.
* running `ssh git@server info` gets you the output of the GNU 'info'
command instead of gitolite's version and access info.
* running `git clone git@server:repositories/reponame` (note presence of
`repositories/` in URL) works.
[A proper gitolite key will only let you `git clone git@server:reponame`
(note absence of `repositories/`)]
* you are able to clone repositories but are unable to push changes back
(the error complains about the `GL_RC` environment variable not being set,
and the `hooks/update` failing in some way).
[If you run `git remote -v` you will find that your clone URL included the
`repositories/` described above!]
* conversely, using the correct syntax, `git clone git@server:reponame`
(note absence of `repositories/` in the URL), gets you `fatal: 'reponame'
does not appear to be a git repository`, and yet you are sure 'reponame'
exists, you haven't mis-spelled it, etc.
## step by step
Since I'm pretty sure at least some of you didn't bother to read the
"IMPORTANT: PLEASE READ FIRST" section above, let me take a minute to point
you there again. Especially the first bullet.
Done? OK, now the general outline for ssh troubleshooting is this:
* make sure the server's overall setup even *allows* pubkey based login.
I.e., check that git fetch/clone/ls-remote commands or a plain `ssh
git@server info` do NOT ask for a password. If you do get asked for a
password, see [appendix 1][stsapp1_].
* match client-side pubkeys (`~/.ssh/*.pub`) with the server's authkeys
file. To do this, run `sshkeys-lint`, which tells you in detail what key
has what access. See [appendix 2][stsapp2_].
* at this point, we know that we have the right key, and that if sshd
receives that key, things will work. But we're not done yet. We still
need to make sure that this specific key is being offered/sent by the
client, instead of the default key. See [appendix 3][stsapp3_] and [appendix 4][sshhostaliases].
## random tips, tricks, and notes
### giving shell access to gitolite users
We've managed (thanks to an idea from Jesse Keating) to make it possible for a
single key to allow both gitolite access *and* shell access.
This is done by copying the pubkey (to which you want to give shell access) to
the server and running
gl-tool add-shell-user ~/foo.pub
**IMPORTANT UPGRADE NOTE**: previous implementations of this feature were
crap. There was no easy/elegant way to ensure that someone who had repo admin
access would not manage to get himself shell access.
Giving someone shell access requires that you should have shell access in the
first place, so the simplest way is to enable it from the server side only.
### losing your admin key
If you lost the admin key, and need to re-establish ownership of the
gitolite-admin repository with a fresh key, get a shell on the server and use
the program called `gl-admin-push` that comes with gitolite. See instructions
[here][adminpush].
### simulating ssh-copy-id
don't have `ssh-copy-id`? This is broadly what that command does, if you want
to replicate it manually. The input is your pubkey, typically
`~/.ssh/id_rsa.pub` from your client/workstation.
* it copies it to the server as some file
* it appends that file to `~/.ssh/authorized_keys` on the server
(creating it if it doesn't already exist)
* it then makes sure that all these files/directories have go-w perms
set (assuming user is "git"):
/home/git/.ssh/authorized_keys
/home/git/.ssh
/home/git
[Actually, `sshd` requires that even directories *above* `~` (`/`, `/home`,
typically) also must be `go-w`, but that needs root. And typically
they're already set that way anyway. (Or if they're not, you've got
bigger problems than gitolite install not working!)]
### problems with using non-openssh public keys
Gitolite accepts public keys only in openssh format. Trying to use an "ssh2"
key (used by proprietary SSH software) results in:
WARNING: a pubkey file can only have one line (key); ignoring YourName.pub
To convert ssh2-compatible keys to openssh run:
ssh-keygen -i -f /tmp/ssh2/YourName.pub > /tmp/openssh/YourName.pub
then use the resulting pubkey as you normally would in gitolite.
### windows issues
On windows, I have only used msysgit, and the openssh that comes with it.
Over time, I have grown to distrust putty/plink due to the number of people
who seem to have trouble when those beasts are involved (I myself have never
used them for any kind of git access). If you have unusual ssh problems that
just don't seem to have any explanation, try removing all traces of
putty/plink, including environment variables, etc., and then try again.
Thankfully, someone contributed [contrib/putty.mkd][contrib_putty].
## #stsapp1_ appendix 1: ssh daemon asks for a password
> **NOTE**: This section should be useful to anyone trying to get
> password-less access working. It is not necessarily specific to gitolite,
> so keep that in mind if the wording feels a little more general than you
> were expecting.
You have generated a keypair on your workstation (`ssh-keygen`) and copied the
public part of it (`~/.ssh/id_rsa.pub`, by default) to the server.
On the server you have appended this file to `~/.ssh/authorized_keys`. Or you
ran something, like the `gl-setup` step during a gitolite install, which
should have done that for you.
You now expect to log in without having to type in a password, but when you
try, you are being asked for a password.
This is a quick checklist:
* make sure you're being asked for a password and not a pass*phrase*. Do
not confuse or mistake a prompt saying `Enter passphrase for key
'/home/sitaram/.ssh/id_rsa':` for a password prompt from the remote
server!
When you create an ssh keypair using `ssh-keygen`, you have the option of
protecting it with a passphrase. When you subsequently use that keypair
to access a remote host, your *local* ssh client needs to unlock the
corresponding private key, and ssh will probably ask for the passphrase
you set when you created the keypair.
You have two choices to avoid this prompt every time you try to use the
private key. The first is to create keypairs *without* a passphrase (just
hit enter when prompted for one). **Be sure to add a passphrase later,
once everything is working, using `ssh-keygen -p`**.
The second is to use `ssh-agent` (or `keychain`, which in turn uses
`ssh-agent`) or something like that to manage your keys. Other than
discussing one more potential trouble-spot with ssh-agent (see below),
further discussion of ssh-agent/keychain is out of scope of this document.
* ssh is very sensitive to permissions. An extremely conservative setup is
given below, but be sure to do this on **both the client and the server**:
cd $HOME
chmod go-rwx .
chmod -R go-rwx .ssh
* actually, every component of the path to `~/.ssh/authorized_keys` all the
way upto the root directory must be at least `chmod go-w`. So be sure to
check `/` and `/home` also.
* while you're doing this, make sure the owner and group info for each of
these components are correct. `ls -ald ~ ~/.ssh ~/.ssh/authorized_keys`
will tell you what they are.
* you may also want to check `/etc/ssh/sshd_config` to see if the "git" user
is allowed to login at all. For example, if that file contains an
`AllowUsers` config entry, then only users mentioned in that line are
allowed to log in!
* some OSs/distributions require that the "git" user should have a password
and/or not be a locked account. You may want to check that as well.
* if all that fails, log onto the server as root, `cd /var/log`, and look
for a file called `auth.log` or `secure` or some such name. Look inside
this file for messages matching the approximate time of your last attempt
to login, to see if they tell you what is the problem.
## #stsapp2_ appendix 2: which key is which -- running sshkeys-lint
Follow these steps on the client:
* get a copy of `~/.ssh/authorized_keys` from the server and put it in
`/tmp/foo` or something
* cd to `~/.ssh`
* run `sshkeys-lint *.pub < /tmp/foo`
This tells you, for each pubkey, what type of access (if any) it has to the
server.
Note that it is not trying to log in or anything -- it's just comparing bits
of text (the contents of STDIN taken as an authkeys file, and the contents of
each of the `*.pub` files one by one).
> Note: It's also a stand-alone program, so even if your gitolite version is
> old, you can safely bring over just this program from a more recent
> gitolite and use it, without having to upgrade gitolite itself.
If the pubkey file you're interested in appears to have the correct access to
the server, you're done with this step.
Otherwise you have to rename some keypairs and try again to get the effect you
need. Be careful:
* do not just rename the ".pub" file; you will have to rename the
corresponding private key also (the one with the same basename but without
an extension)
* if you're running ssh-agent, you may have to delete (using `ssh-add -D`)
and re-add identities for it to pick up the renamed ones correctly
### typical cause(s)
The admin often has passwordless shell access to `git@server` already, and
then used that same key to get access to gitolite (i.e., copied that same
pubkey as YourName.pub and ran `gl-setup` on it).
As a result, the same key appears twice in the authkeys file now, and since
the ssh server will always use the first match, the second occurrence (which
invokes gitolite) is ignored.
To fix this, you have to use a different keypair for gitolite access. The
best way to do this is to create a new keypair, copy the pubkey to the server
as YourName.pub, then run `gl-setup YourName.pub` on the server. Remember to
adjust your agent identities using ssh-add -D and ssh-add if you're using
ssh-agent, otherwise these new keys may not work.
## #stsapp3_ appendix 3: ssh client may not be offering the right key
* make sure the right private key is being offered. Run ssh in very
verbose mode and look for the word "Offering", like so:
ssh -vvv user@host pwd 2> >(grep -i offer)
If some keys *are* being offered, but not the key that was supposed to be
used, you may be using ssh-agent (next bullet). You may also need to
create some host aliases in `~/.ssh/config` ([appendix 4][sshhostaliases]).
* (ssh-agent issues) If `ssh-add -l` responds with either "The agent has no
identities." or "Could not open a connection to your authentication
agent.", then you can skip this bullet.
However, if `ssh-add -l` lists *any* keys at all, then something weird
happens. Due to a quirk in ssh-agent, ssh will now *only* use one of
those keys, *even if you explicitly ask* for some other key to be used.
In that case, add the key you want using `ssh-add ~/.ssh/YourName` and try
the access again.
## F=sshhostaliases appendix 4: host aliases
(or "making git use the right options for ssh")
The ssh command has several options for non-default items to be specified.
Two common examples are `-p` for the port number if it is not 22, and `-i` for
the public key file if you do not want to use just `~/.ssh/id_rsa` or such.
Git has two ssh-based URL syntaxes, but neither allows specifying a
non-default public key file. And a port number is only allowed in one of
them. (See `man git-clone` for details). Finally, hosts often have to be
referred with IP addresses (such is life), or the name is very long, or hard
to remember.
Using a "host" para in `~/.ssh/config` lets you nicely encapsulate all this
within ssh and give it a short, easy-to-remember, name. Example:
host gitolite
user git
hostname a.long.server.name.or.annoying.IP.address
port 22
identityfile ~/.ssh/id_rsa
Now you can simply use the one word `gitolite` (which is the host alias we
defined here) and ssh will infer all those details defined under it -- just
say `ssh gitolite` and `git clone gitolite:reponame` and things will work.
(By the way, the 'port' and 'identityfile' lines are needed only if you have
non-default values, although I put them in anyway just to be complete).
If you have *more than one* pubkey with access to the *same* server, you
**must** use this method to make git pick up the right key. There is no other
way to do this, as far as I know.
[tut]: http://sites.google.com/site/senawario/home/gitolite-tutorial
## #stsapp5_ appendix 5: why bypassing gitolite causes a problem
When you bypass gitolite, you end up running your normal shell instead of the
special gitolite entry point script `gl-auth-command`.
This means commands (like 'info') are interpreted by the shell instead of
gitolite.
It also means git operations look for repos in `$HOME`.
However, gitolite places all your repos in a subdirectory pointed to by
`$REPO_BASE` in the rc file (default: `repositories`), and internally prefixes
this before calling the actual git command you invoked. Thus, the pathname of
the repo that you use on the client is almost never the correct pathname on
the server. (This is by design. Don't argue...)
This means that, you get 2 kinds of errors if you bypass gitolite
* when you use `git@server:reponame` with a key that bypasses gitolite
(i.e., gets you a shell), this prefixing does not happen, and so the repo
is not found. Neither a clone/fetch nor a push will work.
* conversely, consider `git@server:repositories/reponame.git`. The clone
operation will work -- you're using the full Unix path, (assuming default
`$REPO_BASE` setting), and so the shell finds the repo where you said it
would be. However, when you push, gitolite's **update hook** kicks in,
and fails to run because some of the environment variables it is expecting
are not present.

36
doc/support.mkd Normal file
View file

@ -0,0 +1,36 @@
# F=support support for gitolite
Email addresses etc are in the "contact and license" section at the bottom of
the [main](http://sitaramc.github.com/gitolite/) document.
If you're willing to read, you will only need support for the most obscure
issues. I often don't bother responding for silly issues that are already in
the docs. Seriously, folks, troubleshooting involves reading; do I really
need to *say* that?
* Lost your admin key? Try [gl-admin-push][adminpush].
* For install/setup issues see the [install trouble?][insttrouble] section
of the [install][] document.
In particular, that section has two links for ssh issues. **I've yet to
see an ssh problem that is not already covered by those** (but please tell
me if you find one!)
* The [suggested reading list][rtfm] section is a quick overview of the
documentation.
* If you still can't find what you need, try the [**master index/table of
contents**](http://sitaramc.github.com/gitolite/master-toc.html) and use
your browser's search function.
----
**Note on the online docs**: the online docs break up longer documents by
putting less "central" concepts into subdocuments (replacing the text with a
link so you know it's there). I did this because the most frequent complaint
was "TL;DR" (your documentation is too long); I do believe it is now easier to
find things/navigate.
<font color="gray">Of course, now someone complained that it took him "4
middle-of-text clicks" to reach some section... sigh!</font>

413
doc/tips-notes.mkd Normal file
View file

@ -0,0 +1,413 @@
# F=tips assorted tips and notes
## common errors and mistakes
* adding `repositories/` at the start of the repo name in the `git clone`.
This error is typically made by the *admin* himself -- because he knows
what `$REPO_BASE` is set to and thinks he has to provide that prefix on
the client side also :-) In fact gitolite prepends `$REPO_BASE`
internally, so you shouldn't also do the same thing!
* being able to clone but getting errors on push. Most likely caused by a
combination of:
* you already have shell access to the server, not just "gitolite"
access, *and*
* you cloned using `git clone git@server:repositories/repo.git` (notice
there's an extra "repositories/" in there?)
In other words, you used a key that completely bypassed gitolite and went
straight to the shell to do the clone.
Please see doc/ssh-troubleshooting.mkd for what all this means.
## other errors, warnings, notes...
### cloning an empty repo
Cloning an empty repo is only possible with clients greater than 1.6.2. So at
least one of your clients needs to have a recent git. Once at least one
commit has been made, older clients can also use it
When you clone an empty repo, git seems to complain about `fatal: The remote
end hung up unexpectedly`. However, you can ignore this, since it doesn't
seem to hurt anything. [Update 2009-09-14; this has been fixed in git
1.6.4.3]
### `@all` syntax for repos
There *is* a way to use the `@all` syntax for repos also, as described in
`doc/gitolite.conf.mkd`. However, there are a couple of minor cautions:
* don't use `NAME/` or such restrictions on the special `@all` repo. Due to
the potential for defeating a crucial optimisation and slowing down *all*
access, we do not support this.
## features
Apart from the big ones listed in the top level README, and subjective ones
like "better config file format", gitolite has evolved to have many useful
features than the original goal of branch-level access control.
### syntax and normal usage
#### #multikey one user, many keys
If you have a user who has more than one pubkey (like from different machines)
the simplest way to deal with it is to add subdirectories and add keys there.
For example, I might have these files in `keydir/`:
sitaram.pub
home/sitaram.pub
laptop/sitaram.pub
##### F=oldmultikeys old style multi keys
This is an older method of enabling multi-keys. It will continue to work and
be supported in *code*, simply because I prefer it. But I am not going to
document it except for the example below, nor am I going to support it in
terms of questions. Sorry. Apparently it was too complex to understand, even
for some smart folks I know. This tells me it was probably ill thought out
and should have been obsoleted as soon as e0fe73a was pushed.
Anyway, here's *all* the documentation for it -- some sample pubkey filenames
and the corresponding derived usernames:
* plain username, no multikey
sitaramc.pub sitaramc
* plain username, with multikeys
sitaramc@laptop.pub sitaramc
sitaramc@desktop.pub sitaramc
* email address as username, no multikey
sitaramc@gmail.com.pub sitaramc@gmail.com
* email address as username, with multikeys
sitaramc@gmail.com@laptop.pub sitaramc@gmail.com
sitaramc@gmail.com@desktop.pub sitaramc@gmail.com
### F=tipssec_ security, access control, and auditing
#### #2levels two levels of access rights checking
Gitolite has two levels of access checks. The **first check** is what I will
call the **pre-git** level. At this stage, the `gl-auth-command` has been
invoked by `sshd`, and it knows just three things:
* who,
* what repository, and
* what type of access (R or W)
Note that at this point no git program has entered the picture, and we have no
way of knowing what **ref** (branch, tag, etc) he is trying to update, even if
it is a "write" operation.
For a "read" operation to pass this check, the username (or `@all` users) must
have read permission (i.e., R, RW, RW+, etc.) on at least one branch of the
repo (or `@all` repos).
For a "write" operation, there is an additional restriction: lines specifying
only `R` (read access) don't count. *The user must have write access to
**some** ref in the repo in order to pass this stage!*
The **second check** is via a git `update hook`. This check only happens for
write operations. By this time we know what "ref" he is trying to update, as
well as the old and the new SHAs of that ref (by which we can also deduce
whether it's a rewind or not). This is where the "per-branch" permissions
come into play.
Each refex that allows `W` access (or `+` if this is a rewind) for *this*
user, on *this* repo, is matched against the actual refname being updated. If
any of the refexes match, the push succeeds. If none of them match, it fails.
Gitolite also allows "exclude" or "deny" rules. See later in this document
for details.
#### better logging
If you have been too liberal with the permission to rewind, it has built-in
logging as an emergency fallback if someone goes too far, or for audit
purposes [`*`]. The logfile names and location are configurable, and can
include the year/month/day etc in the filename for easy archival or further
processing. The log file even tells you which pattern in the config file
matched to allow that specific access to proceed.
> [`*`] setting `core.logAllRefUpdates true` does provide a safety net
> against over-zealous rewinds, but it does not tell you "who". And
> strangely, management does not seem to share the view that "blame" is just
> a synonym for "annotate" ;-)]
The log lines look like this:
2009-09-19.10:24:37 + b4e76569659939 4fb16f2a88d8b5 myrepo refs/heads/master user2 refs/heads/master
The "+" at the start indicates a non-fast forward update, in this case from
b4e76569659939 to 4fb16f2a88d8b5. So b4e76569659939 is the one to restore!
Can it get easier?
The other parts of the log line are the name of the repo, the refname being
updated, the user updating it, and the refex pattern (from the config file)
that matched, in case you need to debug the config file itself.
#### delegating parts of the config file
You can now split up the config file and delegate the authority to specify
access control for their own pieces. See [delegation][deleg] for details.
### F=tnconv_ convenience features
#### what repos do I have access to?
Sometimes there are too many repos, maybe even named similarly, or with the
potential for typos, confusion about hyphens/underscores or upper/lower case,
etc. You'd just like a simple way to know what repos you have access to.
Gitolite provides two commands ([`info`][info] and [`expand`][expand])
to help you find this information.
#### support for git installed outside default PATH
The normal solution is to add to the system default PATH somehow, either by
munging `/etc/profile` or by enabling `PermitUserEnvironment` in
`/etc/ssh/sshd_config` and then setting the PATH in `~/.ssh/.environment`.
All these are security risks because they allow a lot more than just you and
your git install :-)
And if you don't have root, you can't do this anyway.
The only solution till now has been to ask every client to set the config
parameters `remote.<name>.receivepack` and `remote.<name>.uploadpack`. But
telling *every* client to do so is a pain...
Gitolite lets you specify the directory in which git binaries are to be found,
via a new variable (`$GIT_PATH`) in the "rc" file. If this variable is
non-empty, it will be appended to the PATH environment variable before
attempting to run git stuff.
Very easy, very simple, and completely transparent to the users :-)
**Note**: sometimes you have a system that already has an older "git"
installed in one of the system PATHs, but you've installed a newer git in some
non-standard location and want that picked up. Because of security reasons,
gitolite will not prepend `GIT_PATH` to the PATH variable, so the older git
comes first and it gets kinda frustrating!
Here's a simple workaround. Ignore the `GIT_PATH` variable, and directly set
the full PATH in the rc file, like so:
$ENV{PATH} = "/home/sitaram/bin:$ENV{PATH}";
#### #pers "personal" branches
"personal" branches are great for corporate environments, where
unauthenticated pull/clone is a no-no. Since a dev workstation cannot do
authentication, even work shared just between 2 devs has to go *via* the
server. This causes the same branch name clutter as in a centralised VCS,
plus setting up permissions for this becomes a chore for the admin.
Personal branches exist **in a namespace** of their own. The syntax is
RW+ personal/USER/ = @userlist
where the "personal" can be anything you like (but cannot be empty), and the
"/USER/" part is **necessary (including both slashes)**. A user "alice" (if
she's in the userlist) can then push any branches inside `personal/alice/`.
Which means she can push `personal/alice/foo` and `personal/alice/bar`, but
NOT `personal/alice`.
(Background: at runtime the "USER" component will be replaced by the name of
the invoking user. Access is determined by the right hand side, as usual).
#### custom hooks and custom git config
You can specify hooks that you want to propagate to all repos, as well as
per-repo "gitconfig" settings. Please see `doc/2-admin.mkd` and
`doc/gitolite.conf.mkd` for details.
#### bypassing gitolite
Sometimes you'll need to access one of the gitolite-managed repos directly on
the server, without going through gitolite. Reasons may be some automatic
updates or some other ad hoc purposes you can dream up.
Cloning a gitolite-controlled repo is easy enough -- just use the full path
(typically `~/repositories/reponame.git`) instead of just `reponame`, to
compensate for gitolite not sitting in between and adding those things to the
repo path.
But when you push, the update hook (which git will invoke anyway) will fail
because it needs all sorts of access control info that it now doesn't have,
because the push was invoked without going through gitolite.
In order to bypass the update hook, just set the `GL_BYPASS_UPDATE_HOOK`
environment variable to "1" or something, export it, and push. I prefer not
to set that variable permanently, preferring this mode instead:
GL_BYPASS_UPDATE_HOOK=1 git push
#### F=adminpush gl-admin-push: bypassing gitolite for the gitolite-admin repo
The method described in the previous section (setting `GL_BYPASS_UPDATE_HOOK`)
will work for all the repos managed by gitolite, **except** for the special
`gitolite-admin` repo. For that you will need some extra magic, because there
is also a `post-update` hook that runs here, and this needs additional
information which is NOT available if you bypass gitolite.
(Note: If your gitolite is too old to have the `gl-admin-push` program, try
`gl-dont-panic`; run it without arguments for usage info. If you don't even
have that, it may be best to [clean][] things out more thoroughly!)
Use the `gl-admin-push` program to make changes to the admin repo *directly on
the server*. Here's how:
* clone the repo to some safe location and cd to it:
cd /tmp
git clone ~/repositories/gitolite-admin.git
cd gitolite-admin
* make whatever changes you want to that clone and commit. You can add new
keys, change the conf file, or anything at all that needs fixing up. You
can even reset to an older commit (rewind) if that is the simplest way to
fix up some config problem that may have lost you your access.
* when done, instead of `git push <push arguments>`, use this program
instead. For example, instead of `git push -f`, use `gl-admin-push -f`.
Note that this method will work for *any* repo, not just the special admin
repo.
#### #disable disabling write access to take backups
If you want to take normal, OS-level, backups of the system, you might want
git to be quiescent during that time, so that the backup is clean. The best
way to do this is to disable write-access to the server for the duration of
the backup.
Here's how:
cd $HOME # if running as "git" user, else "cd ~git" or whatever
echo writes disabled during backup window > .gitolite.down
# << RUN YOUR BACKUP COMMAND(s) HERE >>
rm .gitolite.down
I leave it to you to
* make sure that if the backup script fails, the `.gitolite.down` file is
still removed (or not; maybe your policy is that if the backup failed, no
further writes are allowed. Whatever...)
* if you're extremely paranoid (even I wouldn't worry about this!) make sure
that no push is *in progress* by checking for any `git-receive-pack`
processes in a `ps` output.
### INconvenience features
#### #repodel deleting a repo
By design, there is no code in gitolite to *delete* a repo if the repo was
specified by name in the config file. (Wildcard repos *can* be deleted by the
user; see [here][wild_repodel] for details).
If you *do* want to permanently delete a *non*-wildcard repo, here's what you
do:
* remove the repo from the gitolite-admin repo clone's `conf/gitolite.conf`
file. "add" the change, commit, and push.
* *then* remove the repo from `~/repositories` on the server (or whatever
you set `$REPO_BASE` to in the `~/.gitolite.rc`)
#### renaming a repo
This is similar; there's no code to do this in gitolite. What you do is:
* log on to the server, `cd $REPO_BASE` (default: `cd ~/repositories`), and
`mv old-name.git new-name.git`
* back on your gitolite-admin clone, edit `conf/gitolite.conf` and replace
all occurrences of `old-name` with `new-name`. Then add, commit, and push
as usual.
The order of these 2 steps is important; do not reverse them :-)
### helping with gitweb
Although gitweb is a completely separate program, gitolite can do quite a
lot to help you manage gitweb access as well; once the initial setup is
complete, you can do it all from within the gitolite config file!
If you just want gitweb to show some repositories, see [here][gwd] for how to
specify which repos to show.
#### #gitwebauth easier to link gitweb authorisation with gitolite
Over and above whether a repo is even *shown* by gitweb, you may want to
further restrict people, allowing them to view *only* those repos for which
they have been given read access by gitolite.
This requires that:
* you have to have some sort of HTTP auth on your web server (out of my
scope, sorry!)
* the HTTP auth should use the same username (like "sitaram") as used in the
gitolite config (for the corresponding user)
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
Of course some other authentication method can be used (e.g. `mod_ldap`) as
long as the usernames match.
Gitweb allows you to specify a subroutine to decide on access. We use that
feature and tie it to gitolite. Configuration example can be found in
`contrib/gitweb/`.
#### #umask umask setting
Gitweb not able to read your repos? You can change the umask for newly
created repos to something more relaxed -- see the `REPO_UMASK` setting in the
[rc file documentation][rc].
### advanced features
There are some really cool features that are now in pretty wide use.
* **[repos named with wildcards][wild]** is useful when some or most of your
repos fit a pattern, avoiding the need to name repos individually in the
config file. New repos matching the pattern can be created by any user
(if you give them rights to), with a set of permissions assigned to
"roles", and the creator can then place users into those roles.
* **[admin defined commands][ADCs]** allow controlled access to specific
commands and scripts without giving users full shell access.
### odds and ends
#### "poking" the admin repo to force a compile
Sometimes you need to force a compile, as if you pushed the gitolite-admin
repo. I have a git alias that looks like this:
[alias]
poke = !git ls-remote origin | grep -w refs/heads/poke && git push origin :poke || git push origin master:poke
so I just run `git poke`. This toggles between deleting and creating a dummy
branch called "poke". Either operation will trigger the hooks.

Some files were not shown because too many files have changed in this diff Show more