master TOC | main page | single-page | license | New: Gitolite Essentials book

This is for gitolite "g3"; for older (v2.x) documentation click here

1 access rules

NOTE: In the following description, "user" means "user or a group that he/she is a member of", and "repo" means "repo, or a group that it is a member of, or a (wild repo) pattern that matches it, or a group that contains a pattern that matches it".

1.1 what do rules look like

Here's an example ruleset.

@staff          =   dilbert alice wally bob

repo foo
    RW+         =   dilbert     # line 1
    RW+ dev     =   alice       # line 2
    -           =   wally       # line 3
    RW  temp/   =   @staff      # line 4
    R           =   ashok       # line 5

A rule line has the structure

<permission> <zero or more refexes> = <one or more users/user groups>

The most commonly used permissions are:

There are also other, less commonly used, types of permissions.

A refex is an expression that matches the ref (i.e., branch or tag) being pushed.

You can also use virtual refs to perform extra checks and controls that you can't do with just the normal ref (like refs/heads/master) being pushed. The most common example is restricting pushes by dir/file name, but there are lots of other possibilities.

1.2 how are the rules checked

Note that gitolite first accumulates the rules before checking access.

1.2.1 read access -- clone, fetch, archive

Read access is checked only once, just before passing control to git-upload-pack or git-archive-pack. At this point gitolite only knows the repo name, the user name, and the fact that it is a read operation.

Here's the default flow:

The refex field is ignored for this check. (Git does not support distinguishing one ref from another for access control during read operations). read access respecting deny rules

Deny rules (the "-" permission) are ignored by default. In our example, line 3 does not prevent wally from cloning the repo, because line 4 permits it.

You can change that by using the deny-rules option. If this option is active for a repo then this is the flow for access checking:

Apart from the extra check for deny rules, there is another very subtle but important difference here: the order of the rules matters now, where previously it did not.

Later in this document are a couple of examples showing this option in use.

1.2.2 write access -- push

Write access is checked twice, once before passing control to git-receive-pack, and once from within the update hook.

The first check is identical to the one for read access, except of course the permission field must contain a "W". As before, deny rules are ignored, and you can override that using the deny-rules option. The refex field is also ignored, because at this point we don't know what refs are going to be pushed.

The second check happens from within the update hook. Deny rules are considered, which in turn means the sequence of the rules matters.

Also, this time, git supplies us with three more pieces of information: the name of the ref being updated (like "refs/heads/master"), the old SHA, and the new SHA. This information is sufficient to determine whether this is a normal push or a forced, (a.k.a rewind), push. A normal push requires the permission field to contain a "W", while a forced push requires it to contain a "+".

Here's how the actual rule matching happens:

Now all you need is to understand how refex matching happens and how the permissions match the various types of write operations.

1.3 summary of permissions

The full set of permissions, in regex syntax, is -|R|RW+?C?D?M?. This expands to one of -, R, RW, RW+, RWC, RW+C, RWD, RW+D, RWCD, or RW+CD, all but the first two optionally followed by an M. And by now you know what they all mean.

1.4 additional topics

1.4.1 rule accumulation

Gitolite was meant to collect rules from multiple places and apply them all in sequence. For example, if you have the following (we've added line numbers to aid later explanation):

 1  # we have 3 specifically named FOSS projects, but we also consider any
 2  # project in the foss/ directory to be FOSS.
 3  @FOSS-projects  =   git gitolite linux foss/..*

 4  # similarly for proprietary projects
 5  @prop-projects  =   foo bar baz prop/..*

 6  # our users are divided into staff, interns, and bosses
 7  @staff          =   alice dilbert wally
 8  @interns        =   ashok
 9  @bosses         =   PHB

10  # we have certain policies.  The first is that FOSS projects are readable
11  # by everyone
12  repo @FOSS-projects
13      R   =   @all

14  # the second is that bosses can read any repo if they wish to
15  repo @all
16      R   =   @bosses

17  # now we have specific rules for specific projects
18  repo git
19      RW+ =   junio
20      ...some other rules...

21  repo gitolite
22      RW+ =   sitaram
23      ...some other rules...

24  ...etc...

the effective rule list for, say, the "gitolite" repo will be (keeping the line numbers the same so you know where they are coming from):

13      R   =   @all            # since it is a member of @FOSS-projects
16      R   =   @bosses         # since every repo is a member of @all anyway
22      RW+ =   sitaram         # from the gitolite-specific ruleset
23      ...some other rules...  # from the gitolite-specific ruleset

As you can see, for each user+repo combination, several rules will apply. Gitolite combines them all into one list (in the sequence they are found in the conf file), before applying the access checks.

This extends to patterns also. For example, if you have this:

repo foss/apache
    ...some rules...

then, because this repo fits the pattern foss/..*, it is considered part of the @FOSS-projects group, so all the rules that apply to that group are in play when someone accesses foss/apache.

This is what we meant by "repo, or a group that it is a member of, or a (wild repo) pattern that matches it, or a group that contains a pattern that matches it", up at the top of this document.

1.4.2 examples of deny rules

example 1

 1  @secret = secret/one secret/two [...]

 2  # put this at or near the top of the conf file, or at least before any
 3  # rules that give 'gitweb' and 'daemon' any kind of access
 4  repo @secret
 5      -   =   gitweb daemon
 6      option deny-rules = 1
 7      # make sure you do not set deny-rules to 0 for these repos later

 8  repo @all
 9      R   =   gitweb daemon

10  <...other rules...>

In this example, we have lots of repos, which should all be accessible by gitweb or daemon, so we want the convenience provided by lines 8 and 9 (we don't want to put line 9 in each repo). However, we also have some secret repos, which we are able to list explicitly in some way, and we want to prevent gitweb or daemon from seeing them.

Therefore we apply the "deny-rules" option to just those repos, and ensure that the first rule encountered by these two "users" for those repos is a deny rule. This makes gitolite deny read access to those users for those repos.

example 2

In this example the "open" repos are fewer in number, so it is the opposite situation to the above in terms of our ability to enumerate all the repos.

@open = git gitolite foss/..* [...]

# put this at or near the top of the conf file, or at least before any
# rules that give 'gitweb' and 'daemon' any access
repo @all
    -   =   gitweb daemon
    option deny-rules = 1

repo @open
    R   =   gitweb daemon
    option deny-rules = 0
    # make sure you do not set deny-rules to 1 for these repos later

To see why this works, you need to remember that for options and config lines, a later setting overrides earlier ones. So we set it to 1 for all repos, then selectively set it to 0 for some.

This means the "deny-rules" option applies to all the repos except the "open" repos. For such repos, the first rule encountered by gitweb and daemon is a deny rule, so they are denied read access.