From d03152316f0d0d96c2a6c11c5ee6c7b31b0dc7d8 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Thu, 24 Dec 2009 22:35:43 +0530 Subject: [PATCH 01/20] install transcript --- doc/7-install-transcript.mkd | 211 +++++++++++++++++++++++++++++++++++ 1 file changed, 211 insertions(+) create mode 100644 doc/7-install-transcript.mkd diff --git a/doc/7-install-transcript.mkd b/doc/7-install-transcript.mkd new file mode 100644 index 0000000..026c98e --- /dev/null +++ b/doc/7-install-transcript.mkd @@ -0,0 +1,211 @@ +# gitolite install transcript + +This is a *complete* transcript of a full gitolite install, *from scratch*, +using brand new userids ("sita" on the client, "git" on the server). Please +note that you can use existing userids also, it is not necessary to use +dedicated user IDs for this. Also, you don't have to use some *other* server +for all this, both server and client can be "localhost" if you like. + +Please note that this entire transcript can be summarised as: + + * create users on client and server (optional) + * get pubkey access to server from client (`ssh-copy-id` or manual eqvt) + * run one command ***on client*** (`gl-easy-install`) + +...and only that last step is actually gitolite. In fact, the bulk of the +transcript is **non**-gitolite stuff :) + +---- + +### create userids on server and client (optional) + +Client side: add user, give him a password + + sita-lt:~ # useradd sita + + sita-lt:~ # passwd sita + Changing password for user sita. + New UNIX password: + Retype new UNIX password: + passwd: all authentication tokens updated successfully. + +Server side: (log on to server, then) add user, give it a password + + sita-lt:~ # ssh sitaram@server + sitaram@server's password: + Last login: Fri Dec 18 20:25:06 2009 + -bash-3.2$ su - + Password: + + sita-sv:~ # useradd git + + sita-sv:~ # passwd git + Changing password for user git. + New UNIX password: + Retype new UNIX password: + passwd: all authentication tokens updated successfully. + +Server side: allow ssh access to "git" user + +This is done by editing the sshd config file and adding "git" to the +"AllowUsers" list (the grep command is just confirming the change we made, +because I'm not showing the actual "vi" session): + + sita-sv:~ # vim /etc/ssh/sshd_config + + sita-sv:~ # grep -i allowusers /etc/ssh/sshd_config + AllowUsers sitaram git + + sita-sv:~ # service sshd restart + Stopping sshd: [ OK ] + Starting sshd: [ OK ] + +---- + +### get pubkey access from client to server + +This involves creating a keypair for yourself (using `ssh-keygen`), and +copying the public part of that keypair to the `~/.ssh/authorized_keys` file +on the server (using `ssh-copy-id`, if you're on Linux, or the manual method +described in the `ssh-copy-id` section in `doc/3-faq-tips-etc.mkd`). + + sita-lt:~ $ su - sita + Password: + + sita@sita-lt:~ $ ssh-keygen + Generating public/private rsa key pair. + Enter file in which to save the key (/home/sita/.ssh/id_rsa): + Created directory '/home/sita/.ssh'. + Enter passphrase (empty for no passphrase): + Enter same passphrase again: + Your identification has been saved in /home/sita/.ssh/id_rsa. + Your public key has been saved in /home/sita/.ssh/id_rsa.pub. + The key fingerprint is: + 8a:e0:60:1b:04:58:68:50:a4:d7:d0:3a:a5:2d:bf:0a sita@sita-lt.atc.tcs.com + The key's randomart image is: + +--[ RSA 2048]----+ + |===. | + |+o oo | + |o..=. | + |..= . | + |.o.+ S | + |.oo... . | + |E.. ... | + | . . | + | .. | + +-----------------+ + + sita@sita-lt:~ $ ssh-copy-id -i ~/.ssh/id_rsa git@server + git@server's password: + /usr/bin/xauth: creating new authority file /home/git/.Xauthority + Now try logging into the machine, with "ssh 'git@server'", and check in: + + .ssh/authorized_keys + + to make sure we haven't added extra keys that you weren't expecting. + +Double check to make sure you can log on to `git@server` without a password: + + sita@sita-lt:~ $ ssh git@server pwd + /home/git + +---- + +### get gitolite source + + sita@sita-lt:~ $ git clone git://github.com/sitaramc/gitolite gitolite-source + Initialized empty Git repository in /home/sita/gitolite-source/.git/ + remote: Counting objects: 1157, done. + remote: Compressing objects: 100% (584/584), done. + remote: Total 1157 (delta 756), reused 912 (delta 562) + Receiving objects: 100% (1157/1157), 270.08 KiB | 61 KiB/s, done. + Resolving deltas: 100% (756/756), done. + +### install gitolite + +Note that gitolite is installed from the *client*. The `easy-install` script +runs on the client but installs gitolite on the server! + + sita@sita-lt:~ $ cd gitolite-source/src + + **This is the only gitolite specific command in a typical +install sequence**. Run it without any arguments to see a usage +message. Run it without the `-q` to get a more verbose, pause-at-every-step, +install mode that allows you to change the defaults etc. + + + sita@sita-lt:src $ ./gl-easy-install -q git server sitaram + you are upgrading (or installing first-time) to v0.95-38-gb0ce84d + setting up keypair... + Generating public/private rsa key pair. + Enter passphrase (empty for no passphrase): + Enter same passphrase again: + Your identification has been saved in /home/sita/.ssh/sitaram. + Your public key has been saved in /home/sita/.ssh/sitaram.pub. + The key fingerprint is: + 2a:8e:88:42:36:7e:71:e8:cc:ff:4c:54:64:8e:cf:19 sita@sita-lt.atc.tcs.com + The key's randomart image is: + +--[ RSA 2048]----+ + | o | + | = | + | . E | + | + o | + | . .S+ | + | + o ... | + |+ = + .. | + |oo B .o | + |+ o o..o | + +-----------------+ + creating gitolite para in ~/.ssh/config... + finding/creating gitolite rc... + installing/upgrading... + Initialized empty Git repository in /home/git/repositories/gitolite-admin.git/ + Initialized empty Git repository in /home/git/repositories/testing.git/ + Pseudo-terminal will not be allocated because stdin is not a terminal. + fatal: No HEAD commit to compare with (yet) + [master (root-commit) 2f40d4b] start + 2 files changed, 13 insertions(+), 0 deletions(-) + create mode 100644 conf/gitolite.conf + create mode 100644 keydir/sitaram.pub + cloning gitolite-admin repo... + Initialized empty Git repository in /home/sita/gitolite-admin/.git/ + remote: Counting objects: 6, done. + remote: Compressing objects: 100% (4/4), done. + remote: Total 6 (delta 0), reused 0 (delta 0) + Receiving objects: 100% (6/6), done. + + + --------------------------------------------------------------- + + done! + + Reminder: + *Your* URL for cloning any repo on this server will be + gitolite:reponame.git + *Other* users you set up will have to use + git@server:reponame.git + + If this is your first time installing gitolite, please also: + tail -31 ./gl-easy-install + for next steps. + +---- + +### examine what you have + + sita@sita-lt:src $ cd ~/gitolite-admin/ + + sita@sita-lt:gitolite-admin $ git --no-pager log --stat + commit 2f40d4bb80d424dc39aae5d0973f8c1b2e395666 + Author: git + Date: Thu Dec 24 21:39:15 2009 +0530 + + start + + conf/gitolite.conf | 12 ++++++++++++ + keydir/sitaram.pub | 1 + + 2 files changed, 13 insertions(+), 0 deletions(-) + +And that's really all. Add keys to keydir here, edit conf/gitolite.conf as +needed, then add, commit, and push the changes to the server. Try out that +`tail -31 ./gl-easy-install` too :) From 79647078a3d285a7d0d6a97741f3b3bf3aa3fde0 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Fri, 25 Dec 2009 01:13:31 +0530 Subject: [PATCH 02/20] auth: regex goof on my part for those not yet able to upgrade (or until I merge this into the branch you care about), if you have a repo called, say "bk2git", just refer to it as "bk2git.git" in the clone command! [Thanks to Mark Frazer for finding this...] --- src/gl-auth-command | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gl-auth-command b/src/gl-auth-command index 1e0d1d2..d6482ed 100755 --- a/src/gl-auth-command +++ b/src/gl-auth-command @@ -96,7 +96,7 @@ if ($cmd eq 'info') { # git-receive-pack 'reponame.git' # including the single quotes -my ($verb, $repo) = ($cmd =~ /^\s*(git\s+\S+|\S+)\s+'\/?(.*?)(?:.git)?'/); +my ($verb, $repo) = ($cmd =~ /^\s*(git\s+\S+|\S+)\s+'\/?(.*?)(?:\.git)?'/); unless ( $verb and ( $verb =~ $R_COMMANDS or $verb =~ $W_COMMANDS ) and $repo and $repo =~ $REPONAME_PATT ) { # if the user is allowed a shell, just run the command exec $ENV{SHELL}, "-c", $ENV{SSH_ORIGINAL_COMMAND} if $shell_allowed; From ab3c861241b774b73e5e3b6d7abaf642f0231939 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Wed, 30 Dec 2009 22:06:16 +0530 Subject: [PATCH 03/20] Revert "easy install: needs a minor fix to accommodate auto-vivification" This reverts commit 6576e82e3342a869e18845df432ef4128e693131. On oddball configs, where the shell key is reused as the gitolite key by smart( people|-alecks), the ls-remote stops the program dead, preventing the "git add" and "git commit" that seed the admin repo. This makes extra work in terms of fixing it after the fact; removing it makes the install go further, and all you need to do is (1) delete the first line from ~/.ssh/authorized_keys on the server and (2) back on the client do a "git clone gitolite:gitolite-admin". OK so it needs to be removed. Explaining that was the easy part! The hard part is explaining why removing it is harmless. Look at the commit tree around that commit, and see that the commit before that (b78a720) was partially reverted in e7e6085. b78a720 removed the new_repo call from compile, forcing it to happen only on auth, which forced this workaround for seeding the admin repo. Since e7e6085 reverted that part of b78a720, giving back new_repo functions to compile, this line of code wasn't doing any good. QED and all that :) --- src/gl-easy-install | 1 - 1 file changed, 1 deletion(-) diff --git a/src/gl-easy-install b/src/gl-easy-install index 819eb20..6077e23 100755 --- a/src/gl-easy-install +++ b/src/gl-easy-install @@ -401,7 +401,6 @@ setup_pta() { # Substitute $GL_ADMINDIR and $REPO_BASE appropriately. Note there is no # space around the "=" in the second and third lines. - git ls-remote gitolite:gitolite-admin echo "cd $REPO_BASE/gitolite-admin.git GIT_WORK_TREE=$GL_ADMINDIR git add conf/gitolite.conf keydir GIT_WORK_TREE=$GL_ADMINDIR git diff --cached --quiet || GIT_WORK_TREE=$GL_ADMINDIR git commit -am start From 7124faa9f3ee06bb095b8eea3cea1caeab3a885f Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Thu, 7 Jan 2010 17:59:34 +0530 Subject: [PATCH 04/20] NAME-based restrictions Gitolite allows you to restrict changes by file/dir name. The syntax for this used "PATH/" as a prefix to denote such file/dir patterns. This has now been changed to "NAME/" because PATH is potentially confusing. While this is technically a backward-incompatible change, the feature itself was hitherto undocumented, and only a few people were using it, so I guess it's not that bad... Also added documentation now. --- conf/example.conf | 34 ++++++++++++++++++++++++++++++++++ doc/3-faq-tips-etc.mkd | 10 ++++++++++ src/gl-compile-conf | 10 +++++----- src/hooks/update | 10 +++++----- 4 files changed, 54 insertions(+), 10 deletions(-) diff --git a/conf/example.conf b/conf/example.conf index 2c3c6fb..a9b037c 100644 --- a/conf/example.conf +++ b/conf/example.conf @@ -165,6 +165,40 @@ repo git # looking for (`W` or `+`), or a minus (`-`), results in success, or failure, # respectively. A fallthrough also results in failure +# FILE/DIR NAME BASED RESTRICTIONS +# -------------------------------- + +# 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 + +# - 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/". + # GITWEB AND DAEMON STUFF # ----------------------- diff --git a/doc/3-faq-tips-etc.mkd b/doc/3-faq-tips-etc.mkd index a7ee05a..808cf03 100644 --- a/doc/3-faq-tips-etc.mkd +++ b/doc/3-faq-tips-etc.mkd @@ -13,6 +13,7 @@ In this document: * differences from gitosis * simpler syntax * two levels of access rights checking + * file/dir NAME based restrictions * error checking the config file * delegating parts of the config file * easier to specify gitweb "description" and gitweb/daemon access @@ -235,6 +236,15 @@ 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. +#### file/dir NAME based restrictions + +In addition to branch-name based restrictions, gitolite also allows you to +restrict what files or directories can be involved in changes being pushed. +This basically uses `git diff --name-only` to obtain the list of files being +changed, treating each filename as a "ref" to be matched. + +Please see `conf/example.conf` for syntax and examples. + #### error checking the config file gitosis does not do any. I just found out that if you mis-spell `members` as diff --git a/src/gl-compile-conf b/src/gl-compile-conf index 4c34d1d..23f26c7 100755 --- a/src/gl-compile-conf +++ b/src/gl-compile-conf @@ -200,9 +200,9 @@ sub parse_conf_file # if no ref is given, this PERM applies to all refs @refs = qw(refs/.*) unless @refs; - # fully qualify refs that dont start with "refs/" or "PATH/"; + # fully qualify refs that dont start with "refs/" or "NAME/"; # prefix them with "refs/heads/" - @refs = map { m(^(refs|PATH)/) or s(^)(refs/heads/); $_ } @refs; + @refs = map { m(^(refs|NAME)/) or s(^)(refs/heads/); $_ } @refs; # expand the user list, unless it is just "@all" @users = expand_list ( @users ) @@ -239,13 +239,13 @@ sub parse_conf_file # for 2nd level check, store each "ref, perms" pair in order for my $ref (@refs) { - # checking PATH based restrictions is expensive for + # checking NAME based restrictions is expensive for # the update hook (see the changes to src/hooks/update # in this commit for why) so we would *very* much like # to avoid doing it for the large majority of repos - # that do *not* use PATH limits. Setting a flag that + # that do *not* use NAME limits. Setting a flag that # can be checked right away will help us do that - $repos{$repo}{PATH_LIMITS} = 1 if $ref =~ /^PATH\//; + $repos{$repo}{NAME_LIMITS} = 1 if $ref =~ /^NAME\//; push @{ $repos{$repo}{$user} }, { $ref => $perms } unless $rurp_seen{$repo}{$user}{$ref}{$perms}++; } diff --git a/src/hooks/update b/src/hooks/update index 235fa40..a3c1be4 100755 --- a/src/hooks/update +++ b/src/hooks/update @@ -65,12 +65,12 @@ push @allowed_refs, @ { $repos{$ENV{GL_REPO}}{'@all'} || [] }; # prepare the list of refs to be checked # previously, we just checked $ref -- the ref being updated, which is passed -# to us by git (see man githooks). Now we also have to treat each PATH being -# updated as a potential "ref" and check that, if PATH-based restrictions have +# to us by git (see man githooks). Now we also have to treat each NAME being +# updated as a potential "ref" and check that, if NAME-based restrictions have # been specified my @refs = ($ref); # the first ref to check is the real one -if (exists $repos{$ENV{GL_REPO}}{PATH_LIMITS}) { +if (exists $repos{$ENV{GL_REPO}}{NAME_LIMITS}) { # this is special to git -- the hash of an empty tree my $empty='4b825dc642cb6eb9a060e54bf8d69288fbee4904'; # well they're not really "trees" but $empty is indeed the empty tree so @@ -78,7 +78,7 @@ if (exists $repos{$ENV{GL_REPO}}{PATH_LIMITS}) { # diff' only wants trees my $oldtree = $oldsha eq '0' x 40 ? $empty : $oldsha; my $newtree = $newsha eq '0' x 40 ? $empty : $newsha; - push @refs, map { chomp; s/^/PATH\//; $_; } `git diff --name-only $oldtree $newtree`; + push @refs, map { chomp; s/^/NAME\//; $_; } `git diff --name-only $oldtree $newtree`; } my $refex = ''; @@ -111,7 +111,7 @@ sub check_ref { # and in this version, we have many "refs" to check. The one we print in the # log is the *first* one (which is a *real* ref, like refs/heads/master), -# while all the rest (if they exist) are like PATH/something. So we do the +# while all the rest (if they exist) are like NAME/something. So we do the # first one separately to capture it, then run the rest (if any) my $log_refex = check_ref(shift @refs); check_ref($_) for @refs; From 08ef3555a1ca3797c9873f41ca293b23d7f6d12f Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Sat, 9 Jan 2010 19:57:44 +0530 Subject: [PATCH 05/20] deprecation warning about old style PATH/ syntax (this commit will probably get reverted after a suitable period has elapsed and no one is likely to still be using the old syntax). Forgetting to change it to NAME/ after is a security issue -- you end up permitting stuff you don't want to! This commit allows the old syntax but prints a warning --- src/gl-compile-conf | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/gl-compile-conf b/src/gl-compile-conf index 23f26c7..2787893 100755 --- a/src/gl-compile-conf +++ b/src/gl-compile-conf @@ -200,6 +200,8 @@ sub parse_conf_file # if no ref is given, this PERM applies to all refs @refs = qw(refs/.*) unless @refs; + # deprecation warning + map { warn "WARNING: old syntax 'PATH/' found; please use new syntax 'NAME/'\n" if s(^PATH/)(NAME/) } @refs; # fully qualify refs that dont start with "refs/" or "NAME/"; # prefix them with "refs/heads/" @refs = map { m(^(refs|NAME)/) or s(^)(refs/heads/); $_ } @refs; From 839027f7a798832a6ddc4d815672622ef085be61 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Thu, 7 Jan 2010 06:56:04 +0530 Subject: [PATCH 06/20] change delegation to NAME/ style (warning: backward compat breakage) This is a backward incompatible change. If you are using delegation and you upgrade to this version, please do the following: * change your gitolite.conf file to use the new syntax (see doc/5-delegation.mkd in this commit) * for each branch "foo" in the gitolite-admin repo, do this: # (on "master" branch) git checkout foo -- conf/fragments/foo.conf * git add all those new fragments and commit to master * delete all the branches on your clone and the server # again, for each branch foo git branch -D foo git push origin :foo --- doc/5-delegation.mkd | 34 +++++++++++++++++----------------- src/ga-post-update-hook | 23 ----------------------- 2 files changed, 17 insertions(+), 40 deletions(-) diff --git a/doc/5-delegation.mkd b/doc/5-delegation.mkd index af73b81..6c62b24 100644 --- a/doc/5-delegation.mkd +++ b/doc/5-delegation.mkd @@ -61,35 +61,35 @@ You do this by adding branches to the `gitolite-admin` repo: # the admin repo access was probably like this to start with: repo gitolite-admin - RW+ = sitaram + RW+ = sitaram # now add these lines to the config for the admin repo - RW webbrowser_repos = alice - RW webserver_repos = bob - RW malware_repos = mallory + RW NAME/conf/fragments/webbrowser_repos = alice + RW NAME/conf/fragments/webserver_repos = bob + RW NAME/conf/fragments/malware_repos = mallory + +This uses gitolite's ability to restrict pushes by file/dir name being changed +-- the syntax you see above ensures that, while "sitaram" does not have any +NAME based restrictions, the other 3 users do. See `conf/example.conf` for +syntax and notes. As you can see, **for each repo group** you want to delegate authority over, -there's a **branch with the same name** in the `gitolite-admin` repo. If you -have write access to that branch, you are allowed to define rules for repos in -that repo group. +there's a rule for a **corresponding file** in `conf/fragments` in the +`gitolite-admin` repo. If you have write access to that file, you are allowed +to define rules for repos in that repo group. -In other words, we use gitolite's per-branch permissions to "enforce" the -separation between the delegated configs! +In other words, we use gitolite's file/dir NAME-based permissions to "enforce" +the separation between the delegated configs! Here's how to use this in practice: - * Alice clones the `gitolite-admin` repo, creates (if not already created) and - checks out a new branch called `webbrowser_repos`, and adds a file called - `conf/fragments/webbrowser_repos.conf` in that branch - - * (the rest of the contents of that branch do not matter; she can keep - all the other files or delete all of them -- it doesn't make any - difference. Only that one specific file is used). + * Alice clones the `gitolite-admin` repo, and adds a file called + `conf/fragments/webbrowser_repos.conf` * she writes in this file any access control rules for the "firefox" and "lynx" repos. She should not write access rules for any other project -- they will be ignored - * Alice then commits and pushes this branch to the `gitolite-admin` repo + * Alice then commits and pushes to the `gitolite-admin` repo Naturally, a successful push invokes the post-update hook that the admin repo has, which eventually runs the compile script. The **net effect** is as if diff --git a/src/ga-post-update-hook b/src/ga-post-update-hook index 91d2bfb..b84dfa8 100755 --- a/src/ga-post-update-hook +++ b/src/ga-post-update-hook @@ -4,28 +4,5 @@ # (the GL_ADMINDIR env var would have been set by gl-auth-command) GIT_WORK_TREE=$GL_ADMINDIR git checkout -f master -# remove all fragments. otherwise, you get spurious error messages when you -# take away someone's delegation in the main config but the fragment is still -# hanging around. The ones that are valid will get re-created anyway -rm -rf $GL_ADMINDIR/conf/fragments -# collect all the delegated fragments -mkdir $GL_ADMINDIR/conf/fragments -for br in `git for-each-ref --format='%(refname:short)'` -do - # skip master (duh!) - [ "$br" = "master" ] && continue - - # all other branches *should* contain a file called .conf - # inside conf/fragments; if so copy it - if git show $br:conf/fragments/$br.conf > /dev/null 2>&1 - then - git show $br:conf/fragments/$br.conf > $GL_ADMINDIR/conf/fragments/$br.conf - echo "(extracted $br conf; `wc -l < $GL_ADMINDIR/conf/fragments/$br.conf` lines)" - else - echo " ***** ERROR *****" - echo " branch $br does not contain conf/fragments/$br.conf" - fi -done - cd $GL_ADMINDIR $GL_BINDIR/gl-compile-conf From 6c38e30e9ae2ec206913e6e0eca5dc29abee7a27 Mon Sep 17 00:00:00 2001 From: Teemu Matilainen Date: Tue, 5 Jan 2010 20:34:40 +0200 Subject: [PATCH 07/20] compile: support "include" definition Support config file including using: include "filename" If filename is not an absolute path, it is looked from the $GL_ADMINDIR/conf/ directory. For security reasons include is not allowed for fragments. Signed-off-by: Teemu Matilainen --- src/gl-compile-conf | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/gl-compile-conf b/src/gl-compile-conf index 2787893..b654e69 100755 --- a/src/gl-compile-conf +++ b/src/gl-compile-conf @@ -264,6 +264,16 @@ sub parse_conf_file $repo_config{$repo}{$key} = $value; } } + # include + elsif (/^include "(.+)"/) + { + my $file = $1; + $file = "$GL_ADMINDIR/conf/$file" unless $file =~ /^\//; + die "$WARN $fragment attempting to include configuration\n" if $fragment ne 'master'; + die "$ABRT included file not found: '$file'\n" unless -f $file; + + parse_conf_file($file, $fragment); + } # very simple syntax for the gitweb description of repo; one of: # reponame = "some description string" # reponame "owner name" = "some description string" From a9824464e5729f1c8b031b491c384a49a199d4ff Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Wed, 13 Jan 2010 14:59:45 +0530 Subject: [PATCH 08/20] update hook: anchor refex with ^ when matching refs Currently, a line like RW foo = user1 allows user1 to push any ref that contains the string refs/heads/foo. This includes refs like refs/heads/foo refs/heads/foobar refs/heads/foo/bar which is fine; that is what is intended. (You can always use foo$ instead of foo if you want to prevent the latter two). Similarly, RW refs/foo = user1 allows refs/foo refs/foobar refs/foo/bar Now, I don't see this as a "security risk" but the fact is that this allows someone to clutter your repo with junk like refs/bar/refs/heads/foo refs/heads/bar/refs/heads/foo (or, with the second config line example, refs/bar/refs/foo refs/heads/bar/refs/foo ) My personal advice is if you find someone doing that intentionally, you should probably take him out and shoot him [*], but since now *two* people have complained about this, here goes... ---- [*] you don't have to take him out if you don't want to --- src/hooks/update | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/hooks/update b/src/hooks/update index a3c1be4..c1ff18f 100755 --- a/src/hooks/update +++ b/src/hooks/update @@ -100,7 +100,7 @@ sub check_ref { for my $ar (@allowed_refs) { $refex = (keys %$ar)[0]; # refex? sure -- a regex to match a ref against :) - next unless $ref =~ /$refex/; + next unless $ref =~ /^$refex/; die "$perm $ref $ENV{GL_USER} DENIED by $refex\n" if $ar->{$refex} eq '-'; # as far as *this* ref is concerned we're ok From ecfd20e793df7f63682b412603161d7e1b5ed7e3 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Thu, 14 Jan 2010 15:14:40 +0530 Subject: [PATCH 09/20] @SHELL is now $SHELL_USERS in the rc file (warning: backward compat breakage) Stop conflating the privilege to push changes to the admin repo with the privilege to get a shell on the server. Please read doc/6 carefully before upgrading to this version. Also please ensure that the gitolite key is *not* your only means to get a command line on the server --- conf/example.conf | 16 ---------------- conf/example.gitolite.rc | 13 +++++++++++++ doc/6-ssh-troubleshooting.mkd | 32 ++++++++++++++++++++++++++++---- src/gl-compile-conf | 4 ++-- src/gl-easy-install | 26 ++++++++++++++++++++------ 5 files changed, 63 insertions(+), 28 deletions(-) diff --git a/conf/example.conf b/conf/example.conf index a9b037c..07f60ab 100644 --- a/conf/example.conf +++ b/conf/example.conf @@ -246,19 +246,3 @@ repo gitolite # 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 (--add, the value_regex, etc) are not supported. - -# SHELL ACCESS -# ------------ - -# It is possible to give certain users shell access as well as allow them to -# use gitolite features for their git repo access. The idea is to eliminate -# the need for 2 keys when both shell and gitolite access are needed. - -# To give a user shell access, add the username to the special @SHELL group: - -@SHELL = sitaram - -# Do not add people to this group indiscriminately. AUDITABILITY OF ACCESS -# CONTROL CHANGES (AND OF REPO ACCESSES) WILL BE COMPROMISED IF ADMINS CAN -# FIDDLE WITH THE ACTUAL (PLAIN TEXT) LOG FILES THAT GITOLITE KEEPS, WHICH -# THEY CAN EASILY DO IF THEY HAVE A SHELL. diff --git a/conf/example.gitolite.rc b/conf/example.gitolite.rc index 4cda90b..d53b65f 100644 --- a/conf/example.gitolite.rc +++ b/conf/example.gitolite.rc @@ -93,6 +93,19 @@ $PERSONAL=""; $GIT_PATH=""; # $GIT_PATH="/opt/bin/"; +# -------------------------------------- + +# if you want to give shell access to any gitolite user(s), name them here. +# Please see doc/6-ssh-troubleshooting.mkd for details on how this works. + +# Do not add people to this list indiscriminately. AUDITABILITY OF ACCESS +# CONTROL CHANGES (AND OF REPO ACCESSES) WILL BE COMPROMISED IF ADMINS CAN +# FIDDLE WITH THE ACTUAL (PLAIN TEXT) LOG FILES THAT GITOLITE KEEPS, WHICH +# THEY CAN EASILY DO IF THEY HAVE A SHELL. + +# syntax: space separated list of gitolite usernames in *one* string variable. +# $SHELL_USERS = "alice bob"; + # -------------------------------------- # per perl rules, this should be the last line in such a file: 1; diff --git a/doc/6-ssh-troubleshooting.mkd b/doc/6-ssh-troubleshooting.mkd index 9602dd2..570e1bd 100644 --- a/doc/6-ssh-troubleshooting.mkd +++ b/doc/6-ssh-troubleshooting.mkd @@ -8,10 +8,34 @@ and shell access]...". We've managed (thanks to an idea from Jesse Keating) to get around this. Now it *is* possible for a single key to allow both gitolite access *and* shell access. -This is done by placing such a user in a special `@SHELL` group in the -gitolite config file. As usual, please see `conf/example.conf` for more info -on this, since I'm using that as a central place to document anything -concerned with the conf file. +This is done by: + + * (**on the server**) listing all such users in a variable called + `$SHELL_USERS` in the `~/.gitolite.rc` file. For example: + + $SHELL_USERS = "alice bob"; + + (Note the syntax: a space separated list of users in one string variable). + + * (**on your client**) make at least a dummy change to your clone of the + gitolite-admin repo and push it. + +**IMPORTANT UPGRADE NOTE**: a previous implementation of this feature worked +by adding people to a special group (`@SHELL`) in the *config* file. This +meant that anyone with gitolite-admin repo write access could add himself to +the `@SHELL` group and push, thus obtaining shell. + +This is not a problem for most setups, but if someone wants to separate these +two privileges (the right to push the admin repo and the right to get a shell) +then it does pose a problem. Since the "rc" file can only be edited by +someone who already has shell access, we now use that instead, even though +this forces a change in the syntax. + +To migrate from the old scheme to the new one, add a new variable +`$SHELL_USERS` to `~/.gitolite.rc` on the server with the appropriate names in +it. **It is best to do this directly on the server *before* upgrading to this +version.** (After the upgrade is done and tested you can remove the `@SHELL` +lines from the gitolite config file). ---- diff --git a/src/gl-compile-conf b/src/gl-compile-conf index b654e69..dcd2535 100755 --- a/src/gl-compile-conf +++ b/src/gl-compile-conf @@ -52,7 +52,7 @@ $Data::Dumper::Sortkeys = 1; open STDOUT, ">", "/dev/null" if (@ARGV and shift eq '-q'); # these are set by the "rc" file -our ($GL_ADMINDIR, $GL_CONF, $GL_KEYDIR, $GL_CONF_COMPILED, $REPO_BASE, $REPO_UMASK, $PROJECTS_LIST, $GIT_PATH); +our ($GL_ADMINDIR, $GL_CONF, $GL_KEYDIR, $GL_CONF_COMPILED, $REPO_BASE, $REPO_UMASK, $PROJECTS_LIST, $GIT_PATH, $SHELL_USERS); # and these are set by gitolite.pm our ($REPONAME_PATT, $USERNAME_PATT, $AUTH_COMMAND, $AUTH_OPTIONS, $ABRT, $WARN); @@ -458,7 +458,7 @@ for my $pubkey (glob("*")) print STDERR "WARNING: pubkey $pubkey exists but user $user not in config\n" unless $user_list{$user}; $user_list{$user} = 'has pubkey'; - if ($groups{'@SHELL'}{$user}) { + if ($SHELL_USERS and $SHELL_USERS =~ /(^|\s)$user(\s|$)/) { print $newkeys_fh "command=\"$AUTH_COMMAND -s $user\",$AUTH_OPTIONS "; } else { print $newkeys_fh "command=\"$AUTH_COMMAND $user\",$AUTH_OPTIONS,no-pty "; diff --git a/src/gl-easy-install b/src/gl-easy-install index 6077e23..e8a000b 100755 --- a/src/gl-easy-install +++ b/src/gl-easy-install @@ -302,12 +302,16 @@ copy_gl() { prompt " ...trying to reuse existing rc" \ "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 < $tmpgli/.gitolite.rc | perl -ne 'print "$1\n" if /^\s*(\$\w+) *=/' > $tmpgli/glrc.old - sort < conf/example.gitolite.rc | perl -ne 'print "$1\n" if /^\s*(\$\w+) *=/' > $tmpgli/glrc.new - if diff -u $tmpgli/glrc.old $tmpgli/glrc.new + < $tmpgli/.gitolite.rc perl -ne 'print "$1\n" if /^\s*(\$\w+) *=/' | sort > $tmpgli/glrc.old + < conf/example.gitolite.rc perl -ne 'print "$1\n" if /^\s*(\$\w+) *=/' | sort > $tmpgli/glrc.new + comm -13 $tmpgli/glrc.old $tmpgli/glrc.new > $tmpgli/glrc.comm13 + if [[ ! -s $tmpgli/glrc.comm13 ]] then [[ $quiet == -q ]] || ${VISUAL:-${EDITOR:-vi}} $tmpgli/.gitolite.rc else + echo new variables found in rc file: + cat $tmpgli/glrc.comm13 + echo # MANUAL: if you're upgrading, read the instructions below and # manually make sure your final ~/.gitolite.rc has both your existing # customisations as well as any new variables that the new version of @@ -339,6 +343,8 @@ run_install() { if ssh -p $port $user@$host cat $GL_ADMINDIR/conf/gitolite.conf &> /dev/null then upgrade=1 + ssh -p $port $user@$host cat $GL_ADMINDIR/conf/gitolite.conf 2> /dev/null | grep '@SHELL' && + prompt "" "$v_at_shell_bwi" [[ -n $admin_name ]] && echo -e "\n *** WARNING ***: looks like an upgrade... ignoring argument '$admin_name'" else [[ -z $admin_name ]] && die " *** ERROR ***: doesn't look like an upgrade, so I need a name for the admin" @@ -361,7 +367,6 @@ run_install() { # MANUAL: setup the initial config file. Edit $GL_ADMINDIR/conf/gitolite.conf # and add at least the following lines to it: -# @SHELL = sitaram # repo gitolite-admin # RW+ = sitaram @@ -369,8 +374,6 @@ initial_conf_key() { echo "#gitolite conf # please see conf/example.conf for details on syntax and features -@SHELL = $admin_name - repo gitolite-admin RW+ = $admin_name @@ -543,6 +546,17 @@ next set of command outputs coming up. They're only relevant for a manual install, not this one... " +v_at_shell_bwi=" +you are using the @SHELL feature in your gitolite config. This feature has +now changed in a backward incompatible way; see doc/6-ssh-troubleshooting.mkd +for information on migrating this to the new syntax. + +DO NOT hit enter unless you have understood that information and properly +migrated your setup, or you are sure you have shell access to the server +through some other means than the $admin_name key. + +" + v_done=" done! From d61890301fea2a920d246bafe308a173bcf50369 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Fri, 15 Jan 2010 09:27:47 +0530 Subject: [PATCH 10/20] delegation doc: minor oops I know hardly anyone is using delegation, but if you find yourself locked out from pushing because of this one little thing, do this: * on your gitolite-admin clone, add the required lines per this patch, and commit * on the server, edit ~/.gitolite/conf/gitolite.conf-compiled.pm, and delete the following line 'NAME_LIMITS' => 1 from the entry for "gitolite-admin" (if you don't know what that means delete *all* such lines) and save the file * back on your admin repo clone, do a push --- doc/5-delegation.mkd | 2 ++ 1 file changed, 2 insertions(+) diff --git a/doc/5-delegation.mkd b/doc/5-delegation.mkd index 6c62b24..9260046 100644 --- a/doc/5-delegation.mkd +++ b/doc/5-delegation.mkd @@ -63,6 +63,8 @@ You do this by adding branches to the `gitolite-admin` repo: 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/fragments/webbrowser_repos = alice RW NAME/conf/fragments/webserver_repos = bob RW NAME/conf/fragments/malware_repos = mallory From 261b289609c19143573b599ae10c8951f371874d Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Fri, 15 Jan 2010 10:40:07 +0530 Subject: [PATCH 11/20] mention NAME-based restrictions in README --- README.mkd | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.mkd b/README.mkd index b766f7e..1df3bf8 100644 --- a/README.mkd +++ b/README.mkd @@ -70,6 +70,8 @@ detail [here][gsdiff]. * 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 + * apart from branch-name based restrictions, you can also restrict by + file/dir name changed (i.e., output of `git diff --name-only`) * 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 From 645ab77af5d3854e1d8c2cef39101c2c5c21f33e Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Fri, 15 Jan 2010 14:20:00 +0530 Subject: [PATCH 12/20] compile: disallow multiple pubkeys in one file The way pubkey files are handled by gitolite, this could be used by a repo admin to get shell access. It's always been there as an undocumented emergency mechanism for an admin who lost his shell keys or overwrote them due to not understanding ssh well enough (and it has been so used at least once). But not any more... Like the @SHELL case, this reflects a shift away from treating people with repo admin rights as eqvt to people who have shell on the server, and systematically making the former lesser privileged than the latter. While in most cases (including my $DAYJOB) these two may be the same person, I am told that's not a valid assumption for others, and there've been requests to close this potential loophole. --- src/gl-compile-conf | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/gl-compile-conf b/src/gl-compile-conf index dcd2535..141f7d7 100755 --- a/src/gl-compile-conf +++ b/src/gl-compile-conf @@ -458,14 +458,20 @@ for my $pubkey (glob("*")) print STDERR "WARNING: pubkey $pubkey exists but user $user not in config\n" unless $user_list{$user}; $user_list{$user} = 'has pubkey'; + # apparently some pubkeys don't end in a newline... + my $pubkey_content = `cat $pubkey`; + $pubkey_content =~ s/\s*$/\n/; + # don't trust files with multiple lines (i.e., something after a newline) + if ($pubkey_content =~ /\n./) + { + print STDERR "WARNING: a pubkey file can only have one line (key); ignoring $pubkey\n"; + next; + } if ($SHELL_USERS and $SHELL_USERS =~ /(^|\s)$user(\s|$)/) { print $newkeys_fh "command=\"$AUTH_COMMAND -s $user\",$AUTH_OPTIONS "; } else { print $newkeys_fh "command=\"$AUTH_COMMAND $user\",$AUTH_OPTIONS,no-pty "; } - # apparently some pubkeys don't end in a newline... - my $pubkey_content = `cat $pubkey`; - $pubkey_content =~ s/\s*$/\n/; print $newkeys_fh $pubkey_content; } # lint check 3; a little more severe than the first two I guess... From e68d76f1274150a794a8e6d843569e034556411a Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Fri, 22 Jan 2010 20:05:14 +0530 Subject: [PATCH 13/20] doc/6 revamp; would appreciate reviews ;-) --- doc/3-faq-tips-etc.mkd | 2 +- doc/6-ssh-troubleshooting.mkd | 280 ++++++++++++++++++++++------------ 2 files changed, 184 insertions(+), 98 deletions(-) diff --git a/doc/3-faq-tips-etc.mkd b/doc/3-faq-tips-etc.mkd index 808cf03..9361099 100644 --- a/doc/3-faq-tips-etc.mkd +++ b/doc/3-faq-tips-etc.mkd @@ -471,7 +471,7 @@ etc. You'd just like a simple way to know what repos you have access to. Easy! Just use ssh and try to log in as if you were attempting to get a shell: - $ ssh gitolite + $ ssh gitolite info PTY allocation request failed on channel 0 hello sitaram, the gitolite version here is v0.6-17-g94ed189 you have the following permissions: diff --git a/doc/6-ssh-troubleshooting.mkd b/doc/6-ssh-troubleshooting.mkd index 570e1bd..b09c09f 100644 --- a/doc/6-ssh-troubleshooting.mkd +++ b/doc/6-ssh-troubleshooting.mkd @@ -1,116 +1,175 @@ # ssh troubleshooting -Update 2009-12-23: most of this document is now of historical interest and -will be totally revamped when I have time. For now, just note this amendment. - -The document below says "we can't use the same key for both [gitolite access -and shell access]...". We've managed (thanks to an idea from Jesse Keating) -to get around this. Now it *is* possible for a single key to allow both -gitolite access *and* shell access. - -This is done by: - - * (**on the server**) listing all such users in a variable called - `$SHELL_USERS` in the `~/.gitolite.rc` file. For example: - - $SHELL_USERS = "alice bob"; - - (Note the syntax: a space separated list of users in one string variable). - - * (**on your client**) make at least a dummy change to your clone of the - gitolite-admin repo and push it. - -**IMPORTANT UPGRADE NOTE**: a previous implementation of this feature worked -by adding people to a special group (`@SHELL`) in the *config* file. This -meant that anyone with gitolite-admin repo write access could add himself to -the `@SHELL` group and push, thus obtaining shell. - -This is not a problem for most setups, but if someone wants to separate these -two privileges (the right to push the admin repo and the right to get a shell) -then it does pose a problem. Since the "rc" file can only be edited by -someone who already has shell access, we now use that instead, even though -this forces a change in the syntax. - -To migrate from the old scheme to the new one, add a new variable -`$SHELL_USERS` to `~/.gitolite.rc` on the server with the appropriate names in -it. **It is best to do this directly on the server *before* upgrading to this -version.** (After the upgrade is done and tested you can remove the `@SHELL` -lines from the gitolite config file). - ----- - -Ssh has always been the biggest troublespot in all this. While gitolite makes -it as easy as possible, you might still run into trouble sometimes. - In this document: - * ssh sanity checks - * explanation + * basic ssh troubleshooting + * passphrases versus passwords + * ssh-agent problems + * basic ssh troubleshooting for the main admin + * basic ssh troubleshooting for a normal user + * details * files on the server * files on client * why two keys on client * more complex ssh setups * two gitolite servers to manage? - * further reading + * giving shell access to gitolite users ---- -> But before we get to all that, let's clarify that all this is applicable -> **only** to the gitolite **admin**. He's the only one who needs both a -> shell and gitolite access, so he has **two** pubkeys in play. +This document should help you troubleshoot ssh-related problems in accessing +gitolite *after* the install has completed successfully. -> Normal users have only one pubkey, since they are only allowed to access -> gitolite itself. They do not need to worry about any of this stuff, and -> their repo urls are very simple, like: `git@my.git.server:reponame.git`. +In addition, I **strongly** recommend reading [this document][glb] -- it's a +very detailed look at how gitolite uses ssh's features on the server side. +Most people don't know ssh as well as they *think* they do; even if you dont +have any problems right now, it's worth skimming over. ----- +Please also note that ssh problems don't always look like ssh problems. One +common example: when the remote says the repo you're trying to access "does +not appear to be a git repository", and yet you are sure it exists, you +haven't mis-spelled it, etc. -### ssh sanity checks +### basic ssh troubleshooting -There are two quick sanity checks you can run: +[glb]: http://sitaramc.github.com/0-installing/9-gitolite-basics.html#IMPORTANT_overview_of_ssh - * running `ssh gitolite` should get you a list of repos you have rights to - access, as described [here][myrights] +I assume the gitolite server is called "server" and the user hosting all the +gitolite repos is "git". I will also be using "sitaram" as the *gitolite +username* of the admin. + +Unless specifically mentioned, all these commands are run on the user's or +admin's workstation, not on the server. + +#### passphrases versus passwords + +When you create an ssh keypair, 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. + +Do not confuse or mistake this prompt (`Enter passphrase for key +'/home/sitaram/.ssh/id_rsa':`) for a password prompt from the remote server! + +You have two choices to avoid this prompt every time you try to access the +remote. 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 the next +section, further discussion of this is out of scope of this document. + +#### ssh-agent problems + +1. Run `ssh-add -l`. If this responds with either "The agent has no + identities." or "Could not open a connection to your authentication + agent.", skip this section. + +2. However, if it lists some keys, like this: + + 2048 fc:c1:48:1e:06:31:97:a4:8b:fc:37:b2:76:14:c7:53 /home/sitaram/.ssh/id_rsa (RSA) + 2048 d2:e0:7f:fa:1a:89:22:41:bb:06:d9:ff:a7:27:36:5c /home/sitaram/.ssh/sitaram (RSA) + + then run `ls ~/.ssh` and make sure that all the keypairs you have there + are represented in the `ssh-add -l` output. + +3. If you find any keypairs in `~/.ssh` that are not represented in the + `ssh-add -l` output, add them. For instance, if `ssh-add -l` showed me + only the `id_rsa` key, but I also had a `sitaram` (and `sitaram.pub`) + keypair, I'd run `ssh-add ~/.ssh/sitaram` to add it. + +This is because ssh-agent has a quirk: if `ssh-add -l` shows *any* keys at +all, ssh will only use those keys. Even if you explicitly specify an unlisted +key using `ssh -i` or an `identityfile` directive in the config file, it won't +use it. + +#### basic ssh troubleshooting for the main admin + +You're the "main admin" if you're trying to access gitolite from the same +workstation and user account where you ran the "easy install" command. You +should have two keypairs in your `~/.ssh` directory. The pair called `id_rsa` +(and `id_rsa.pub`) was probably the first one you created, and you used this +to get passwordless (pubkey based) access to the server (which was a +pre-requisite for running the easy install command). + +The second keypair has the same name as the last argument in the easy install +command you ran (in my case, `sitaram` and `sitaram.pub`). It was probably +created by the easy install script, and is the key used for gitolite access. + +In addition, you should have a "gitolite" paragraph in your `~/.ssh/config`, +looking something like this: + + host gitolite + user git + hostname server + identityfile ~/.ssh/sitaram + +If any of these are not true, you did something funky in your install; email +me or hop onto #git and hope for the best ;-) + +Otherwise, run these checks: + +1. `ssh git@server` should get you a command line. + + If it asks you for a password, then your `id_rsa` keypair changed after + you ran the easy install, or someone fiddled with the + `~/.ssh/authorized_keys` file on the server. + + If it prints [gitolite version and access info][myrights], you managed to + overwrite the `id_rsa` keypair with the `sitaram` keypair, or something + equally weird. + +2. `ssh gitolite info` should print some [gitolite version and access + info][myrights]. If you get the output of the GNU info command instead, + you probably reused your `id_rsa` keypair as your `sitaram` keypair, or + overwrote the `sitaram` keypair with the `id_rsa` keypair. + +There are many ways to fix this, depending on where and what the damage is. +The most generic way (and therefore time-taking) is to re-install gitolite +from scratch: + + * make a backup of your gitolite-admin repo clone somewhere (basically your + "keydir/*.pub" and your "conf/gitolite.conf"). If necessary get these + files from the server's `~/.gitolite` directory. + * log on to the server somehow (using some other account, using a password, + su-ing in, etc) and delete `~/.ssh/authorized_keys`. Rename or move aside + `~/.gitolite` so that also looks like it is missing. + * back on your workstation, make sure you have 2 keypairs (`id_rsa` and + `sitaram`, along with corresponding `.pub` files). Create them if needed. + Also make sure they are *different* and not a copy of each other :-) + * install gitolite normally: + * run `ssh-copy-id -i ~/.ssh/id_rsa git@server` to get passwordless + access to the server. (Mac users may have to do this step manually) + * make sure `ssh git@server pwd` prints the `$HOME` of `git@server` + **without** asking for a password. Do not proceed till this works. + * run easy install again, (in my case: `cd gitolite-source; + src/gl-easy-install -q git server sitaram`) + * go to your gitolite-admin repo clone, and copy `conf/gitolite.conf` and + `keydir/*.pub` from your backup to this directory + * copy (be sure to overwrite!) `~/.ssh/sitaram.pub` also to keydir + * now `git add keydir; git commit; git push -f` + +That's a long sequence but it should work. + +#### basic ssh troubleshooting for a normal user + +For a normal user, life is much simpler. They should have only one pubkey, +which was previously sent to the gitolite admin to add into the admin repo's +`keydir` as "user.pub", and then "user" given permissions to some repo. + +`ssh git@server info` should get you [gitolite version and access +info][myrights]. If it asks you for a password, your pubkey was not sent to +the server properly. Check with your admin. [myrights]: http://github.com/sitaramc/gitolite/blob/pu/doc/3-faq-tips-etc.mkd#myrights - * conversely, `ssh git@server` should get you a command line +If it gets you the GNU info command output, you have shell access. This means +you had command line access to the server *before* you were added as a +gitolite user. If you send that same key to your gitolite admin to include in +the admin repo, it won't work. For reasons why, see below. -If one or both of these does not work as expected, do this: - - * first, check that your `~/.ssh` has two public keys, like below: - - $ ls -al ~/.ssh/*.pub - -rw-r--r-- 1 sitaram sitaram 409 2008-04-21 17:42 /home/sitaram/.ssh/id_rsa.pub - -rw-r--r-- 1 sitaram sitaram 409 2009-10-15 16:25 /home/sitaram/.ssh/sitaram.pub - - If it doesn't you have either lost your keys or you're on the wrong - machine. As long as you have password access to the server you can alweys - recover; just pretend you're installing from scratch and start over. - - * next, try running `ssh-add -l`. On my desktop the output looks like this: - - 2048 63:ea:ab:10:d2:4f:88:f4:85:cb:d3:7d:3a:83:37:9a /home/sitaram/.ssh/id_rsa (RSA) - 2048 d7:23:89:12:5f:22:4f:ad:54:7d:7e:f8:f5:2a:e9:13 /home/sitaram/.ssh/sitaram (RSA) - - If you get only one line (typically the top one), you should ssh-add the - other one, using (in my case) `ssh-add ~/.ssh/sitaram`. - - If you get no output, add both of them and check `ssh-add -l` again. - - If this error keeps happening please consider installing [keychain][kch] - or something similar, or add these commands to your bash startup scripts. - -[kch]: http://www.gentoo.org/proj/en/keychain/ - - * Finally, make sure your `~/.ssh/config` has the required `host gitolite` - para (see below for more on this). - -Once these sanity checks have passed, things should be fine. However, if you -still have problems, make sure that the "origin" URL in any clones looks like -`gitolite:reponame.git`, not `git@server:reponame.git`. - -### explanation +### details Here's how it all hangs together. @@ -301,10 +360,37 @@ instance if you have *two* gitolite servers you are administering)? * now access one server's repos as `gitolite:reponame.git` and the other server's repos as `gitolite2:reponame.git`. -### further reading +### giving shell access to gitolite users -While this focused mostly on the client side ssh, you may also want to read -[this][glb] for a much more detailed explanation of the ssh magic on the -server side. +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: + + * (**on the server**) listing all such users in a variable called + `$SHELL_USERS` in the `~/.gitolite.rc` file. For example: + + $SHELL_USERS = "alice bob"; + + (Note the syntax: a space separated list of users in one string variable). + + * (**on your client**) make at least a dummy change to your clone of the + gitolite-admin repo and push it. + +**IMPORTANT UPGRADE NOTE**: a previous implementation of this feature worked +by adding people to a special group (`@SHELL`) in the *config* file. This +meant that anyone with gitolite-admin repo write access could add himself to +the `@SHELL` group and push, thus obtaining shell. + +This is not a problem for most setups, but if someone wants to separate these +two privileges (the right to push the admin repo and the right to get a shell) +then it does pose a problem. Since the "rc" file can only be edited by +someone who already has shell access, we now use that instead, even though +this forces a change in the syntax. + +To migrate from the old scheme to the new one, add a new variable +`$SHELL_USERS` to `~/.gitolite.rc` on the server with the appropriate names in +it. **It is best to do this directly on the server *before* upgrading to this +version.** (After the upgrade is done and tested you can remove the `@SHELL` +lines from the gitolite config file). -[glb]: http://sitaramc.github.com/0-installing/9-gitolite-basics.html#IMPORTANT_overview_of_ssh From c8d4aef460e84bfd90e12a9c3f741065f030d2ca Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Sat, 23 Jan 2010 14:57:22 +0530 Subject: [PATCH 14/20] compile: allow "#" in *simple* strings like: config notify.ircChannel = "#foo" (thanks, jhelwig) --- src/gl-compile-conf | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/gl-compile-conf b/src/gl-compile-conf index 141f7d7..e88819a 100755 --- a/src/gl-compile-conf +++ b/src/gl-compile-conf @@ -162,13 +162,13 @@ sub parse_conf_file my @repos; while (<$conf_fh>) { + # kill comments, but take care of "#" inside *simple* strings + s/^((".*?"|[^#"])*)#.*/$1/; # normalise whitespace; keeps later regexes very simple s/=/ = /; s/\s+/ /g; s/^ //; s/ $//; - # kill comments - s/\s*#.*//; # and blank lines next unless /\S/; From 11e8ab048af553024b32110c41d83dcb235396dd Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Mon, 25 Jan 2010 09:49:39 +0530 Subject: [PATCH 15/20] doc/6 revamp: minor addition --- doc/6-ssh-troubleshooting.mkd | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/doc/6-ssh-troubleshooting.mkd b/doc/6-ssh-troubleshooting.mkd index b09c09f..9548792 100644 --- a/doc/6-ssh-troubleshooting.mkd +++ b/doc/6-ssh-troubleshooting.mkd @@ -28,7 +28,16 @@ have any problems right now, it's worth skimming over. Please also note that ssh problems don't always look like ssh problems. One common example: when the remote says the repo you're trying to access "does not appear to be a git repository", and yet you are sure it exists, you -haven't mis-spelled it, etc. +haven't mis-spelled it, etc. Another example is being able to access +repositories using the full unix path (typically like +`git@server:repositories/reponame.git`, assuming default `$REPO_BASE` setting, +instead of specifying only the part below `$REPO_BASE`, i.e., +`git@server:reponame.git`). + +[Both these errors indicate that you managed to bypass gitolite completely and +are using your shell access -- instead of running via +`/some/path/gl-auth-command ` it is just going to bash and +working from there!] ### basic ssh troubleshooting From c3ec349721a4e07ecf2d80ef3e49f2e8198a4466 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Mon, 25 Jan 2010 12:29:01 +0530 Subject: [PATCH 16/20] sshkeys-lint: new program run without arguments for usage --- doc/6-ssh-troubleshooting.mkd | 4 ++ src/sshkeys-lint | 100 ++++++++++++++++++++++++++++++++++ 2 files changed, 104 insertions(+) create mode 100755 src/sshkeys-lint diff --git a/doc/6-ssh-troubleshooting.mkd b/doc/6-ssh-troubleshooting.mkd index 9548792..8189af8 100644 --- a/doc/6-ssh-troubleshooting.mkd +++ b/doc/6-ssh-troubleshooting.mkd @@ -25,6 +25,10 @@ very detailed look at how gitolite uses ssh's features on the server side. Most people don't know ssh as well as they *think* they do; even if you dont have any problems right now, it's worth skimming over. +In addition to both these documents, there's now a program called +`sshkeys-lint` that you can run on your client. Run it without arguments to +get help on how to run it and what inputs it needs. + Please also note that ssh problems don't always look like ssh problems. One common example: when the remote says the repo you're trying to access "does not appear to be a git repository", and yet you are sure it exists, you diff --git a/src/sshkeys-lint b/src/sshkeys-lint new file mode 100755 index 0000000..acb8d72 --- /dev/null +++ b/src/sshkeys-lint @@ -0,0 +1,100 @@ +#!/usr/bin/perl -w + +use strict; +our (%users, %linenos); + +&usage unless $ARGV[0] and -f $ARGV[0]; +my @authlines = &filelines($ARGV[0]); +my $lineno = 0; +for (@authlines) +{ + $lineno++; + if (/^# gitolite start/ .. /^# gitolite end/) { + warn "line $lineno: non-gitolite key found in gitolite section" if /ssh-rsa|ssh-dss/ and not /command=.*gl-auth-command/; + } else { + warn "line $lineno: gitolite key found outside gitolite section" if /command=.*gl-auth-command/; + } + next if /\# gitolite (start|end)/; + die "line $lineno: unrecognised line\n" unless /^(?:command=".*gl-auth-command (\S+?)"\S+ )?(?:ssh-rsa|ssh-dss) (\S+)/; + my ($user, $key) = ($1 || '', $2); + if ($linenos{$key}) { + warn "authkeys file line $lineno is repeat of line $linenos{$key}, will be ignored by server sshd\n"; + next; + } + $linenos{$key} = $lineno; + $users{$key} = ($user ? "maps to gitolite user $user" : "gets you a command line"); +} + +print "\n"; + +# all *.pub in current dir should be exactly one line, starting with ssh-rsa +# or ssh-dss + +my @pubkeys = glob("*.pub"); +die "no *.pub files here\n" unless @pubkeys; +for my $pub (@pubkeys) { + my @lines = &filelines($pub); + die "$pub has more than one line\n" if @lines > 1; + die "$pub does not start with ssh-rsa or ssh-dss\n" unless $lines[0] =~ /^(?:ssh-rsa|ssh-dss) (\S+)/; + my $key = $1; + if ($users{$key}) { + print "$pub $users{$key}\n"; + } else { + print "$pub has NO ACCESS to the server\n"; + } +} + +print <; +} + +sub usage +{ + print STDERR < Date: Mon, 25 Jan 2010 14:36:02 +0530 Subject: [PATCH 17/20] (rats! msysgit doesnt have 'comm'...) --- src/gl-easy-install | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/gl-easy-install b/src/gl-easy-install index e8a000b..9887d70 100755 --- a/src/gl-easy-install +++ b/src/gl-easy-install @@ -304,7 +304,10 @@ copy_gl() { Let's see if we can use that instead of the default one..." < $tmpgli/.gitolite.rc perl -ne 'print "$1\n" if /^\s*(\$\w+) *=/' | sort > $tmpgli/glrc.old < conf/example.gitolite.rc perl -ne 'print "$1\n" if /^\s*(\$\w+) *=/' | sort > $tmpgli/glrc.new - comm -13 $tmpgli/glrc.old $tmpgli/glrc.new > $tmpgli/glrc.comm13 + # msysgit doesn't have "comm". diff is not ideal for our purposes + # because we only care about differences in one direction, but we'll + # have to make do... + diff -u $tmpgli/glrc.old $tmpgli/glrc.new | grep '^+' > $tmpgli/glrc.comm13 if [[ ! -s $tmpgli/glrc.comm13 ]] then [[ $quiet == -q ]] || ${VISUAL:-${EDITOR:-vi}} $tmpgli/.gitolite.rc @@ -526,7 +529,7 @@ v_upgrade_glrc=" looks like you're upgrading, and there are some new rc variables that this version is expecting that your old rc file doesn't have. -I'm going to run your editor with two filenames. The first is the example +I'm going to run your \\\$EDITOR with two filenames. The first is the example file from this gitolite version. It will have a block (code and comments) for each of the variables shown above with a '+' sign. @@ -536,7 +539,7 @@ it. This is necessary; please dont skip this! -[It's upto you to figure out how your editor handles 2 filename arguments, +[It's upto you to figure out how your \\\$EDITOR handles 2 filename arguments, switch between them, copy lines, etc ;-)] " From 7afaafc54aa84ba614d07f19cb5dc284c296c686 Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Wed, 27 Jan 2010 16:48:56 +0530 Subject: [PATCH 18/20] document the "include" mechanism --- conf/example.conf | 13 +++++++++++++ doc/3-faq-tips-etc.mkd | 5 +++++ 2 files changed, 18 insertions(+) diff --git a/conf/example.conf b/conf/example.conf index 07f60ab..9d73aca 100644 --- a/conf/example.conf +++ b/conf/example.conf @@ -246,3 +246,16 @@ repo gitolite # 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 (--add, the value_regex, etc) are not supported. + +# INCLUDE SOME OTHER FILE +# ----------------------- + + include "foo.conf" + +# this includes the contents of $GL_ADMINDIR/conf/foo.conf here + +# Notes: +# - the include statement is not allowed inside delegated fragments for +# security reasons. +# - you can also use an absolute path if you like, although in the interests +# of cloning the admin-repo sanely you should avoid doing this! diff --git a/doc/3-faq-tips-etc.mkd b/doc/3-faq-tips-etc.mkd index 9361099..8b82086 100644 --- a/doc/3-faq-tips-etc.mkd +++ b/doc/3-faq-tips-etc.mkd @@ -15,6 +15,7 @@ In this document: * two levels of access rights checking * file/dir NAME based restrictions * error checking the config file + * including config lines from other files * delegating parts of the config file * easier to specify gitweb "description" and gitweb/daemon access * easier to link gitweb authorisation with gitolite @@ -254,6 +255,10 @@ was denied. Gitolite "compiles" the config file first and keyword typos *are* caught so you know right away. +#### including config lines from other files + +See the entry under "INCLUDE SOME OTHER FILE" in `conf/example.conf`. + #### delegating parts of the config file You can now split up the config file and delegate the authority to specify From 98d73965b6c42cd7670fcf799e5e9b51177b1a9b Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Wed, 27 Jan 2010 19:34:37 +0530 Subject: [PATCH 19/20] easy install: two rc file update bugs fixed The "msysgit doesnt have 'comm'" commit (from 2 days ago), had 2 bugs: - (smaller) the "+++" which was part of the diff header was triggering a spurious rc file "new variables" warning, but there were no actual variables to update - (bigger) worse, the grep command, when there were no matches, coupled with the "set -e" to kill the program right there (ouch!) --- src/gl-easy-install | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/gl-easy-install b/src/gl-easy-install index 9887d70..35c31e2 100755 --- a/src/gl-easy-install +++ b/src/gl-easy-install @@ -307,7 +307,9 @@ Let's see if we can use that instead of the default one..." # msysgit doesn't have "comm". diff is not ideal for our purposes # because we only care about differences in one direction, but we'll # have to make do... - diff -u $tmpgli/glrc.old $tmpgli/glrc.new | grep '^+' > $tmpgli/glrc.comm13 + set +e + diff -u $tmpgli/glrc.old $tmpgli/glrc.new | grep '^+.*\$' > $tmpgli/glrc.comm13 + set -e if [[ ! -s $tmpgli/glrc.comm13 ]] then [[ $quiet == -q ]] || ${VISUAL:-${EDITOR:-vi}} $tmpgli/.gitolite.rc From b4a65ab73ce7abeaccd5f23c5b0a77d069e029bc Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Sat, 30 Jan 2010 08:35:43 +0530 Subject: [PATCH 20/20] doc/3: couple of clarifications - deny rules only apply to "W" ops - be more specific about what allows "R" to pass --- doc/3-faq-tips-etc.mkd | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/doc/3-faq-tips-etc.mkd b/doc/3-faq-tips-etc.mkd index 8b82086..4f305d0 100644 --- a/doc/3-faq-tips-etc.mkd +++ b/doc/3-faq-tips-etc.mkd @@ -217,8 +217,8 @@ 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`) must be -mentioned on some line in the config for this repo. +For a "read" operation to pass this check, the username (or `@all`) must have +read permission (i.e., R, RW, or RW+) on at least one branch of the repo. 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 @@ -501,7 +501,9 @@ that code path to better use :-) ***IMPORTANT CAVEAT: if you use deny rules, the order of the rules also makes a difference, where earlier it did not. Please review your ruleset carefully or test it. In particular, do not use `@all` in a deny rule -- it won't work -as you might expect***. +as you might expect***. Also, deny rules are only processed in the second +level checks (see "two levels of access rights checking" above), which means +they only apply to write operations. 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