## admin defined commands ---- In this document: * background * details * installing ADCs * user invocation * checking authorisation * checking arguments * passing unchecked arguments * "fake" repos and access control for non-git programs * anatomy of a command * example uses and sample commands in `contrib/adc` * fork * deleting/trashing repos * enable/disable push access temporarily * how this feature came about ---- ### 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 [doc/wildcard-repositories.mkd][wild] before you continue here. [wild]: http://sitaramc.github.com/gitolite/doc/wildcard-repositories.html ### 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 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 `. (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. There's a bash shell function called `get_rights_and_owner` in `contrib/adc/adc.common-functions` that is much more convenient. See any of the other samples for how to use it. 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 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][rdR] for details about this. [rdR]: http://sitaramc.github.com/gitolite/contrib/adc/repo-deletion.html #### 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][diswr] for more on this. [diswr]: http://sitaramc.github.com/gitolite/doc/3-faq-tips-etc.html#_disabling_write_access_to_take_backups ---- ### how this 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][xkcd224], 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][lazy] *is* the first virtue you know! [xkcd224]: http://xkcd.com/224/ [lazy]: http://c2.com/cgi/wiki?LazinessImpatienceHubris 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*.