File: tips-notes.mkd

package info (click to toggle)
gitolite 2.3-1
  • links: PTS, VCS
  • area: main
  • in suites: wheezy
  • size: 1,368 kB
  • sloc: perl: 2,868; sh: 1,249; python: 143; makefile: 81
file content (413 lines) | stat: -rw-r--r-- 17,010 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
# F=tips assorted tips and notes

## common errors and mistakes

  * adding `repositories/` at the start of the repo name in the `git clone`.
    This error is typically made by the *admin* himself -- because he knows
    what `$REPO_BASE` is set to and thinks he has to provide that prefix on
    the client side also :-)  In fact gitolite prepends `$REPO_BASE`
    internally, so you shouldn't also do the same thing!

  * being able to clone but getting errors on push.  Most likely caused by a
    combination of:

      * you already have shell access to the server, not just "gitolite"
        access, *and*

      * you cloned using `git clone git@server:repositories/repo.git` (notice
        there's an extra "repositories/" in there?)

    In other words, you used a key that completely bypassed gitolite and went
    straight to the shell to do the clone.

    Please see doc/ssh-troubleshooting.mkd for what all this means.

## other errors, warnings, notes...

### cloning an empty repo

Cloning an empty repo is only possible with clients greater than 1.6.2.  So at
least one of your clients needs to have a recent git.  Once at least one
commit has been made, older clients can also use it

When you clone an empty repo, git seems to complain about `fatal: The remote
end hung up unexpectedly`.  However, you can ignore this, since it doesn't
seem to hurt anything.  [Update 2009-09-14; this has been fixed in git
1.6.4.3]

### `@all` syntax for repos

There *is* a way to use the `@all` syntax for repos also, as described in
`doc/gitolite.conf.mkd`.  However, there are a couple of minor cautions:

  * don't use `NAME/` or such restrictions on the special `@all` repo.  Due to
    the potential for defeating a crucial optimisation and slowing down *all*
    access, we do not support this.

## features

Apart from the big ones listed in the top level README, and subjective ones
like "better config file format", gitolite has evolved to have many useful
features than the original goal of branch-level access control.

### syntax and normal usage

#### #multikey one user, many keys

If you have a user who has more than one pubkey (like from different machines)
the simplest way to deal with it is to add subdirectories and add keys there.
For example, I might have these files in `keydir/`:

    sitaram.pub
    home/sitaram.pub
    laptop/sitaram.pub

##### F=oldmultikeys old style multi keys

This is an older method of enabling multi-keys.  It will continue to work and
be supported in *code*, simply because I prefer it.  But I am not going to
document it except for the example below, nor am I going to support it in
terms of questions.  Sorry.  Apparently it was too complex to understand, even
for some smart folks I know.  This tells me it was probably ill thought out
and should have been obsoleted as soon as e0fe73a was pushed.

Anyway, here's *all* the documentation for it -- some sample pubkey filenames
and the corresponding derived usernames:

  * plain username, no multikey

        sitaramc.pub                            sitaramc

  * plain username, with multikeys

        sitaramc@laptop.pub                     sitaramc
        sitaramc@desktop.pub                    sitaramc

  * email address as username, no multikey

        sitaramc@gmail.com.pub                  sitaramc@gmail.com

  * email address as username, with multikeys

        sitaramc@gmail.com@laptop.pub           sitaramc@gmail.com
        sitaramc@gmail.com@desktop.pub          sitaramc@gmail.com

### F=tipssec_ security, access control, and auditing

#### #2levels two levels of access rights checking

Gitolite has two levels of access checks.  The **first check** is what I will
call the **pre-git** level.  At this stage, the `gl-auth-command` has been
invoked by `sshd`, and it knows just three things:

  * who,
  * what repository, and
  * what type of access (R or W)

Note that at this point no git program has entered the picture, and we have no
way of knowing what **ref** (branch, tag, etc) he is trying to update, even if
it is a "write" operation.

For a "read" operation to pass this check, the username (or `@all` users) must
have read permission (i.e., R, RW, RW+, etc.) on at least one branch of the
repo (or `@all` repos).

For a "write" operation, there is an additional restriction: lines specifying
only `R` (read access) don't count.  *The user must have write access to
**some** ref in the repo in order to pass this stage!*

The **second check** is via a git `update hook`.  This check only happens for
write operations.  By this time we know what "ref" he is trying to update, as
well as the old and the new SHAs of that ref (by which we can also deduce
whether it's a rewind or not).  This is where the "per-branch" permissions
come into play.

Each refex that allows `W` access (or `+` if this is a rewind) for *this*
user, on *this* repo, is matched against the actual refname being updated.  If
any of the refexes match, the push succeeds.  If none of them match, it fails.

Gitolite also allows "exclude" or "deny" rules.  See later in this document
for details.

#### better logging

If you have been too liberal with the permission to rewind, it has built-in
logging as an emergency fallback if someone goes too far, or for audit
purposes [`*`].  The logfile names and location are configurable, and can
include the year/month/day etc in the filename for easy archival or further
processing.  The log file even tells you which pattern in the config file
matched to allow that specific access to proceed.

>   [`*`] setting `core.logAllRefUpdates true` does provide a safety net
>   against over-zealous rewinds, but it does not tell you "who".  And
>   strangely, management does not seem to share the view that "blame" is just
>   a synonym for "annotate" ;-)]

The log lines look like this:

    2009-09-19.10:24:37  +  b4e76569659939  4fb16f2a88d8b5  myrepo refs/heads/master       user2   refs/heads/master

The "+" at the start indicates a non-fast forward update, in this case from
b4e76569659939 to 4fb16f2a88d8b5.  So b4e76569659939 is the one to restore!
Can it get easier?

The other parts of the log line are the name of the repo, the refname being
updated, the user updating it, and the refex pattern (from the config file)
that matched, in case you need to debug the config file itself.

#### delegating parts of the config file

You can now split up the config file and delegate the authority to specify
access control for their own pieces.  See [delegation][deleg] for details.

### F=tnconv_ convenience features

#### what repos do I have access to?

Sometimes there are too many repos, maybe even named similarly, or with the
potential for typos, confusion about hyphens/underscores or upper/lower case,
etc.  You'd just like a simple way to know what repos you have access to.

Gitolite provides two commands ([`info`][info] and [`expand`][expand])
to help you find this information.

#### support for git installed outside default PATH

The normal solution is to add to the system default PATH somehow, either by
munging `/etc/profile` or by enabling `PermitUserEnvironment` in
`/etc/ssh/sshd_config` and then setting the PATH in `~/.ssh/.environment`.
All these are security risks because they allow a lot more than just you and
your git install :-)

And if you don't have root, you can't do this anyway.

The only solution till now has been to ask every client to set the config
parameters `remote.<name>.receivepack` and `remote.<name>.uploadpack`.  But
telling *every* client to do so is a pain...

Gitolite lets you specify the directory in which git binaries are to be found,
via a new variable (`$GIT_PATH`) in the "rc" file.  If this variable is
non-empty, it will be appended to the PATH environment variable before
attempting to run git stuff.

Very easy, very simple, and completely transparent to the users :-)

**Note**: sometimes you have a system that already has an older "git"
installed in one of the system PATHs, but you've installed a newer git in some
non-standard location and want that picked up.  Because of security reasons,
gitolite will not prepend `GIT_PATH` to the PATH variable, so the older git
comes first and it gets kinda frustrating!

Here's a simple workaround.  Ignore the `GIT_PATH` variable, and directly set
the full PATH in the rc file, like so:

    $ENV{PATH} = "/home/sitaram/bin:$ENV{PATH}";

#### #pers "personal" branches

"personal" branches are great for corporate environments, where
unauthenticated pull/clone is a no-no.  Since a dev workstation cannot do
authentication, even work shared just between 2 devs has to go *via* the
server.  This causes the same branch name clutter as in a centralised VCS,
plus setting up permissions for this becomes a chore for the admin.

Personal branches exist **in a namespace** of their own.  The syntax is

        RW+ personal/USER/  =   @userlist

where the "personal" can be anything you like (but cannot be empty), and the
"/USER/" part is **necessary (including both slashes)**.  A user "alice" (if
she's in the userlist) can then push any branches inside `personal/alice/`.
Which means she can push `personal/alice/foo` and `personal/alice/bar`, but
NOT `personal/alice`.

(Background: at runtime the "USER" component will be replaced by the name of
the invoking user.  Access is determined by the right hand side, as usual).

#### custom hooks and custom git config

You can specify hooks that you want to propagate to all repos, as well as
per-repo "gitconfig" settings.  Please see `doc/2-admin.mkd` and
`doc/gitolite.conf.mkd` for details.

#### bypassing gitolite

Sometimes you'll need to access one of the gitolite-managed repos directly on
the server, without going through gitolite.  Reasons may be some automatic
updates or some other ad hoc purposes you can dream up.

Cloning a gitolite-controlled repo is easy enough -- just use the full path
(typically `~/repositories/reponame.git`) instead of just `reponame`, to
compensate for gitolite not sitting in between and adding those things to the
repo path.

But when you push, the update hook (which git will invoke anyway) will fail
because it needs all sorts of access control info that it now doesn't have,
because the push was invoked without going through gitolite.

In order to bypass the update hook, just set the `GL_BYPASS_UPDATE_HOOK`
environment variable to "1" or something, export it, and push.  I prefer not
to set that variable permanently, preferring this mode instead:

    GL_BYPASS_UPDATE_HOOK=1 git push

#### F=adminpush gl-admin-push: bypassing gitolite for the gitolite-admin repo

The method described in the previous section (setting `GL_BYPASS_UPDATE_HOOK`)
will work for all the repos managed by gitolite, **except** for the special
`gitolite-admin` repo.  For that you will need some extra magic, because there
is also a `post-update` hook that runs here, and this needs additional
information which is NOT available if you bypass gitolite.

(Note: If your gitolite is too old to have the `gl-admin-push` program, try
`gl-dont-panic`; run it without arguments for usage info.  If you don't even
have that, it may be best to [clean][] things out more thoroughly!)

Use the `gl-admin-push` program to make changes to the admin repo *directly on
the server*.  Here's how:

  * clone the repo to some safe location and cd to it:

        cd /tmp
        git clone ~/repositories/gitolite-admin.git
        cd gitolite-admin

  * make whatever changes you want to that clone and commit.  You can add new
    keys, change the conf file, or anything at all that needs fixing up.  You
    can even reset to an older commit (rewind) if that is the simplest way to
    fix up some config problem that may have lost you your access.

  * when done, instead of `git push <push arguments>`, use this program
    instead.  For example, instead of `git push -f`, use `gl-admin-push -f`.

Note that this method will work for *any* repo, not just the special admin
repo.

#### #disable disabling write access to take backups

If you want to take normal, OS-level, backups of the system, you might want
git to be quiescent during that time, so that the backup is clean.  The best
way to do this is to disable write-access to the server for the duration of
the backup.

Here's how:

    cd $HOME    # if running as "git" user, else "cd ~git" or whatever
    echo writes disabled during backup window > .gitolite.down

    # << RUN YOUR BACKUP COMMAND(s) HERE >>

    rm .gitolite.down

I leave it to you to

  * make sure that if the backup script fails, the `.gitolite.down` file is
    still removed (or not; maybe your policy is that if the backup failed, no
    further writes are allowed.  Whatever...)
  * if you're extremely paranoid (even I wouldn't worry about this!) make sure
    that no push is *in progress* by checking for any `git-receive-pack`
    processes in a `ps` output.

### INconvenience features

#### #repodel deleting a repo

By design, there is no code in gitolite to *delete* a repo if the repo was
specified by name in the config file.  (Wildcard repos *can* be deleted by the
user; see [here][wild_repodel] for details).

If you *do* want to permanently delete a *non*-wildcard repo, here's what you
do:

  * remove the repo from the gitolite-admin repo clone's `conf/gitolite.conf`
    file.  "add" the change, commit, and push.

  * *then* remove the repo from `~/repositories` on the server (or whatever
    you set `$REPO_BASE` to in the `~/.gitolite.rc`)

#### renaming a repo

This is similar; there's no code to do this in gitolite.  What you do is:

  * log on to the server, `cd $REPO_BASE` (default: `cd ~/repositories`), and
    `mv old-name.git new-name.git`
  * back on your gitolite-admin clone, edit `conf/gitolite.conf` and replace
    all occurrences of `old-name` with `new-name`.  Then add, commit, and push
    as usual.

The order of these 2 steps is important; do not reverse them :-)

### helping with gitweb

Although gitweb is a completely separate program, gitolite can do quite a
lot to help you manage gitweb access as well; once the initial setup is
complete, you can do it all from within the gitolite config file!

If you just want gitweb to show some repositories, see [here][gwd] for how to
specify which repos to show.

#### #gitwebauth easier to link gitweb authorisation with gitolite

Over and above whether a repo is even *shown* by gitweb, you may want to
further restrict people, allowing them to view *only* those repos for which
they have been given read access by gitolite.

This requires that:

  * you have to have some sort of HTTP auth on your web server (out of my
    scope, sorry!)
  * the HTTP auth should use the same username (like "sitaram") as used in the
    gitolite config (for the corresponding user)

Normally a superuser sets up passwords for users using the "htpasswd" command,
but this is an administrative chore.

Robin Smidsrød had the *great* idea that, since each user already has pubkey
access to `git@server`, this gives us a very neat way of using gitolite to let
the users *manage their own HTTP passwords*.  Here's how:

  * setup apache so that the htaccess file it looks for is owned by the "git"
    user
  * in the `~/.gitolite.rc` file, look for the variable `$HTPASSWD_FILE` and
    point it to this file
  * tell your users to type in `ssh git@server htpasswd` to set or change
    their HTTP passwords

Of course some other authentication method can be used (e.g. `mod_ldap`) as
long as the usernames match.

Gitweb allows you to specify a subroutine to decide on access.  We use that
feature and tie it to gitolite.  Configuration example can be found in
`contrib/gitweb/`.

#### #umask umask setting

Gitweb not able to read your repos?  You can change the umask for newly
created repos to something more relaxed -- see the `REPO_UMASK` setting in the
[rc file documentation][rc].

### advanced features

There are some really cool features that are now in pretty wide use.

  * **[repos named with wildcards][wild]** is useful when some or most of your
    repos fit a pattern, avoiding the need to name repos individually in the
    config file.  New repos matching the pattern can be created by any user
    (if you give them rights to), with a set of permissions assigned to
    "roles", and the creator can then place users into those roles.

  * **[admin defined commands][ADCs]** allow controlled access to specific
    commands and scripts without giving users full shell access.

### odds and ends

#### "poking" the admin repo to force a compile

Sometimes you need to force a compile, as if you pushed the gitolite-admin
repo.  I have a git alias that looks like this:

    [alias]
        poke = !git ls-remote origin | grep -w refs/heads/poke && git push origin :poke || git push origin master:poke

so I just run `git poke`.  This toggles between deleting and creating a dummy
branch called "poke".  Either operation will trigger the hooks.