Compare commits
No commits in common. "master" and "g2" have entirely different histories.
4
.gitattributes
vendored
Normal file
4
.gitattributes
vendored
Normal 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
5
.gitignore
vendored
Normal file
|
@ -0,0 +1,5 @@
|
|||
*.tar
|
||||
*.tgz
|
||||
*.tar.gz
|
||||
*.tar.bz2
|
||||
conf/VERSION
|
86
CHANGELOG
86
CHANGELOG
|
@ -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
19
Makefile
Normal 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
359
README.md
|
@ -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
41
README.mkd
Normal 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]).
|
|
@ -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
2
conf/example.conf
Normal 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
96
conf/example.gitolite.rc
Normal 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:
|
|
@ -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
45
contrib/VREF/gl-VREF-DUPKEYS
Executable 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...
|
|
@ -1,4 +1,4 @@
|
|||
#!/bin/sh
|
||||
#!/bin/bash
|
||||
|
||||
# gitolite VREF to find autogenerated files
|
||||
|
|
@ -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:
|
||||
|
52
contrib/adc/README-adc.mkd
Normal file
52
contrib/adc/README-adc.mkd
Normal 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
56
contrib/adc/able
Executable 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
|
132
contrib/adc/adc.common-functions
Normal file
132
contrib/adc/adc.common-functions
Normal 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
84
contrib/adc/delete-branch
Executable 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
45
contrib/adc/fork
Executable 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
|
52
contrib/adc/get-rights-and-owner.in-perl
Executable file
52
contrib/adc/get-rights-and-owner.in-perl
Executable 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
23
contrib/adc/getdesc
Normal 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
123
contrib/adc/git
Executable 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
63
contrib/adc/git-annex-shell
Executable 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
101
contrib/adc/gl-reflog
Executable 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
63
contrib/adc/help
Executable 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
30
contrib/adc/htpasswd
Normal 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
470
contrib/adc/hub
Executable 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
184
contrib/adc/hub.mkd
Normal 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
13
contrib/adc/list-trash
Executable 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
12
contrib/adc/lock
Executable 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
115
contrib/adc/perms
Executable 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
77
contrib/adc/pygitolite.py
Normal 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)
|
55
contrib/adc/repo-deletion.mkd
Normal file
55
contrib/adc/repo-deletion.mkd
Normal 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
16
contrib/adc/restore
Executable 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
10
contrib/adc/restrict-admin
Executable 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
40
contrib/adc/rm
Executable 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
4
contrib/adc/rmrepo
Executable 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
76
contrib/adc/rsync
Executable 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
150
contrib/adc/s3backup
Normal 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
25
contrib/adc/setdesc
Normal 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
275
contrib/adc/sskm
Executable 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
237
contrib/adc/sskm.mkd
Normal 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
55
contrib/adc/su-expand
Executable 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
32
contrib/adc/su-getperms
Executable 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
1
contrib/adc/su-setperms
Symbolic link
|
@ -0,0 +1 @@
|
|||
su-getperms
|
23
contrib/adc/sudo
Executable file
23
contrib/adc/sudo
Executable 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
20
contrib/adc/svnserve
Normal 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
35
contrib/adc/symbolic-ref
Executable 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
29
contrib/adc/trash
Executable 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
12
contrib/adc/unlock
Executable 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
84
contrib/adc/watch
Executable 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
32
contrib/adc/who-pushed
Executable 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
|
8
contrib/emacs/README-emacs.mkd
Normal file
8
contrib/emacs/README-emacs.mkd
Normal 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
116
contrib/gerrit.mkd
Normal 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
|
15
contrib/gitolite-tools.mkd
Normal file
15
contrib/gitolite-tools.mkd
Normal 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)
|
51
contrib/gitweb/gitweb.conf
Normal file
51
contrib/gitweb/gitweb.conf
Normal 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/);
|
||||
};
|
17
contrib/ldap/README-ldap.mkd
Normal file
17
contrib/ldap/README-ldap.mkd
Normal 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!
|
||||
|
80
contrib/ldap/ldap-query-example.pl
Normal file
80
contrib/ldap/ldap-query-example.pl
Normal 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";
|
68
contrib/ldap/ldap-query-example.sh
Normal file
68
contrib/ldap/ldap-query-example.sh
Normal 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
112
contrib/ldap/passwd
Normal 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
182
contrib/mirrorconf-helper.sh
Executable 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
|
294
contrib/mirroring-complex-example.mkd
Normal file
294
contrib/mirroring-complex-example.mkd
Normal 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
67
contrib/monkeysphere.mkd
Normal 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
51
contrib/partial-copy/gl-pre-git
Executable 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
|
130
contrib/partial-copy/partial-copy.mkd
Normal file
130
contrib/partial-copy/partial-copy.mkd
Normal 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
208
contrib/partial-copy/t.sh
Executable 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
|
32
contrib/partial-copy/update.secondary
Executable file
32
contrib/partial-copy/update.secondary
Executable 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
203
contrib/putty.mkd
Normal 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
95
contrib/real-users/gl-shell
Executable 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", @_);
|
||||
}
|
77
contrib/real-users/gl-shell-setup
Executable file
77
contrib/real-users/gl-shell-setup
Executable 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
|
108
contrib/real-users/password-access.mkd
Normal file
108
contrib/real-users/password-access.mkd
Normal 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'.
|
10
contrib/vim/README-vim.mkd
Normal file
10
contrib/vim/README-vim.mkd
Normal 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
227
doc/1000-words.mkd
Normal 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) |----------->| ~/gitolite‐admin |
|
||||
| | (1) | |
|
||||
| | | |
|
||||
| |<-----------| |
|
||||
\-------------------/ (3) \-----------------------/
|
||||
|
||||
/-----------------------\
|
||||
/-------------------\ | (alice@workstation) |
|
||||
| bob cYEL |----\ |~/gitolite‐admin/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) |----------->| ~/gitolite‐admin |
|
||||
| | (1) | |
|
||||
| | | |
|
||||
| |<-----------| |
|
||||
\-------------------/ (3) \-----------------------/
|
||||
|
||||
/-----------------------------------\
|
||||
| (alice@workstation) |
|
||||
|~/gitolite‐admin/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
325
doc/CHANGELOG
Normal 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
|
|
@ -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.
|
208
doc/admin-defined-commands.mkd
Normal file
208
doc/admin-defined-commands.mkd
Normal 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
330
doc/admin.mkd
Normal 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
89
doc/auth.mkd
Normal 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
399
doc/big-config.mkd
Normal 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
139
doc/delegation.mkd
Normal 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
151
doc/developer-notes.mkd
Normal 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
145
doc/gitolite-and-ssh.mkd
Normal 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.
|
257
doc/gitolite-gitweb-http-backend.mkd
Normal file
257
doc/gitolite-gitweb-http-backend.mkd
Normal 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:<repo-name>
|
||||
* http://git.example.com/<repo-name>.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 >>
|
221
doc/gitolite.conf-by-example.mkd
Normal file
221
doc/gitolite.conf-by-example.mkd
Normal 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
663
doc/gitolite.conf.mkd
Normal 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
361
doc/gitolite.rc.mkd
Normal 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
163
doc/hook-propagation.mkd
Normal 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
193
doc/http-backend.mkd
Normal 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
203
doc/index.mkd
Normal 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
455
doc/install.mkd
Normal 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
126
doc/migrate.mkd
Normal 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
720
doc/mirroring.mkd
Normal 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
76
doc/mob-branches.mkd
Normal 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
266
doc/nagp.mkd
Normal 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
53
doc/packaging.mkd
Normal 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
173
doc/progit-article.mkd
Normal 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
124
doc/report-output.mkd
Normal 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
427
doc/ssh-troubleshooting.mkd
Normal 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
36
doc/support.mkd
Normal 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
413
doc/tips-notes.mkd
Normal 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
Loading…
Reference in a new issue