Merge branch 'easy-install'

This commit is contained in:
Sitaram Chamarty 2009-10-13 11:43:15 +05:30
commit 2fdd7b34f5
12 changed files with 684 additions and 302 deletions

View file

@ -3,19 +3,22 @@
> [IMPORTANT: There is now an "upgrade" document in the "doc" directory;
> please read if upgrading gitolite]
> [Update 2009-10-10: apart from all the nifty new features, there's now an
> "easy install" script in the src directory. Please see the INSTALL
> document in the doc directory for details]
----
Gitolite is the bare essentials of gitosis, with a completely different
config file that allows (at last!) access control down to the branch level,
including specifying who can and cannot *rewind* a given branch. It is
released under GPL v2. See COPYING for details.
Gitolite is a rewrite of gitosis, with a completely different config file that
allows (at last!) access control down to the branch level, including
specifying who can and cannot *rewind* a given branch.
In this document:
* why
* what's gone
* what's new
* the workflow
* what's extra
* security
* contact and license
----
@ -28,30 +31,15 @@ a typical $DAYJOB setting, there are some issues:
and be done
* often, "python-setuptools" isn't installed (and on a Solaris9 I was trying
to help remotely, we never did manage to install it eventually)
* or you don't have root access, or the ability to add users
* you don't have root access, or the ability to add users (this is also true
for people who have just one userid on a hosting provider)
* the most requested feature (see "what's new?") had to be written anyway
### what's gone
While I was pondering the need to finally learn python[1] , I also realised
that:
* no one in $DAYJOB type environments will use or approve access methods
that work without any authentication, so I didn't need gitweb/daemon
support in the tool or in the config file.
Update 2009-09-24: I don't use this feature but someone wanted it, so I
added it... see the "faq, tips, etc" document for more
* the idea that you admin it by pushing to a special repo is nice, but not
really necessary because of how rarely these changes are made, especially
considering how much code is involved in that piece
All of this pointed to a rewrite. In perl, naturally :-)
### what's new
### what's extra
Per-branch permissions. You will not believe how often I am asked this at
**Per-branch permissions**. You will not believe how often I am asked this at
$DAYJOB. This is almost the single reason I started *thinking* about rolling
my own gitosis in the first place.
@ -61,50 +49,42 @@ 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`).
Take a look at the example config file in the repo to see how I do this. I
copied the basic idea from `update-hook-example.txt` (it's one of the "howto"s
that come with the git source tree). However, please note the difference in
the size and complexity of the *operational code* between the update hook in
that example, and in mine :-) The reason is in the next section.
Here're **some more features**. All of them are documented in detail
somewhere in the `doc/` subdirectory.
### the workflow
* simpler, yet far more powerful, config file syntax, including specifying
gitweb/daemon access. You'll need this power if you manage lots of users
+ repos + combinations of access
* config file syntax gets checked upfront, and much more thoroughly
* if your requirements are still too complex, you can split up the config
file and delegate authority over parts of it
* more 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 in the config file) -- this is the "rebel"
branch in the repository, and always will be ;-)
In order to get per-branch access, you *must* use an update hook. However,
that only gets invoked on a push; "read" access still has to be controlled
right at the beginning, before git even enters the scene (just the way gitosis
currently works).
### security
So: either split the access control into two config files, or have two
completely different programs *both* parse the same one and pick what they
want. Crap... I definitely don't want the hook doing any parsing, (and it
would be nice if the auth-control program didn't have to either).
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 code is
very small and easily reviewable -- the 2 programs that actually control
access when a user logs in total about 200 lines of code (about
80 lines according to "sloccount").
So I changed the workflow completely:
For the first person to find a security hole in it, defined as allowing a
normal user (not the gitolite admin) to read a repo, or write/rewind a ref,
that the config file says he shouldn't, and caused by a bug in *code* that is
in the "master" branch, (not in the other branches, or the configuration file
or in Unix, perl, shell, etc.)... well I can't afford 1000 USD rewards like
djb, so you'll have to settle for 1000 INR (Indian Rupees) as a "token" prize
:-)
* all admin changes happen *on the server*, in a special directory that
contains the config and the users' pubkeys. But there's no commit and
push afterward
* instead, after making changes, you "compile" the configuration. This
refreshes `~/.ssh/authorized_keys`, as well as puts a parsed form of the
access list in a file for the other two pieces to use.
----
The pre-parsed form is basically a huge perl variable. It's human readable
too (never mind what the python guys say!)
### contact and license
So the admin knows immediately if the config file had any problems, which is
good. Also, the relatively complex parse code is not part of the actual
access control points, which are:
* the program that is run via `~/.ssh/authorized_keys` (I call it
`gl-auth-command`, equivalent to `gitosis-serve`); this decides whether
git should even be allowed to run (basic R/W/no access)
* the update-hook on each repo, which decides the per-branch permissions
### footnotes
[1] I hate whitespace to mean anything significant except for text; this is a
personal opinion *only*, so pythonistas please back off :-)
### contact
Gitolite is released under GPL v2. See COPYING for details.
sitaramc@gmail.com

View file

@ -5,7 +5,7 @@
# this file is meant to be pulled into a perl program using "do" or "require".
# You do NOT need to know perl to edit the paths; it should be fairly
# self-explanatory
# self-explanatory and easy to maintain perl syntax :-)
# --------------------------------------
@ -88,6 +88,16 @@ $PERSONAL="";
# NOTE: whatever value you choose, for security reasons it is better to make
# it fully qualified -- that is, starting with "refs/"
# --------------------------------------
# if git on your server is on a standard path (that is
# ssh git@server git --version
# works), leave this setting as is. Otherwise, choose one of the
# alternatives, or write your own
$GIT_PATH="";
# $GIT_PATH="/opt/bin/";
# --------------------------------------
# per perl rules, this should be the last line in such a file:
1;

View file

@ -1,98 +1,75 @@
# installing gitolite
### pre-requisites
This document tells you how to install gitolite. After the install is done,
you may want to see the "admin" document for adding users, repos, etc.
If you managed to install git, you might already have what gitolite needs:
There's an easy install script for Linux, and for other Unixes there's a
slightly more manual process. Both are explained here.
* git itself, the more recent the better
* perl, typically installed with git, since git sort of needs it; any
version that includes `Data::Dumper`[1] will do.
* one user account on the server, with password access [2]
In this document:
A major objective is to allow use by people without root access, permissions
to create other userids, etc. Even if you have root, please add a user just
for gitolite and do all this from that user.
### getting a tar file from a clone
You can clone the repo from github, then execute a make command to extract a
tar file of the branch you want. Please use the make command, not a plain
"git archive". The comments in the `Makefile` will explain why.
git clone git://github.com/sitaramc/gitolite.git
cd gitolite
make master.tar
# or maybe "make rebel.tar" or "make pu.tar"
### quick install from tar file
* make a temp directory somewhere, cd to it, and unpack the tar file
* run `src/install.pl` and follow the prompts
**When you are told to edit some file, please read the comments in the file**.
And if you can make some time to read the documentation, please do.
Especially if you have problems.
Notes:
* At present the location of `~/.gitolite.rc` is fixed (maybe later I'll
change it to a "git config" variable but I don't see much need right now)
If you edit it and change any paths, be sure to keep the perl syntax --
you *don't* have to know perl to do so, it's fairly easy to guess in this
limited case. And of course, make sure you adjust the commands shown
above to suit the new locations
* the config file is (by default) at `~/.gitolite/conf/gitolite.conf`,
though you can change its location in the "rc" file. Edit the file as you
wish. The comments in the file ought to be clear enough but let me know
if not
* if you want to bring in existing (bare, server) repos into gitolite, this
should work (refer to `~/.gitolite.rc` for *your* values of the pathnames
below):
* backup the repo, then move it to `$BASE_REPO`
* copy `$GL_ADMINDIR/src/update-hook.pl` to
`[reponame].git/hooks/update` -- if you don't do this, per branch
restrictions will not work
* then update the keys and the config file and "compile" (see "admin"
document)
### Footnotes:
[1] Actually, due to the way gitolite is architected, you can manage
without `Data::Dumper` on the server if you have no choice. Only
`gl-compile-conf` needs it, so just run that on some other machine and copy
the two output files across. Cumbersome but doable... the advantage of
separating all the hard work into a manually-run piece :)
[2] If you have *only* pubkey access, and **no** password access, then your
pubkey is already in the server's `~/.ssh/authorized_keys`. If you also need
to access git as a developer (clone, push, etc), do *not* submit this same
pubkey to gitolite -- it won't work.
Instead, create a different keypair for your "developer" role (by, e.g.,
`ssh-keygen -t rsa -f ~/.ssh/gitdev`), then give `~/.ssh/gitdev.pub` to
gitolite as "yourname.pub", just like you would do for any other user.
Then you create a suitable `~/.ssh/config` to use the correct key
automatically, something like this:
host gitadm
hostname my.server
user my_userid_on_server
host gitdev
hostname my.server
user my_userid_on_server
identityfile ~/.ssh/gitdev
From now on, `ssh gitadm` will get you a command line on the server, to do
gitolite admin and other work. And your repository URLs would look like
`gitdev:reponame.git`. Very, very, simple...
And as with gitosis, there's more "ssh" magic than "git" magic here :-)
* easy install
* manual install
* other notes
* next steps
----
gitolite is released under the GPL v2 license. See COPYING for details
### easy install
There is an easy install script that makes installing very easy for the common
case. **This script is meant to be run on your workstation, not on the
server!** It will take care of all the server side work, *and* get you
"push-to-admin" too :-) In short, it does **everything**!
Assumptions:
* you have a server to host gitolite
* git is installed on that server (and so is perl)
* you have a userid on that server
* you have ssh-pubkey (password-less) login to that userid
* (if you have only password access, run `ssh-keygen -t rsa` to create a
new keypair if needed, then run `ssh-copy-id user@host`)
* you have a clone or an archive of gitolite somewhere on your workstation
If so, just `cd` to that clone and run `src/00-easy-install.sh` and follow the
prompts! (Running it without any arguments shows you usage plus other useful
info).
#### advantages over the older install methods
* all ssh problems reduced to **just one pre-requisite**: enable ssh pubkey
(password-less) access to the server from your workstation first
* the script takes care of all the server side work
* when done:
* you get two different pubkeys (the original one for command line
access as before, plus a new one, created by the script, for gitolite
access)
* you can admin gitolite by commit+push a "gitolite-admin" repo, just
like gitosis (i.e., full "push to admin" power!)
#### disadvantages
* has been tested only with Linux
### manual install
If for some reason you cannot use the easy-install method, (for example,
you're on a non-Linux machine), it's not very complicated. Just open the file
`src/00-easy-install.sh` in a nice, syntax coloring, text editor, and follow
the instructions marked "MANUAL" :-)
### other notes
* If you edit `~/.gitolite.rc` and change any paths, be sure to keep the
perl syntax -- you *don't* have to know perl to do so, it's fairly easy to
guess in this limited case
* the config file is (by default) at `~/.gitolite/conf/gitolite.conf`,
though you can change its location in the "rc" file. Edit the file as you
wish. The comments in the example file (`conf/example.conf`) ought to be
clear enough but let me know if not
### next steps
See the "admin" document for how to add users, etc.

View file

@ -1,11 +1,19 @@
# upgrading gitolite atomically
Upgrading is done **manually, on the server** (except the last step, which is
on your admin repo clone), even if you installed it using the easy install
script on the client. First, it's not as difficult as an install so you don't
really need a script. Second, you may have customised the "rc" file
(`~/.gitolite.rc` on the server) and I'm reluctant to mess with that in an
automated way.
### general upgrade notes
If you follow the steps below, you can make the upgrade "atomic", so you don't
have to do it at a "quiet" time or something.
1. untar the new version to some temp directory and `cd` to it
1. copy a tar file containing the new version to the server, untar it to some
temp directory and `cd` to it
2. *prepare* the new version of `~/.gitolite.rc`. It **must** have **all**
the variables defined in `conf/example.gitolite.rc` (the "new" rc file),
@ -31,12 +39,11 @@ have to do it at a "quiet" time or something.
src/install.pl
5. compile the config once again, in case the *internal* format of the
compiled config file (`$GL_CONF_COMPILED`) has changed
compiled config file (`$GL_CONF_COMPILED`) has changed.
src/gl-compile-conf
(if you've already setup "push-to-admin", this step should be replaced by
a "git push". Make a dummy commit if needed, to make the push happen).
To do this, you have to do a "git push" on the client side. That might
require a dummy change (maybe add a blank line somewhere) because
otherwise the push will not happen.
And you're done.
@ -45,6 +52,27 @@ And you're done.
If any extra steps beyond the generic ones above are needed, they will be
listed here, newest first.
#### upgrading from 410c9ba
Between 410c9ba and this version, gitolite managed to make "push to admin" the
default for new installs, but in a much more painless way. If you're
upgrading, you're not forced to use "push to admin", but I'd suggest you:
* make sure you have password-less (pubkey) login to a command line on your
server
* save your `~/.gitolite.rc`, `keydir/*.pub` and your `conf/gitolite.conf`
files from the server, bring them to your workstation
* then run `src/00-easy-install.sh` on the workstation, as if it were a
fresh install
* when the editor pops up to edit the rc file, delete all the lines in
it and copy them from the saved `~/.gitolite.rc`
* at the end of the script, after the gitolite-admin repo has been
cloned successfully, copy the saved `conf/gitolite.conf` and
`keydir/*.pub` to the clone, then add, commit, and push
Gitolite also learnt to delegate parts of the config to other users. See
`doc/5-delegation.mkd` for details.
#### upgrading from 8217ef9
Between 8217ef9 and this version, gitolite learnt to handle gitweb/daemon

View file

@ -3,14 +3,9 @@
[TODO: make the migration tool fix up gitweb and daemon control also...]
Migrating from gitosis to gitolite is pretty easy, because the basic design is
the same. The differences are:
the same.
* gitolite does not use a special repo for the configuration, pubkeys, etc.
You can choose to version that directory but it is not required that you
do so
Here's how we migrated my work repos (note: substitute real paths, from your
`~/.gitolite.rc`, for `$REPO_BASE` and `$GL_ADMINDIR` below):
Here's how we migrated my work repos:
1. login as the `git` user on the server, and get a bash shell prompt
@ -18,10 +13,17 @@ Here's how we migrated my work repos (note: substitute real paths, from your
else. This will prevent users from pushing anything while you do the
backup, migration, etc.
3. For added safety, **delete** the post-update hook that gitosis-admin
3. **edit** `~/.ssh/authorized_keys` and **carefully** remove all the lines
containing "gitosis-serve", as well as the marker line that says
"auto-generated by gitosis, DO NOT REMOVE", then save the file. If the
file did not have any other keys and is now empty, don't worry -- save it
anyway because gitolite expects the file to be present (even if it is
empty).
4. For added safety, **delete** the post-update hook that gitosis-admin
installed
rm $REPO_BASE/gitosis-admin.git/hooks/post-update
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
@ -30,39 +32,34 @@ Here's how we migrated my work repos (note: substitute real paths, from your
If you do not do this, an accidental push to the gitosis-admin repo will
mess up your `~/.ssh/authorized_keys` file
4. take a **backup** of the `$REPO_BASE` directory
5. take a **backup** of the `~/repositories` directory
5. untar gitolite to some temporary directory and follow the instructions to
**install** it using `src/install.pl`
Now, log off the server and get back to the client:
6. **convert** your gitosis config file:
1. follow instructions to install gitolite; see install document. Make sure
that you **don't** change the default path for `$REPO_BASE`!
cd $GL_ADMINDIR
src/conf-convert.pl < ~/.gitosis.conf > conf/gitolite.conf
2. **convert** your gitosis 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`
be sure to check the file to make sure it converted correctly
src/conf-convert.pl < $GSAC/gitosis.conf > $GLAC/gitolite.conf
7. **copy** the update hook to each of the existing repos (if you have repos
in subdirectories, this won't work as is; adapt it):
Be sure to check the file to make sure it converted correctly
for i in $REPO_BASE/*.git
do
cp src/update-hook.pl $i/hooks/update
done
3. **copy** the keys from gitosis's keydir (same meanings for GSAC and GLAC)
8. **copy** the keys from gitosis's keydir
cp $GSAC/keydir/* $GLAC/keydir
cp $REPO_BASE/gitosis-admin.git/gitosis-export/keydir/* keydir
9. **Important: expand** any multi-key files you may have. See the "faq,
4. **Important: expand any multi-key files you may have**. See the "faq,
tips, etc" document in the doc directory for 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):
copy-paste it into your xterm after "cd"-ing to your gitolite-admin repo
clone):
cd $GL_ADMINDIR
wc -l keydir/*.pub | grep -v total | grep -v -w 1 | while read a b
do
i=1
@ -82,12 +79,4 @@ Here's how we migrated my work repos (note: substitute real paths, from your
"sitaram@laptop.pub" and "sitaram@desktop.pub" or whatever. *Please check
the files to make sure this worked properly*
10. **edit** `~/.ssh/authorized_keys` and **carefully** remove all the lines
containing "gitosis-serve", as well as the marker line that says
"auto-generated by gitosis, DO NOT REMOVE", then save the file. If the
file did not have any other keys and is now empty, don't worry -- save it
anyway because gitolite expects the file to be present (even if it is
empty).
At this point you're ready to "compile" the configuration. See the "admin"
document for what to do, and how to check the outputs, etc.
5. Check all your changes to your gitolite-admin clone, commit, and push

View file

@ -22,15 +22,15 @@ Please read on to see how to do this correctly.
extension, like `sitaram.pub` or `john-smith.pub`. You can also use
periods and underscores
* copy all these `*.pub` files to `$GL_KEYDIR`
* copy all these `*.pub` files to `keydir` in your gitolite-admin repo clone
* the config file (`$GL_CONF`) is very well commented, please take a couple
of minutes to read it. Then edit it and
* edit the config file (`conf/gitolite.conf` in your admin repo clone). See
`conf/example.conf` in the gitolite source 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
* add new repos as needed
* 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
#### specifying gitweb and daemon access
@ -51,41 +51,6 @@ one-time setup you must do separately. All this does is:
value you specified for `$projects_list` when setting up gitweb)
* for daemon, create the file `git-daemon-export-ok` in the repository
`src/gl-compile-conf` will keep these files consistent with the config
settings -- this includes removing such settings if you remove "read"
permissions for the special usernames.
#### compiling
* backup your `~/.ssh/authorized_keys` file if you feel nervous :-)
* that's "backup" as in "copy", not "move". The next step won't work if
the file doesn't exist. Even an empty one is fine but it must be
present
* if you don't have an `~/.ssh/authorized_keys` file at all, you may
have logged in with a password, which in turn might mean you are not
familiar with ssh and authkeys etc. If so, please read up at least
[this](http://sitaramc.github.com/0-installing/9-gitolite-basics.html#IMPORTANT_overview_of_ssh),
and preferably also the man pages for sshd and sshd\_config, to make
sure you understand the security implications of what you are doing.
Once you have understood that, create at least an empty
`~/.ssh/authorized_keys` file before proceeding to the next step
* cd to `$GL_ADMINDIR` and run `src/gl-compile-conf`
That should be it, really. However, if you want to be doubly sure, or maybe
the first couple of times you use it, you may want to check these:
* check the outputs
* `~/.ssh/authorized_keys` should contain one line for each "user" pub
key added, between two "marker" lines (which you should please please
not remove!). The line should contain a "command=" pointing to a
`$GL_ADMINDIR/src/gl-auth-command` file, then some sshd restrictions, the
key, etc.
* `$GL_CONF_COMPILED` should contain an expanded list of the access
control rules. It may look a little long, but it's fairly intuitive!
* if the run threw up any "initialising empty repo" messages, check the
individual repos (inside `$REPO_BASE`) if you wish. Especially make sure
the `$REPO_BASE/[reponame].git/hooks/update` got copied OK and is
executable
The "compile" script will keep these files consistent with the config settings
-- this includes removing such settings if you remove "read" permissions for
the special usernames.

View file

@ -5,19 +5,22 @@ In this document:
* common errors and mistakes
* git version dependency
* other errors, warnings, notes...
* getting a tar file from a clone
* differences from gitosis
* simpler syntax
* two levels of access rights checking
* error checking the config file
* delegating parts of the config file
* easier to specify gitweb/daemon access
* built-in logging
* better logging
* one user, many keys
* support for git installed outside default PATH
* who am I?
* other cool things
* developer specific branches
* "personal" branches
* design choices
* why we don't do "excludes"
* keeping the parser and the access control separate
### common errors and mistakes
@ -37,10 +40,10 @@ In this document:
Here's a workaround for a version dependency that the normal flow of gitolite
has.
When you edit your config file to create a new repo, and run
`src/gl-compile-conf`, gitolite creates an empty, bare repo for you.
Normally, you're expected to clone this on the client side, and start working
-- make your first commit(s), then push, etc.
When you edit your config file to create a new repo, and push the changes to
the server, gitolite creates an empty, bare repo for you. Normally, you're
expected to clone this on the client side, and start working -- make your
first commit(s), then push, etc.
However, cloning an empty repo requires a server side git version that is at
least 1.6.2. Gitolite detects this when creating a repo, and warns you.
@ -74,21 +77,27 @@ normal way, since it's not empty anymore.
* if you specify a repo that is not at the top level `$REPO_BASE`, be sure
to manually create the intermediate directories first. For instance if
you specify a new repo called "a/b/c" to the config file and "compile",
the "compile" script will just `mkdir a/b/c.git`, assuming "a/b" has
already been created
* if you run `git init` inside `$GL_ADMINDIR` (that is, make it a normal,
non-bare, repo), then, everytime you "compile" (run
`src/gl-compile-conf`), any changes to `conf` and `keydir` will
automatically be committed. This is a simple safety net in case you
accidentally delete the whole config or something. Also see
[4-push-to-admin.mkd](http://github.com/sitaramc/gitolite/blob/pu/doc/4-push-to-admin.mkd)
if you really know what you're doing and want "push to admin"
you specify a new repo called "a/b/c" to the config file and push, the
"compile" script will just `mkdir a/b/c.git`, assuming "a/b" has already
been created
* gitweb not able to read your repos? You can change the umask for newly
created repos to something more relaxed -- see the `~/.gitolite.rc` file
### getting a tar file from a clone
You can clone the repo from github or indefero, then execute a make command to
extract a tar file of the branch you want. Please use the make command, not a
plain "git archive", because the Makefile adds a file called
`.GITOLITE-VERSION` that will help you identify which version you are using.
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 rebel.tar" or "make pu.tar"
### differences from gitosis
Apart from the big ones listed in the top level README, and subjective ones
@ -176,9 +185,8 @@ gitosis does not do any. I just found out that if you mis-spell `members` as
`member`, gitosis will silently ignore it, and leave you wondering why access
was denied.
In gitolite, you have to "compile" the config file first (this step takes the
place of the commit+push in gitosis), and keyword typos *are* caught so you
know right away.
Gitolite "compiles" the config file first and keyword typos *are* caught so
you know right away.
#### delegating parts of the config file
@ -219,24 +227,21 @@ bits and pieces. Here's an example, using short repo names for convenience:
repo r2
# ...and so on...
#### built-in logging
#### better logging
...just in case of emergency :-)
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.
Let's say you gave a dev the right to rewind a branch and he went and rewound
it all the way, or pushed something drastically different on it. Now you need
to recover the commit that got wiped out.
> [`*`] 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" ;-)]
If you'd remembered to `git config core.logAllRefUpdates` for that repo, or
globally, you'd be fine -- the reflog will tell you. Otherwise you'd be left
grubbing around in `git fsck --unreachable` a bit :-(
And even if you recover the correct commit, you'll never know *who* did it --
not unless you add a one-line patch to gitosis, plus a `post-receive` hook to
every repository.
With gitolite, there's a log file in `$GL_ADMINDIR` that contains lines like
this:
The log lines look like this:
2009-09-19.10:24:37 + b4e76569659939 4fb16f2a88d8b5 myrepo refs/heads/master user2 refs/heads/master
@ -259,9 +264,35 @@ each of my pubkeys. In gitolite, we keep them separate: "sitaram@laptop.pub"
and "sitaram@desktop.pub". The part before the "@" is the username, so
gitolite knows these two keys belong to the same person.
Note that you don't say "sitaram@laptop" and so on in the **config** file --
as far as the config file is concerned there's just **one** user called
"sitaram" -- so you only say "sitaram" there. Only the **pubkey files** have
the extra "@" stuff.
I think this is easier to maintain if you have to delete or change one of
those keys.
#### 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 :-)
#### who am I?
As a developer, I send a file called `id_rsa.pub` to the gitolite admin. He
@ -283,33 +314,31 @@ In gitolite, it's simple: just ask nicely :-)
### other cool things
#### developer specific branches
#### "personal" branches
So I know what gitolite calls me. Big deal... who cares?
"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.
Here is an idea: give every developer a personal "scratch" namespace within
which she can create, rewind, or delete any branch. For example, I would own
anything under
gitolite lets you define a "personal" or "scratch" namespace prefix for
each developer (e.g., `refs/personal/<devname>/*`), with full
permissions for that dev and read-only for everyone else. And you get
this without adding a single line to the access config file -- pretty
much fire and forget as far as the admin is concerned, even if there is
constant churn in the project teams.
$PERSONAL_BRANCH_PREFIX/sitaram/
Not bad for something that took just *one* line of code to implement.
And that's one clean, readable, line, by the way ;-)
The admin could set `$PERSONAL_BRANCH_PREFIX` in the rc file and communicate
The admin would set `$PERSONAL_BRANCH_PREFIX` in the rc file and communicate
this to all users. It could be something like `refs/heads/personal`, which
means all such branches will show up in `git branch` lookups and `git clone`
will fetch them. Or he could use, say, `refs/personal`, which means it won't
show up in any normal "branch-y" commands and stuff, and generally be much
less noisy.
Yes, I know git is all about allowing private branches, but in a corporate
environment it's not always possible to pull from a co-worker, for the same
reasons you don't have anonymous access (like the git:// protocol). A normal
developer workstation cannot do authentication, so how would they know who's
pulling? This is a perfect way to share code *without* cluttering the global
namespace, and each developer controls his/her own set of branches!
The amount of code needed? *One line!* I'll spend about 3x more on declaring
and initialising the new variable, and 30x more on documenting it :-)
**Note that a user who has NO write access cannot have personal branches**; if
you read the section (above) on "two levels of access rights checking" you'll
understand why.
@ -341,8 +370,31 @@ Just don't *show* the user this config file; it might sound insulting :-)
### design choices
#### keeping the parser and the access control separate
There are two programs concerned with access control:
* `gl-auth-command`, the program that is run via `~/.ssh/authorized_keys`;
this decides whether git should even be allowed to run (basic R/W/no
access). (This one cannot decide on the branch-level access; it is not
known at this point what branch is being accessed)
* the update-hook on each repo, which decides the per-branch permissions
I have chosen to keep the relatively complex task of parsing the config file
out of them to keep them simpler (and faster). So any changes to the config
have to be first "compiled", and the access control programs use this
"compiled" version of the config. (The compile step also refreshes
`~/.ssh/authorized_keys`).
If you choose the "easy install" method, all this is quite transparent to you
anyway. If you cannot use the easy install and must install manually, I have
clear instructions on how to set it up.
#### why we don't do "excludes"
[umm... having said all this, I implemented it anyway; see the "rebel"
branch!]
I found an error in the example conf file. This snippet *seems* to say that
"bruce" can write versioned tags (`refs/tags/v[0-9].*`), but the other
staffers can't:
@ -387,3 +439,5 @@ The lack of overlap between refexes ensures ***no confusion*** in specifying,
understanding, and ***auditing***, what is allowed and what is not.
And in security, "no confusion" is a good thing :-)

View file

@ -1,5 +1,10 @@
# "push to admin" in gitolite
**WARNING: THIS DOCUMENT IS OBSOLETE. DO NOT USE. IT IS RETAINED ONLY FOR
HISTORICAL PURPOSES**. Gitolite now does "push-to-admin" by default, and does
it very easily and simply by front-loading the ssh problem. See the install
doc for details.
----
Gitosis's default mode of admin is by cloning and pushing the `gitosis-admin`

362
src/00-easy-install.sh Executable file
View file

@ -0,0 +1,362 @@
#!/bin/bash
# easy install for gitolite
# you run this on the client side, and it takes care of all the server side
# work. You don't have to do anything on the server side directly
# to do a manual install (since I have tested this only on Linux), open this
# script in a nice, syntax coloring, text editor and follow the instructions
# prefixed by the word "MANUAL" in the comments below :-)
# run without any arguments for "usage" info
# important setting: bail on any errors (else we have to check every single
# command!)
set -e
die() { echo "$@"; echo; echo "run $0 again without any arguments for help and tips"; exit 1; }
prompt() {
echo
echo
echo ------------------------------------------------------------------------
echo " $1"
echo
read -p '...press enter to continue or Ctrl-C to bail out'
}
usage() {
cat <<EOFU
Usage: $0 user host port admin_name
- "user" is the username on the server where you will be installing gitolite
- "host" is that server's hostname (or IP address is also fine)
- "port" is optional
- "admin_name" is *your* name as you want it to appear in the eventual
gitolite config file
Example usage: $0 git my.git.server sitaram
Output:
- a proper gitolite admin repo in $HOME/gitolite-admin
Notes:
- "user" and "admin_name" must be simple names -- no special characters etc
please (only alphanumerics, dot, hyphen, underscore)
- traditionally, the "user" is "git", but it can be anything you want
- "admin_name" should be your name, for clarity, or whoever will be the
gitolite admin
Pre-requisites:
- you must run this from the gitolite working tree top level directory.
This means you run this as "src/00-easy-install.sh"
- you must already have pubkey based access to user@host. If you currently
only have password access, use "ssh-copy-id" or something. Somehow get to
the point where you can type "ssh user@host" and get a command line. Run
this program only after that is done
Errors:
- if you get a "pubkey [...filename...] exists" error, it is either leftover
from a previous, failed, run, or a genuine file you need. Decide which it
is, and remove it and retry, or use a different "admin_name", respectively.
EOFU
exit 1;
}
# ----------------------------------------------------------------------
# basic sanity / argument checks
# ----------------------------------------------------------------------
# MANUAL: this *must* be run as "src/00-easy-install.sh", not by cd-ing to src
# and then running "./00-easy-install.sh"
[[ $0 =~ ^src/00-easy-install.sh$ ]] ||
{
echo "please cd to the gitolite repo top level directory and run this as
'src/00-easy-install.sh'"
exit 1;
}
# MANUAL: (info) we'll use "git" as the user, "server" as the host, and
# "sitaram" as the admin_name in example commands shown below, if any
[[ -z $3 ]] && usage
user=$1
host=$2
admin_name=$3
# but if the 3rd arg is a number, that's a port number, and the 4th arg is the
# admin_name
port=22
[[ $3 =~ ^[0-9]+$ ]] && {
port=$3
[[ -z $4 ]] && usage
admin_name=$4
}
[[ "$user" =~ [^a-zA-Z0-9._-] ]] && die "user '$user' invalid"
[[ "$admin_name" =~ [^a-zA-Z0-9._-] ]] && die "admin_name '$admin_name' invalid"
# MANUAL: make sure you're in the gitolite directory, at the top level.
# The following files should all be visible:
ls src/gl-auth-command \
src/gl-compile-conf \
src/install.pl \
src/update-hook.pl \
conf/example.conf \
conf/example.gitolite.rc >/dev/null ||
die "cant find at least some files in gitolite sources/config; aborting"
# MANUAL: make sure you have password-less (pubkey) auth on the server. That
# is, running "ssh git@server" should log in straight away, without asking for
# a password
ssh -p $port -o PasswordAuthentication=no $user@$host true ||
die "pubkey access didn't work; please set it up using 'ssh-copy-id' or something"
# MANUAL: make sure there's no "gitolite-admin" directory in $HOME (actually
# for the manual flow this doesn't matter so much!)
[[ -d $HOME/gitolite-admin ]] &&
die "please delete or move aside the \$HOME/gitolite-admin directory"
# MANUAL: create a new key for you as a "gitolite user" (as opposed to you as
# the "gitolite admin" who needs to login to the server and get a command
# line). For example, "ssh-keygen -t rsa ~/.ssh/sitaram"; this would create
# two files in ~/.ssh (sitaram and sitaram.pub)
prompt "the next command will create a new keypair for your gitolite access
The pubkey will be $HOME/.ssh/$admin_name.pub. You will have to
choose a passphrase or hit enter for none. I recommend not having a
passphrase for now, and adding one with 'ssh-keygen -p' *as soon as*
all the setup is done and you've successfully cloned and pushed the
gitolite-admin repo.
After that, I suggest you (1) install 'keychain' or something
similar, and (2) add the following command to your bashrc (since
this is a non-default key)
ssh-add \$HOME/.ssh/$admin_name
This makes using passphrases very convenient."
if [[ -f $HOME/.ssh/$admin_name.pub ]]
then
prompt "Hmmm... pubkey $HOME/.ssh/$admin_name.pub exists; should I just re-use it?
Be sure you remember the passphrase, if you gave one when you created it!"
else
ssh-keygen -t rsa -f $HOME/.ssh/$admin_name || die "ssh-keygen failed for some reason..."
fi
# MANUAL: copy the pubkey created to the server, say to /tmp. This would be
# "scp ~/.ssh/sitaram.pub git@server:/tmp" (the script does this at a later
# stage, you do it now for convenience). Note: only the pubkey (sitaram.pub).
# Do NOT copy the ~/.ssh/sitaram file -- that is a private key!
# MANUAL: if you're running ssh-agent (see if you have an environment variable
# called SSH_AGENT_PID in your "env"), you should add this new key. The
# command is "ssh-add ~/.ssh/sitaram"
if ssh-add -l &>/dev/null
then
prompt "you're running ssh-agent. We'll try and do an ssh-add of the
private key we just created, otherwise this key won't get picked up. If
you specified a passphrase in the previous step, you'll get asked for one
now -- type in the same one."
ssh-add $HOME/.ssh/$admin_name
fi
# MANUAL: you now need to add some lines to the end of your ~/.ssh/config
# file. If the file doesn't exist, create it. Make sure the file is "chmod
# 644".
# The lines to be included look like this:
# host gitolite
# user git
# hostname server
# port 22
# identityfile ~/.ssh/sitaram
echo "
host gitolite
user $user
hostname $host
port $port
identityfile ~/.ssh/$admin_name" > $HOME/.ssh/.gl-stanza
if grep 'host *gitolite' $HOME/.ssh/config &>/dev/null
then
prompt "your \$HOME/.ssh/config already has settings for gitolite. I will
assume they're correct, but if they're not, please edit that file, delete
that paragraph (that line and the following few lines), Ctrl-C, and rerun.
In case you want to check right now (from another terminal) if they're
correct, here's what they are *supposed* to look like:
$(cat ~/.ssh/.gl-stanza)"
else
prompt "creating settings for your gitolite access in $HOME/.ssh/config;
these are the lines that will be appended to your ~/.ssh/config:
$(cat ~/.ssh/.gl-stanza)"
cat $HOME/.ssh/.gl-stanza >> $HOME/.ssh/config
# if the file didn't exist at all, it might have the wrong permissions
chmod 644 $HOME/.ssh/config
fi
rm $HOME/.ssh/.gl-stanza
# ----------------------------------------------------------------------
# client side stuff almost done; server side now
# ----------------------------------------------------------------------
# MANUAL: copy the gitolite directories "src", "conf", and "doc" to the
# server, to a directory called (for example) "gitolite-install". You may
# have to create the directory first.
ssh -p $port $user@$host mkdir -p gitolite-install
rsync -e "ssh -p $port" -a src conf doc $user@$host:gitolite-install/
# MANUAL: now log on to the server (ssh git@server) and get a command line.
# This step is for your convenience; the script does it all from the client
# side but that may be too much typing for manual use ;-)
# MANUAL: cd to the "gitolite-install" directory where the sources are. Then
# copy conf/example.gitolite.rc as ~/.gitolite.rc and edit it if you wish to
# change any paths. Make a note of the GL_ADMINDIR and REPO_BASE paths; you
# will need them later
prompt "the gitolite rc file needs to be edited by hand. The defaults
are sensible, so if you wish, you can just exit the editor.
Otherwise, make any changes you wish and save it. Read the comments to
understand what is what -- the rc file's documentation is inline.
Please remember this file will actually be copied to the server, and that
all the paths etc. represent paths on the server!"
# lets try and get the file from there first
if scp -P $port $user@$host:.gitolite.rc .
then
prompt "Oh hey... you already had a '.gitolite.rc' file on the server.
Let's see if we can use that instead of the default one..."
sort < .gitolite.rc | perl -ne 'print "$1\n" if /^(\$\w+) *=/' > glrc.old
sort < conf/example.gitolite.rc | perl -ne 'print "$1\n" if /^(\$\w+) *=/' > glrc.new
if diff -u glrc.old glrc.new
then
${VISUAL:-${EDITOR:-vi}} .gitolite.rc
else
prompt " looks like you're upgrading! I'm going to run your editor
with *both* the old and the new files (in that order), so you can add
in the lines pertaining to the variables shown with a '+' sign in the
above diff. This is necessary; please dont skip this
[It's upto you to figure out how your editor handles 2 filename
arguments, switch between them, copy lines, etc ;-)]"
${VISUAL:-${EDITOR:-vi}} .gitolite.rc conf/example.gitolite.rc
fi
else
cp conf/example.gitolite.rc .gitolite.rc
${VISUAL:-${EDITOR:-vi}} .gitolite.rc
fi
# copy the rc across
scp -P $port .gitolite.rc $user@$host:
prompt "ignore any 'please edit this file' or 'run this command' type
lines in the next set of command outputs coming up. They're only relevant
for a manual install, not this one..."
# extract the GL_ADMINDIR and REPO_BASE locations
GL_ADMINDIR=$(ssh -p $port $user@$host "perl -e 'do \".gitolite.rc\"; print \$GL_ADMINDIR'")
REPO_BASE=$( ssh -p $port $user@$host "perl -e 'do \".gitolite.rc\"; print \$REPO_BASE'")
# MANUAL: still in the "gitolite-install" directory? Good. Run
# "src/install.pl"
ssh -p $port $user@$host "cd gitolite-install; src/install.pl"
# MANUAL: setup the initial config file. Edit $GL_ADMINDIR/conf/gitolite.conf
# and add at least the following lines to it:
# repo gitolite-admin
# RW+ = sitaram
echo "#gitolite conf
#please see conf/example.conf for details on syntax and features
repo gitolite-admin
RW+ = $admin_name
repo testing
RW+ = @all
" > gitolite.conf
# send the config and the key to the remote
scp -P $port gitolite.conf $user@$host:$GL_ADMINDIR/conf/
scp -P $port $HOME/.ssh/$admin_name.pub $user@$host:$GL_ADMINDIR/keydir
# MANUAL: cd to $GL_ADMINDIR and run "src/gl-compile-conf"
ssh -p $port $user@$host "cd $GL_ADMINDIR; src/gl-compile-conf"
# ----------------------------------------------------------------------
# hey lets go the whole hog on this; setup push-to-admin!
# ----------------------------------------------------------------------
# MANUAL: make the first commit in the admin repo. This is a little more
# complex, so read carefully and substitute the correct paths. What you have
# to do is:
# cd $REPO_BASE/gitolite-admin.git
# GIT_WORK_TREE=$GL_ADMINDIR git add conf/gitolite.conf keydir
# GIT_WORK_TREE=$GL_ADMINDIR git commit -am start
# Substitute $GL_ADMINDIR and $REPO_BASE appropriately. Note there is no
# space around the "=" in the second and third lines.
echo "cd $REPO_BASE/gitolite-admin.git
GIT_WORK_TREE=$GL_ADMINDIR git add conf/gitolite.conf keydir
GIT_WORK_TREE=$GL_ADMINDIR git commit -am start --allow-empty
" | ssh -p $port $user@$host
# MANUAL: now that the admin repo is created, you have to set the hooks
# properly. The install program does this. So cd back to the
# "gitolite-install" directory and run "src/install.pl"
ssh -p $port $user@$host "cd gitolite-install; src/install.pl"
prompt "now we will clone the gitolite-admin repo to your workstation
and see if it all hangs together. We'll do this in your \$HOME for now,
and you can move it elsewhere later if you wish to."
# MANUAL: you're done! Log out of the server, come back to your workstation,
# and clone the admin repo using "git clone gitolite:gitolite-admin.git"!
cd $HOME
git clone gitolite:gitolite-admin.git
# MANUAL: be sure to read the message below; this applies to you too...
echo
echo
echo ------------------------------------------------------------------------
echo "Cool -- we're done. Now you can edit the config file (currently
in ~/gitolite-admin/conf/gitolite.conf) to add more repos, users, etc.
When done, 'git add' the changed files, 'git commit' and 'git push'.
Read the comments in conf/example.conf for information about the config
file format -- like the rc file, this also has inline documentation.
Your URL for cloning any repo on this server will be
gitolite:reponame.git
However, any other users you set up will have to use
$user@$host:reponame.git
unless they also create similar settings in their '.ssh/config' file."

View file

@ -24,13 +24,16 @@ use warnings;
# ----------------------------------------------------------------------------
our ($GL_LOGT, $GL_CONF_COMPILED, $REPO_BASE);
our ($GL_LOGT, $GL_CONF_COMPILED, $REPO_BASE, $GIT_PATH);
our %repos;
my $glrc = $ENV{HOME} . "/.gitolite.rc";
die "parse $glrc failed: " . ($! or $@) unless do $glrc;
die "parse $GL_CONF_COMPILED failed: " . ($! or $@) unless do $GL_CONF_COMPILED;
# add a custom path for git binaries, if specified
$ENV{PATH} .= ":$GIT_PATH" if $GIT_PATH;
# ----------------------------------------------------------------------------
# definitions specific to this program
# ----------------------------------------------------------------------------

View file

@ -47,7 +47,7 @@ $Data::Dumper::Indent = 1;
# common definitions
# ----------------------------------------------------------------------------
our ($GL_ADMINDIR, $GL_CONF, $GL_KEYDIR, $GL_CONF_COMPILED, $REPO_BASE, $REPO_UMASK, $PROJECTS_LIST);
our ($GL_ADMINDIR, $GL_CONF, $GL_KEYDIR, $GL_CONF_COMPILED, $REPO_BASE, $REPO_UMASK, $PROJECTS_LIST, $GIT_PATH);
# now that this thing *may* be run via "push to admin", any errors have to
# grab the admin's ATTENTION so he won't miss them among the other messages a
@ -57,6 +57,9 @@ my $ATTN = "\n\t\t***** ERROR *****\n ";
my $glrc = $ENV{HOME} . "/.gitolite.rc";
die "$ATTN parse $glrc failed: " . ($! or $@) unless do $glrc;
# add a custom path for git binaries, if specified
$ENV{PATH} .= ":$GIT_PATH" if $GIT_PATH;
# ----------------------------------------------------------------------------
# definitions specific to this program
# ----------------------------------------------------------------------------

View file

@ -3,7 +3,7 @@
use strict;
use warnings;
our ($REPO_BASE, $GL_ADMINDIR, $GL_CONF);
our ($REPO_BASE, $GL_ADMINDIR, $GL_CONF, $GIT_PATH);
# wrapper around mkdir; it's not an error if the directory exists, but it is
# an error if it doesn't exist and we can't create it
@ -33,6 +33,9 @@ unless (-f $glrc) {
# ok now $glrc exists; read it to get the other paths
die "parse $glrc failed: " . ($! or $@) unless do $glrc;
# add a custom path for git binaries, if specified
$ENV{PATH} .= ":$GIT_PATH" if $GIT_PATH;
# mkdir $REPO_BASE, $GL_ADMINDIR if they don't already exist
my $repo_base_abs = ( $REPO_BASE =~ m(^/) ? $REPO_BASE : "$ENV{HOME}/$REPO_BASE" );
wrap_mkdir($repo_base_abs);
@ -66,7 +69,10 @@ for my $repo (`find . -type d -name "*.git"`) {
}
# oh and one of those repos is a bit more special and has an extra hook :)
system("cp $GL_ADMINDIR/src/pta-hook.sh gitolite-admin.git/hooks/post-update");
if ( -d "gitolite-admin.git/hooks" ) {
print STDERR "copying post-update hook to gitolite-admin repo...\n";
system("cp -v $GL_ADMINDIR/src/pta-hook.sh gitolite-admin.git/hooks/post-update");
system("perl", "-i", "-p", "-e", "s(export GL_ADMINDIR=.*)(export GL_ADMINDIR=$GL_ADMINDIR)",
"gitolite-admin.git/hooks/post-update");
chmod 0755, "gitolite-admin.git/hooks/post-update";
}