1 # the "conf" file (`conf/gitolite.conf`)
3 <center>(part 2)</center>
7 # access control rule matching
9 Access control rule matching is pretty simple. From the previous section,
10 you know what "permission", "refex", "user", and "repo" are. Here's how the
11 rules are used to decide whether to allow or deny a request.
13 Access is checked once only for "read" operations, but twice for "write"s.
15 (Note that the `deny-rules` option, mentioned below, is explained in more
16 detail in a later section.)
18 **Check #1**: the first check happens as soon as gitolite-shell receives
19 control (from sshd or httpd). gitolite-shell will pass control to
20 git-upload-pack or git-receive-pack only if this check succeeds.
22 * collect all the rules pertaining to this repo *and* this user
23 * ignore all the refexes; they don't apply to the first access check
24 * **if** the `deny-rules` option is **not** in effect for this repo, discard
25 all the `-` (deny) rules
26 * look at the rules *in sequence*:
27 * if you find a `-`, access is denied
28 * for a "read" operation (clone, fetch, ...), if you find a rule
29 containing `R`, access is allowed
30 * for a "write" operation (push), if you find a rule containing `W`,
32 * if there are no more rules left, access is denied
34 **Check #2**: the second check only happens for "push" operations. It is
35 invoked by `git-receive-pack` running the gitolite-installed `update` hook.
36 If access is denied, the update hook fails, and git then aborts the push for
37 this ref. (See `man githooks` for more.)
39 In the following description, we use the word `operation` instead of `W`,
40 because the actual operation could be a plain, fast-forward, push (`W`) or a
41 rewind/delete (`+`). <span class="gray">Other, less commonly used, values are "C", "D",
42 or "M"; see [here][write-types].</span>
44 * collect all the rules pertaining to this repo *and* this user
45 * discard all the rules where the refex does not match the ref (branch or
47 * look at the rules *in sequence*:
48 * if you find a `-`, access is denied
49 * if you find a rule containing the operation you are performing, access
51 * if there are no more rules left, access is denied
55 Just to be clear, let's work out an example of what happens when dilbert tries
56 to push a branch called "xyz".
58 We'll pretend the rule list looks like this:
61 # managers should be able to read any repo
65 # ...other rules for other repos...
69 RW+ = alice @teamleads
70 - master = dilbert @devteam
71 - refs/tags/v[0-9] = dilbert @devteam
72 RW+ dev/ = dilbert @devteam
77 After adding a default refex and expanding the supplied ones (see the
78 [refex][] section earlier), this is what it looks like. We've added line
79 numbers for convenience; we'll see why later.
81 [refex]: conf#the-refex-field
84 # managers should be able to read any repo
88 # ...other rules for other repos...
92 RW+ refs/.* = alice @teamleads
93 - refs/heads/master = dilbert @devteam
94 - refs/tags/v[0-9] = dilbert @devteam
95 RW+ refs/heads/dev/ = dilbert @devteam
96 RW refs/.* = dilbert @devteam
100 This represents a set of rules that are basically this:
102 repo user perm ref (from line)
105 foo alice RW+ refs/.* 9
106 foo @teamleads RW+ refs/.* 9
107 foo dilbert - refs/heads/master 10
108 foo @devteam - refs/heads/master 10
109 foo dilbert - refs/tags/v[0-9] 11
110 foo @devteam - refs/tags/v[0-9] 11
111 foo dilbert RW+ refs/heads/dev/ 12
112 foo @devteam RW+ refs/heads/dev/ 12
113 foo dilbert RW refs/.* 13
114 foo @devteam RW refs/.* 13
117 Which of these rules apply for dilbert? We'll assume he's not a team lead, as
118 *that* would defeat the whole purpose of this example! We *know* he's not a
119 manager, as that would defeat the whole purpose of the comic! Finally, we
120 assume he's also not part of "@devteam", (otherwise why would you name him
121 separately in all those lines?).
123 So we discard all those rules, which leaves us, for repo "foo" and user
128 - refs/heads/master 10
129 - refs/tags/v[0-9] 11
130 RW+ refs/heads/dev/ 12
133 So what happens when dilbert tries to push a branch called "xyz"?
135 At check #1, the data gitolite has is that "oper" is "W" (and ref of course is
136 unknown). We discard lines 10 and 11 (the `deny-rules` option is off by
137 default, so we ignore `-` rules). Line 12 supplies a perm of "RW+", which
138 contains "W" (the "oper") so access is allowed.
140 At check #2, the data gitolite has is that "oper" is "W" and ref is
141 `refs/heads/xyz`. We discard the first three rules, since the ref does not
142 match any of those refexes. That leaves just line 13.
144 If the push were a fast-forward push, the "oper" would be "W", and since it is
145 contained in the perm for rule 13, access is allowed.
147 However, if he were to try a rewind-push, then the "oper" would be "+", which
148 is not contained in "RW", it wouldn't match, then control would go back for
149 the *next* rule, and since there aren't any more, access would be denied.
153 # tracing the access control decision
155 <span class="gray">(v3.6.1)</span> Gitolite can help you trace this logic quickly and easily.
156 Here's one example run, with the above rules. This one tests whether dilbert
157 can push to repo foo (check #1). Note that the syntax for specifying an
158 unknown ref in this command is 'any'.
160 $ gitolite access -s foo dilbert W any
162 d => skipped deny rule due to ref unknown or 'any',
163 r => skipped due to refex not matching,
164 p => skipped due to perm (W, +, etc) not matching,
165 D => explicitly denied,
166 A => explicitly allowed,
167 F => denied due to fallthru (no rules matched)
169 d gitolite.conf:10 - refs/heads/master = dilbert @devteam
170 d gitolite.conf:11 - refs/tags/v[0-9] = dilbert @devteam
171 A gitolite.conf:12 RW+ refs/heads/dev/ = dilbert @devteam
175 Now see what happens when we try check #2 (we've omitted the legend in the
176 output, since it's always the same):
178 $ gitolite access -s foo dilbert W xyz
180 r gitolite.conf:10 - refs/heads/master = dilbert @devteam
181 r gitolite.conf:11 - refs/tags/v[0-9] = dilbert @devteam
182 r gitolite.conf:12 RW+ refs/heads/dev/ = dilbert @devteam
183 A gitolite.conf:13 RW refs/.* = dilbert @devteam
187 And if you try a force push:
189 $ gitolite access -s foo dilbert + refs/heads/xyz
191 r gitolite.conf:10 - refs/heads/master = dilbert @devteam
192 r gitolite.conf:11 - refs/tags/v[0-9] = dilbert @devteam
193 r gitolite.conf:12 RW+ refs/heads/dev/ = dilbert @devteam
194 p gitolite.conf:13 RW refs/.* = dilbert @devteam
197 + refs/heads/xyz foo dilbert DENIED by fallthru
199 I hope that was useful! Be sure you correlated the output of 'gitolite access
200 -s' with the rule workflow pictures and corresponding descriptions to cement
203 # read access respecting deny rules
205 Normally, deny rules are ignored by access check #1 (the one that runs
206 *before* git-upload-pack or git-receive-pack is called by gitolite-shell);
207 they apply only to check #2 (the update hook check).
209 But sometimes you want this "pre-git" access check to respect deny rules;
210 i.e., use the flow of check #2, not check #1. You tell gitolite to do this by
211 setting the "deny-rules" option for the repo; when you do that, the flow of
212 check #2 is used for both stages, before git *and* in the update hook.
216 Here's an example. Here, we have lots of repos, which should all be accessible
217 by gitweb or daemon, so we want the convenience provided by lines 6 and 7 (we
218 don't want to put line 7 in *each* repo). However, we also have some secret
219 repos (maybe the gitolite-admin repo and some others that we will list), which
220 we want to prevent gitweb or daemon from seeing.
224 The naive approach -- putting in a deny rule just for those repos -- doesn't
225 work. In fact nothing else seems to work either; you'll have to replace the
226 `@all` with an exhaustive list of *all repos other than the secret repos*.
229 @secret = gitolite-admin secret-repo/..*
237 # ...other repos and rules...
242 What you really want is for that repo to always use check #2, even when it
243 doesn't actually have a ref to test for.
246 @secret = gitolite-admin secret-repo/..*
249 option deny-rules = 1
254 # ...other repos and rules...
257 This is done by adding *one* line, line 4 in this example. This sets a
258 gitolite ["option"](options) that says you want "deny rules" to be applicable
259 even for read access.
261 Once you do that, all you need to do is to ensure that the first rule
262 encountered by these two "users" for those repos is a deny rule, so that it
263 can take effect first. In this example, the placement of lines 2, 3 vis-a-vis
264 lines 6, 7 matters -- don't switch them!
268 In this example the "open" repos are fewer in number, so it is the opposite
269 situation to the above in terms of our ability to enumerate all the repos.
272 @open = git gitolite foss/..* [...]
276 option deny-rules = 1
280 option deny-rules = 0
283 To see why this works, you need to understand that for [options](options) and
284 [config](git-config) lines, a later setting [overrides][override_conf] earlier
285 ones. So we set it to 1 for all repos, then selectively set it to 0 for some.
287 This means the "deny-rules" option applies to *all the repos except the "open"
288 repos*, and so the first rule encountered by gitweb and daemon is a deny rule,
289 so they are denied read access. The "open" repos, on the other hand, get the
290 normal default behaviour, which is to ignore deny rules for read access, and
291 thus they only see the "R" permission.
293 [override_conf]: git-config#overriding-config-values
297 # appendix 1: different types of write operations
299 Git supplies enough information to the update hook to be able to distinguish
300 several types of writes.
304 * `RW` -- create a ref or fast-forward push a ref. No rewinds or deletes.
305 * `RW+` -- create, fast-forward push, rewind push, or delete a ref.
307 Sometimes you want to allow people to push, but not *create* a ref. Or
308 rewind, but not *delete* a ref. The `C` and `D` qualifiers help here.
310 * If a rule specifies `RWC` or `RW+C`, then *rules that do NOT have the C
311 qualifier will no longer permit **creating** a ref in that repo*.
313 <font color="gray">Please do not confuse this with the standalone `C`
314 permission that allows someone to [create][] a **repo**</font>
316 * If a rule specifies `RWD` or `RW+D`, then *rules that do NOT have the D
317 qualifier will no longer permit **deleting** a ref in that repo*.
319 [create]: wild#user-creating-a-specific-repo
321 Note: These two can be combined, so you can have `RWCD` and `RW+CD` as well.
323 One very rare need is to reject merge commits (a commit series that is not a
324 straight line of commits). The `M` qualifier helps here:
326 * When a rule has `M` appended to the permissions, *rules that do NOT have
327 it will reject a commit sequence that contains a merge commit* (i.e., they
328 only accept a straight line series of commits).
330 ## summary of permissions
332 The full set of permissions, in regex syntax, is `-|R|RW+?C?D?M?`. This
333 expands to one of `-`, `R`, `RW`, `RW+`, `RWC`, `RW+C`, `RWD`, `RW+D`, `RWCD`,
334 or `RW+CD`, all but the first two optionally followed by an `M`.
338 # appendix 2: gitolite access check flow
340 Here's lots more detail on the access check process, with flow diagrams.
342 When do the access checks happen and what are the four pieces of data (repo,
343 user, operation, ref) in each case?
346 ----------- | -------------
347 ![](a1.png) | ![](a2.png)
349 In these pictures the access checks are marked in yellow.
351 The picture on the left is for a read (git clone, fetch, ls-remote). There is
352 only one access check for a read operation. If access is denied, the
353 operation aborts. Otherwise, gitolite-shell invokes git-upload-pack.
355 Notice the information available to the access check. The "oper" (operation)
356 is "R", indicating a read operation. The "ref" is listed as "unknown",
357 although we could also call it "irrelevant"!
359 **Access check #1** proceeds with those 4 bits of information, and either
360 passes or fails. If it passes, gitolite passes control to "git-upload-pack"
365 The flow for a push operation (the picture on the right) is *very* similar
366 upto the first access check. The "oper" is "W" now, although the "ref" is
367 still unknown. <span class="gray">Even though this *is* a push, at this stage in the
368 protocol nothing on the server knows what branch or tag or combination of them
369 are coming down the wire, since we haven't executed git-receive-pack
372 If it succeeds, gitolite passes control to "git-receive-pack", but its job is
373 not done yet. *Git* will eventually invoke the update hook (see 'man
374 githooks'). Gitolite has already grabbed this hook, which receives from git
375 the ref name being pushed, as well as enough information to compute whether
376 this push is a "fast-forward push" or a "rewind push". Based on this,
377 gitolite sets the "oper" field to "W" or "+", respectively.
379 **Access check #2** proceeds with this information. The result is sent back
380 to git-receive-pack (in the form of an exit code; again, see 'man githooks'),
381 and the push fails or succeeds based on that.
383 [c1c2]: conf#putting-it-all-together
385 ## putting it all together
387 At this point, we have the following pieces of information:
389 * A set of rules, each containing 4 pieces of data: repo, user, perm, refex.
390 They are in the sequence they were found in the conf file.
392 We discard all rules that do not apply to this repo and this user, which
393 means our set of rules have only two fields: perm, refex.
395 As a quick reminder, perm is one of R, RW, RW+, or `-`.
397 * Four elements that make up the access being attempted: repo, user, oper,
400 Again, as a reminder, the "oper" is **one letter**. For "check #1" it is
401 either R or W, and for check #2 it can be W or +.
403 <span class="gray">Note on permissions and "oper": there are other [types of
404 permissions][write-types], but for our discussion these are enough. The
405 others are rare, and anyway it is easy to extrapolate to them.</span>
407 [write-types]: conf-2#appendix-1-different-types-of-write-operations
409 With that background, here's the flow. The one on the left is for check #1
410 (ref is unknown) while the one on the right is for check #2 (ref is known).
412 ref unknown | ref known
413 ----------- | -------------
414 ![](a3.png) | ![](a4.png)
416 As you can see, deny rules are ignored by check #1 -- they're not tested in
417 any way. For check #2, if there is a deny rule whose refex matched the ref,
418 access is denied (as you'd expect).
420 # appendix 3: embedding test code in your conf
422 As of v3.6.7, it is possible to embed test code within gitolite.conf. This
423 can be useful if your conf file is complicated, and you need a way to make
424 sure that any changes are not messing with your most important restrictions.
426 Full details, including preparation and caveats, are in
427 `contrib/utils/testconf`, but here's a teaser example:
434 gitolite access -q foo u1 + any || echo FAILED
435 gitolite access -q foo u2 + any && echo FAILED
436 gitolite access -q foo u2 W any || echo FAILED