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 diff --git a/conf/example.conf b/conf/example.conf index 2c3c6fb..9d73aca 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 # ----------------------- @@ -213,18 +247,15 @@ repo gitolite # 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 -# ------------ +# INCLUDE SOME OTHER FILE +# ----------------------- -# 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. + include "foo.conf" -# To give a user shell access, add the username to the special @SHELL group: +# this includes the contents of $GL_ADMINDIR/conf/foo.conf here -@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. +# 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/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/3-faq-tips-etc.mkd b/doc/3-faq-tips-etc.mkd index 572d06c..cd3bdb7 100644 --- a/doc/3-faq-tips-etc.mkd +++ b/doc/3-faq-tips-etc.mkd @@ -13,7 +13,9 @@ In this document: * differences from gitosis * simpler syntax * 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 @@ -215,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 @@ -235,6 +237,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 @@ -244,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 @@ -461,7 +476,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: @@ -486,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 diff --git a/doc/5-delegation.mkd b/doc/5-delegation.mkd index af73b81..9260046 100644 --- a/doc/5-delegation.mkd +++ b/doc/5-delegation.mkd @@ -61,35 +61,37 @@ 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 = 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 + +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/doc/6-ssh-troubleshooting.mkd b/doc/6-ssh-troubleshooting.mkd index 9602dd2..8189af8 100644 --- a/doc/6-ssh-troubleshooting.mkd +++ b/doc/6-ssh-troubleshooting.mkd @@ -1,92 +1,188 @@ # 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 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. - ----- - -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. ----- +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. -### ssh sanity checks +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. 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`). -There are two quick sanity checks you can run: +[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!] - * running `ssh gitolite` should get you a list of repos you have rights to - access, as described [here][myrights] +### basic ssh troubleshooting + +[glb]: http://sitaramc.github.com/0-installing/9-gitolite-basics.html#IMPORTANT_overview_of_ssh + +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. @@ -277,10 +373,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 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 :) 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 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; diff --git a/src/gl-compile-conf b/src/gl-compile-conf index 4c34d1d..e88819a 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); @@ -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/; @@ -200,9 +200,11 @@ 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/"; + # 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|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 +241,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}++; } @@ -262,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" @@ -446,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'; - if ($groups{'@SHELL'}{$user}) { + # 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... diff --git a/src/gl-easy-install b/src/gl-easy-install index 819eb20..35c31e2 100755 --- a/src/gl-easy-install +++ b/src/gl-easy-install @@ -302,12 +302,21 @@ 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 + # 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... + 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 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 +348,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 +372,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 +379,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 @@ -401,7 +409,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 @@ -524,7 +531,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. @@ -534,7 +541,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 ;-)] " @@ -544,6 +551,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! diff --git a/src/hooks/update b/src/hooks/update index 235fa40..c1ff18f 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 = ''; @@ -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 @@ -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; 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 <