gitolite/doc/gitolite.conf.mkd

26 KiB

F=conf the access control file gitolite.conf

Gitolite has an advanced access control language that is designed to be powerful but easy to use. Other objectives were that it should be even easier to read, review and audit the rules, and it should scale to thousands of repos and users. There was also, in the author's mind, a desperate need to create something as different as possible from the brain-dead, nausea-inducing "Windows INI" style syntax that some other popular tools seem to favour.

#confrecap a quick summary of the conf file

This is a quick recap of the most common elements in a typical config file. (In the following description, the angle brackets are not part of the syntax).

  • (optional) [group][groups] definitions

    @<groupname> = <list of users and groups>
    
  • repo access rules

    repo <reponame>
        # one or more access rules like
        <permission> <optional refex> = <list of users and groups>
    

    The [most common][bac] permissions are:

    • R, for read only
    • RW, for push existing ref or create new ref
    • RW+, for "push -f" or ref deletion allowed (i.e., destroy information)
    • - (the minus sign), to [deny][] access

    If no [refex][] is supplied, the access rule applies to all refs.

    Later, gitolite [acquired][rwcd]

    • RWC and RW+C, for ref creation. Once you use this in a repo, a normal RW/RW+ can no longer create a ref; it can only push existing refs (Note also that a standalone "C" permission is something [completely different][wild])
    • RWD and RW+D, for ref deletion. Once you use this in a repo, a normal RW+ can no longer delete a ref; it can only rewind
    • (RWCD and RW+CD, which is just a combination of these two)

    Even later, [gitolite acquired][mergecheck], if you want to enforce a rebase-only workflow.

The following sections dig deeper into the syntax and semantics of the access control rules and other configuration directives in the gitolite.conf file. If narrative descriptions scare you, or your English is not upto scratch, try [gitolite config by example][conf_examples] also.

F=syntax lexical syntax

In general, everything is space separated; there are no commas, semicolons, etc., in the syntax.

Comments are in the usual shell-ish style.

User names and repo names are as simple as possible; they must start with an alphanumeric, but after that they can also contain ., _, or -.

Usernames can optionally be followed by an @ and a domainname containing at least one . (this allows you to use an email address as someone's username). Reponames can contain / characters (this allows you to put your repos in a tree-structure for convenience)

continuation lines

There are no continuation lines -- gitolite does not process C-style backslash-escaped newlines as anything special. However, the section on "groups" will tell you how you can break up large lists of names in a group definition into multiple lines.

include files

Gitolite allows you to break up the configuration into multiple files and include them in the main file for convenience.

include     "foo.conf"

will include the contents of the file "foo.conf".

Details:

  • You can also use a glob (include "*.conf"), or put your include files into subdirectories of "conf" (include "foo/bar.conf"), or both (include "repos/*.conf").

  • Included files are always searched from the gitolite-admin repo's "conf/" directory, unless you supplied an absolute path. (Note: in the interests of cloning the admin-repo sanely you should avoid absolute paths!)

  • If you ended up recursing, files that have been already processed once are skipped, with a warning.

Advanced users: subconf, a command that is very closely related to include, is documented [here][subconf].

F=bac basic access control

Here's a very basic set of rules:

repo    gitolite-admin
        RW+     =   sitaram

repo    testing
        RW+     =   @all

repo    gitolite simplicity
        RW+     =   sitaram dilbert
        RW      =   alice ashok
        R       =   wally

It should be easy to guess what most of this means:

  • R means "read" permission
  • RW means "read and write", but no rewind
  • RW+ means "read and write", with rewind allowed

A "rewind" is more often called a "non-fast forward push"; see git docs for what that is. The + was chosen because it is part of the "git push" syntax for non-ff pushes.

Note that tags are generally considered "write once", so they are treated specially by gitolite: you need rewind permission (RW+) to overwrite a tag with a new value.

In a later section you'll see some more advanced permissions.

how rules are matched

It's important to understand that there're two levels at which access control happens. Please see [this][2levels] for details, especially about the first level check. Much of the complexity applies only to the second level check, so that is all we will be discussing here. This check is done by the update hook, and determines whether the push succeeds or fails.

For basic permissions like this, matching is simple. Gitolite already knows:

  • the user
  • the repo
  • the branch or tag ("ref") being pushed
  • whether it is a normal (ff) push or a rewind (non-ff) push.

Gitolite goes down the list of rules matching the user, repo, and the ref. The first matching rule that has the permission you're looking for (W or +), results in success. A fallthrough results in failure.

#refex branches, tags, and specifying "refex"es

One of the original goals of gitolite was to allow access control at the branch/tag (aka "ref") level. The git source code contains a sample update hook that has the following in it:

# from Documentation/howto/update-hook-example.txt:

refs/heads/master	    junio
+refs/heads/pu          junio
refs/heads/cogito$      pasky
refs/heads/bw/.*        linus
refs/heads/tmp/.*       .*
refs/tags/v[0-9].*      junio

If you did this in gitolite, this is what the equivalents would be:

repo    git
        RW      master$             =   junio   # line 1
        RW+     pu$                 =   junio   # line 2
        RW      cogito$             =   pasky   # line 3
        RW      bw/                 =   linus   # line 4
        RW      tmp/                =   @all    # line 5
        RW      refs/tags/v[0-9]    =   junio   # line 6

The following points will help you understand these rules. (Git recap: branches and tags together are called "ref"s in git. A branch ref usually looks like "refs/heads/foo", while a tag ref looks like "refs/tags/bar")

  • the general syntax of a paragraph of rules is:

    # start line:
    repo [one or more repos and/or repo groups]
    # followed by one or more permissions lines:
    [permission] [zero or more refexes] = [one or more users]
    
  • a refex is a perl regex that matches a ref. When you try to push a commit to a branch or a tag, that "ref" is matched against the refex part of each rule.

  • if the refex does not start with refs/, gitolite assumes a prefix of refs/heads/. This is useful because branch matching is the most common case, as you can see this applies to lines 1 through 5 here.

  • if no refex appears, the rule applies to all refs in that repo (as if you had specified refs/.* as the refex).

  • refexes are prefix-matched (they are internally anchored with ^ before being used). This means only the beginning of the actual ref needs to match the refex, unless the refex has an explicit $ meta-character at the end (like the first 3 lines in our example do).

    Line 5, for instance, allows anyone to push a branch inside the "tmp/" namespace, while line 6 provides the ability to push version tags; "v1", "v1.0", "v2.0rc1", all match the criterion specified by v[0-9] because this is a prefix match only.

#groups groups

Gitolite allows you to define groups of repos. users, or even refexes. A group is semantically (but not syntactically) like a #define in C. Here is an example of each kind:

@oss_repos  =   gitolite linux git perl rakudo entrans vkc
@staff      =   sitaram some_dev another-dev
@important  =   master$ QA_done refs/tags/v[0-9]

The syntax of a group definition is simply:

@groupname = [one or more names]

A group can accumulate values. For example:

@staff      =   sitaram some_dev another-dev
@staff      =   au.thor

is the same as

@staff      =   sitaram some_dev another-dev au.thor

This is more convenient than continuation lines, because it allows you to add to a group anywhere. Many people generate their gitolite.conf itself from some other database, and it is very useful to be able to do this sort of thing.

Groups can include other groups, and the included group will be expanded to whatever value it currently has:

@staff      =   sitaram some_dev another-dev    # line 1
@staff      =   au.thor                         # line 2
@interns    =   indy james                      # line 3
@alldevs    =   bob @interns @staff             # line 4

"@alldevs" expands to 7 names now. However, remember that the config file is parsed in a single-pass, so later additions to a group name cannot affect earlier uses of it. If you moved line 2 to the end, "@alldevs" would only have 6 names in it.

the special @all group

There's a special group called @all that includes all authenticated users when used as a username; you've seen examples of it earlier.

Advanced users: also see the entry for GL_ALL_INCLUDES_SPECIAL in the documentation for [~/.gitolite.rc][rc].

When used as a reponame, it includes all repos physically present in ~/repositories (or whatever is the value of $REPO_BASE).

F=rpr_ side note: "R" permissions for refs

You can control "read" access only at the repo level, not at the branch level. For example, this won't limit Wally to reading only the master branch:

repo foo
    R master    =   wally       # WILL NOT DO WHAT YOU THINK IT DOES!!

and this won't prevent him from reading it:

repo foo
    - master    =   wally       # WILL NOT DO WHAT YOU THINK IT DOES!!
    R           =   wally

This (inability to distinguish one ref from another during a read operation) is a git issue, not a gitolite issue.

There are 3 ways around this, though:

  • switch to gerrit, which has its own git stack, its own sshd, and God knows what else. All written in Java, the COBOL of the internet era ;-)
  • bug the git people to add this feature in ;-)
  • use a separate repo for Wally.

Using separate repos is not that hard with gitolite. Here's how to maintain a [partial copy][partialcopy] of the main repo and keep it synced (while not allowing the secret branches into it).

F=aac advanced access control

The previous section is sufficient for most common needs, but gitolite can go a lot further than that.

#NAME restricting pushes by dir/file name using NAME/

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. The rest of it is treated as a refex to match against each file being touched (see "SPECIFYING AND USING A REFEX" above for details)

  • 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/".

#deny "deny" rules

warnings and required reading

Gitolite performs access checks at 2 levels. The first check is performed for both read and write operations, while the second one happens only for write operations.

Required reading: [this section][2levels] of the documentation.

Warning: When using deny rules, the order of your rules matters, where earlier it did not. If you're just starting to add a deny rule to an existing ruleset, it's a good idea to review the entire ruleset once, to make sure you're doing it right.

"deny" rules for refs in a repo

You can use "deny" rules for the second check, to prevent people pushing branches or tags that they should not be allowed to.

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 staffers can't:

    @staff = bruce whitfield martin
            [... and later ...]
    RW refs/tags/v[0-9]     = bruce
    RW refs/tags            = @staff

But that's not how the matching works. As long as any refex matches the refname being updated, it's a "yes". Since the second refex (which says "anything containing refs/tags") is a superset of the first one, it lets anyone on @staff create versioned tags, not just Bruce.

So how do we say "these people can create any tags except tags that look like this pattern"?

One way to fix this is to allow "deny" rules. We make a small addition to the permissions syntax, and define a more rigorous, ordered, interpretation.

Let's recap the existing semantics:

The first matching refex that has the permission you're looking for (W or +), results in success. A fallthrough results in failure.

Here are the new semantics, with changes from the "main" one in bold:

The first matching refex that has the permission you're looking for (W or +) or a minus (-), results in success or failure, respectively. A fallthrough also results in failure.

So the example we started with becomes, if you use "deny" rules:

    RW refs/tags/v[0-9]     = bruce
    -  refs/tags/v[0-9]     = @staff
    RW refs/tags            = @staff

And here's how it works:

  • for non-version tags, only the 3rd rule matches, so anyone on staff can push them
  • for version tags by bruce, the first rule matches so he can push them
  • for version tags by staffers other than bruce, the second rule matches before the third one, and it has a - as the permission, so the push fails

"deny" rules for the entire repo

The previous section described deny rules for the second check, which is a fairly common need. However, gitolite does not process deny rules for the first check -- it's usually simple enough to make sure your config file does not allow the particular user/repo combindation at all.

But there's one case where this becomes cumbersome: when you want to create exceptions to uses of @all.

For example, if you want gitweb to show @all repos except the special 'gitolite-admin' repo, you must manually (and laboriously) maintain a list of all your repos (except gitolite-admin) and use that in place of @all. Oh joy...

But now you can do this:

repo gitolite-admin
    -   =   gitweb daemon
    [... other access rules ...]
    config gitolite-options.deny-repo = 1

repo @all
    R   =   gitweb daemon

Here are some notes on how/why this works:

  • The order is important -- the deny rule must come first.

  • The 'config' line is what tells git to behave differently, i.e., apply deny rules for the first check also.

  • Since there is no "ref" to match against the refexes in the rules, gitolite just ignores the refexes, and simply looks at the permission (R, RW, "-", etc) and the user list.

#rwcd creating and deleting branches

Since the beginning of gitolite, RW gave the ability, not only to update, but to create a branch (that matched the refex). Similarly, RW+ meant being able to not only rewind, but also delete a ref. Conceptually, a rewind is almost the same as a delete+push (the only difference I can see is if you had core.logAllRefUpdates set, which is not a default setting).

However, there seem to be cases where it is useful to distinguish these cases. Arguments can be made on all sides if you're dealing with new users, so gitolite now supports that (in a backward compatible way).

We'll look at the delete/rewind case in detail first:

  • if the rules for a repo do not contain a D anywhere, then RW+ will allow both rewind and delete operations. Apart from being more convenient if you don't need this separation, this also ensures backward compatibility for setups created before this separation feature was added to gitolite).

  • if, however, any of the rules for a repo contains a D (example: RWD, RW+D, etc) then RW+ by itself will permit only a rewind, not a delete

The same thing applies to create/push, where if you have permissions like RWC or RW+C anywhere in that repo, a simple RW or RW+ can no longer create a new ref.

You can combine the C and D also. Thus, the set of permissions you now know about are, in regex syntax: R|RW+?C?D?. See a later section for the full set of permissions possible.

Some usage hints:

  • if you find that RW+ no longer allows creation/deletion but you can't see a C/D permission in the rules, remember that gitolite allows a repo config to be specified in multiple places for convenience, included delegated or included files. Be sure to search everywhere :)

  • a quick way to make this the default for all your repos is:

    repo @all
        RWCD    dummy-branch    =   foo
    

    where foo can be either the administrator, or if you can ignore the warning message when you push, a non-existant user.

#mergecheck enforcing a no-merges policy

Some people want to enforce a no-merges policy for various reasons. The new "M" qualifier can be used to specify who is allowed to push merge commits. This works just like "C" and "D" in the previous section, so please read that for a more detailed description and apply the same ideas, (including the @all trick!) to "M" for "allow merge commits".

The only other thing to remember is that this qualifier, if used, goes at the end of any permission starting with RW (i.e., all of them except R). For example, RW becomes RWM, RW+ becomes RW+M, RW+CD becomes RW+CDM.

summary: permissions

The full set of permissions, in regex syntax: -|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 one optionally followed by an M. And by now you know what they all mean.

[Side note: There is one more very important permission to be dealt with -- the standalone C, which is not really a "ref" level permission and can be found in doc/wildcard-repositories.mkd.]

F=confother_ other tips

personal branches

Gitolite lets you define a "personal" or "scratch" namespace prefix for each developer. See [here][pers] for details.

#ruleaccum rule accumulation

(Also see [this][ruleaccum2] for a different example that may be more intuitive for some people).

Gitolite lets you specify access rules for a repo in bits and pieces, and accumulates them in the same sequence they were given. This is very convenient. Let's say you have a mix of open source and closed source projects, and "bosses" should have read access to all projects, and everyone should have read access to open source projects. Assuming the appropriate group definitions, this would work:

# all bosses have read access to all projects
repo @open @closed @topsecret
    R   =   @bosses

# everyone has read access to "open" projects
repo @open
    R   =   @bosses @devs @interns

If you notice that @bosses are given read access to @open via both rules, don't worry that this causes some duplication or inefficiency. It doesn't :-)

Elsewhere in the file, you would specify access for individual repos (like RW, RW+, etc). Gitolite combines all of these access rules, maintaining the textual order in which they occur, when authorising a push.

And although this example used groups, you can use reponames as well, or mix and match them. You can even distribute rulesets across multiple "include" files if you wish.

Just remember that if you use [deny rules][deny] anywhere then the order of the rules matters!

This feature also helps people who generate their gitolite.conf itself from some other database -- it allows them much more flexibility in how they generate rules.

#gwd specifying gitweb and daemon access

Gitolite allows you to specify access for git-daemon and gitweb. This is a feature that I personally do not use (corporate environments don't like unauthenticated access of any kind to any repo!), but someone wanted it, so here goes.

Gitolite has two pre-defined, "special", usernames: daemon and gitweb.

To make a repo or repo group accessible via "git daemon", just give read permission to the special user "daemon". Similarly, give read permission to gitweb to allow the gitweb CGI to show the repo. Something like this:

repo    foo bar baz
    R   =   gitweb daemon

This gives you a quick way to offer multiple repos up for gitweb and/or daemon access.

However, setting a description for the project also enables gitweb permissions so you can do it that way if you want. Of course in this case you have to deal with each repo separately. Add lines like this to gitolite.conf:

foo = "some description"
bar = "some other description"
baz = "yet another description"

You can also specify an owner for gitweb to show, if you like; for example I might use:

gitolite "Sitaram Chamarty" = "fast, secure, fine-grained, access control for git"

These lines are standalone, so you can add them anywhere in the conf file.

Note that gitolite does not install or configure gitweb/git-daemon -- that is a one-time setup you must do separately. All gitolite does is:

  • for daemon, create the file git-daemon-export-ok in the repository
  • for gitweb, add the repo (plus owner name, if given) to the list of projects to be served by gitweb (see the config file variable $PROJECTS_LIST, which should have the same value you specified for $projects_list when setting up gitweb)
  • put the description, if given, in $repo/description

The "compile" script will keep these files consistent with the config settings -- this includes removing such settings/files if you remove "read" permissions for the special usernames or remove the description line.

Please note that giving permissions to these special users via @all (that is, using either repo @all or R = @all), will not work unless you set the rc-file variable $GL_ALL_INCLUDES_SPECIAL to 1. Also, NOTE that giving them read access to repo @all means the gitolite-admin repo is also accessible. It is upto you to decide if that is OK in your environment.

#rsgc repo specific git config commands

(Thanks to teemu dot matilainen at iki dot fi)


Note: this won't work unless the rc file has the right settings; please see $GL_GITCONFIG_KEYS in the [rc file doc][rc] for details and security information.


Sometimes you want to specify git config settings for your repos.

For example, say you have a custom post-receive hook that sends an email when a push happens, and this hook looks in the config for whom to send the email to, etc.

You can set these git config values within a "repo" paragraph:

repo gitolite
    config hooks.mailinglist = gitolite-commits@example.tld
    config hooks.emailprefix = "[gitolite] "
    config foo.bar = ""
    config foo.baz =

The syntax is simple:

config sectionname.keyname = [optional value_string]

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 of the git config command (--add, the value_regex, etc) are not supported.


WARNING: simply deleting the config line from the conf/gitolite.conf file will not delete the variable from repo.git/config. The syntax in the last example is the only way to make gitolite execute a --unset-all operation on the given key.


You can repeat the 'config' line as many times as you like, and the last occurrence will be the one in effect. This allows you to override settings just for one project, as in this example:

repo @all
    config gitolite.mirror.master = "frodo"
    config gitolite.mirror.slaves = "sam gollum"

repo top-secret-project
    # only sam, because we don't trust gollum
    config gitolite.mirror.slaves = "sam"

The "delete config variable" syntax can also be used, if you wish:

repo highlander     # there can be only one!
    config gitolite.mirror.master =
    config gitolite.mirror.slaves =

As you can see, the general idea is to place the most generic ones (repo @all, or repo patterns like repo foo.*) first, and place more specific ones later to override the generic settings.