gitolite/doc/admin-defined-commands.mkd

8.2 KiB

F=ADCs admin defined commands

ADC background

The admin-defined commands (ADCs) feature allows controlled access to specific, "safe", programs or scripts, without giving users full shell access.

WARNING: regardless of what you read below, the security of the code in the commands or scripts you install as ADCs is your responsibility. The sample ADCs shipped with gitolite (in contrib/adc) should be safe enough, but an extra pair of eyes never hurt, so please review before use.

Although this is a generic way to allow pretty much any command to be run, most of the examples and sample ADCs pertain to allowing users to manage their "own" repos. If that's your use case, please read the [wildcard repositories][wild] doc before you continue here.

ADC details

installing ADCs

ADCs can only be installed by someone with shell access to the server; merely having push rights to the admin repo is not enough.

  • edit ~/.gitolite.rc and set $GL_ADC_PATH to a directory that is not in $PATH.

  • add your "safe" executables to this directory.

Warning: An ADC can hide (or override) gitolite's built-in commands like 'info', 'expand', 'setperms', or even 'git-receive-pack' or 'git-upload-pack'! This is by design. So be careful what you name your scripts.

However, it is perfectly ok, and may even be necessary in some cases, to name them after system executables (like 'rsync').

user invocation

If you have a command called "foo" in that directory, then a user can invoke it by saying:

ssh git@server foo argument list

checking authorisation inside an ADC

Once an ADC is installed, all users can run it. But sometimes you want only some people to be able to do so.

While you cannot prevent the ADC from running at all, you can start the ADC with code that checks the user's access to any arbitrary repo. For example, you can bail out if the user does not have "W" access to the "gitolite-admin" repo, which is an easy way of making sure an ADC is only run by admins.

See the section on "the anatomy of a command" later for this and many more details.

checking arguments

Gitolite will call an ADC only if the arguments passed to it match a very strict pattern (see $ADC_CMD_ARGS_PATT in src/gitolite_rc.pm). This reduces the risk of various kinds of shell-meta related compromises.

passing unchecked arguments

Some commands need arguments with a broader range of characters than $ADC_CMD_ARGS_PATT will allow. As long as you are sure those commands are doing their own argument checking and sanitisation, you can place such commands in $GL_ADC_PATH/ua and they will be run with no checks on the arguments.

The "ua" stand for "unchecked arguments". Consider this your last warning ;-)

"fake" repos and access control for non-git programs

A "fake" repo is a repo that exists in the config file but is specially named (starts with "EXTCMD/") so that gitolite will not create an actual repo on disk for it. It serves as a place holder for different sets of rules.

If you install the 'rsync' ADC, you can use a fake repo called 'EXTCMD/rsync' to collect a set of rules that specify what user is allowed to read/write what files using the rsync command on his workstation. See contrib/adc/rsync for more on this.

Any non-git program can be similarly access controlled, as long as the command line that the client attempts to execute on the server has sufficient information to decide. Protocols where the command line is just one word and everything else happens in the conversation later cannot be helped by this mechanism.

anatomy of a command

You can do whatever you want in an ADC! It's upto you to check the permissions of each repo that the user is manipulating using your ADC -- your code can rm -rf $GL_REPO_BASE_ABS if you like and gitolite wouldn't stop you.

The current directory ($PWD) will be set to the $HOME of git@server (or whatever id you're using). It won't be any specific repo, it won't even be the base directory of all the repos.

Gitolite defines a few environment variables, as well as allows you to directly query the ownership and access permissions of any repository.

The environment variables available are:

  • GL_USER -- the name of the user invoking the command
  • GL_BINDIR -- the directory containing all the binaries (in particular, gitolite.pm, which is all we really care about here)
  • GL_REPO_BASE_ABS -- the absolute path of the base directory containing all the repos

There are a few other variables also available but the above are the only ones you should rely on. Please treat any other variables you notice as being internal/undocumented/subject to change.

[Implementation note: some of the distro packagers don't seem to like GL_BINDIR. I have not tested this in those scenarios, but they probably put gitolite.pm somewhere in perl's lib path anyway, so it ought to work].

You can query ownership and permissions for the current user (which may not necessarily be the owner). This is done loosely as follows (don't use this exact code yet though):

perl -I$GL_BINDIR -Mgitolite -e "cli_repo_rights('reponame')"

which will print two space-separated words: permissions and owner. Something like _____R__W u1 or maybe ____@R_@W <gitolite>. (The u1 indicates the queried repo is a wildcard repo created by user u1; for meanings of the "@" see doc/report-output.mkd)

But that's cumbersome. It's much nicer to use the convenient functions defined in contrib/adc/adc.common-functions; see the comments in that file for details, and any of the other samples for how to use them.

If you prefer perl, there is a nicely commented example in contrib/adc/get-rights-and-owner.in-perl.

example uses and sample commands in contrib/adc

#fork the 'fork' ADC

A user would use the fork command like this:

ssh git@server fork from to

where "from" is a repo to which the user invoking the fork has "R" access, and "to" is a repo that does not yet exist and to which he has "C" access.

(Reminder: these access checks are done by the "fork" script, not within gitolite -- once again, you are responsible for making sure your scripts maintain the security of the system!)

Strictly speaking this command is not really needed. Even without all this "admin-defined commands" setup you could still do the following, purely from the client side:

git clone git@server:from
cd from
git remote add new git@server:to
git push new refs/*:refs/*

or some such incantation.

deleting/trashing repos

See the [repo-deletion document][wild_repodel] for details about this.

#able enable/disable push access temporarily

If you want to disable push access to gitolite temporarily (maybe for maintenance), anyone with write access to the gitolite-admin repo can do this:

ssh git@server able dis @all    # able dis ==> dis able

To re-enable after the maint work is done:

ssh git@server able en @all     # able en ==> en able

You can also do this for one or more individual repos; in place of @all, just use a space separated list of reponames (exactly as they would appear in the config file). Wildcards are not supported; patches welcome ;-)

Note: please see [this][disable] for more on this.

how the ADC feature came about

Gitolite was named to be short for "gitosis-lite". Someone now wants to turn it into a "github-lite" :-) and even had some code to start me off thinking.

Since my first impulse on being asked for a feature is to say no, I was casting about for a reason when he gave me one: he first made some noises about perl, then said something about rewriting it all in scheme. Nice... I resisted the urge to point him to this, told him that's a great idea and he should go for it, mentally blessing him for letting me off the hook on coding it ;-) Laziness is the first virtue you know!

And that was that. For a couple of days.

Soon, though, I realised that there could be a pretty big bonus in this for tightly controlled setups, so I went and coded it all anyway. See the section on "restricted admin" for what's really exciting about this for me.