From 7313d48247dd55c7a5c7df4766933de15cc75acc Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Tue, 16 Nov 2010 20:09:45 +0530 Subject: [PATCH] gitolite-down: disable write-access to take backups (we quietly do not document the 'able' adc, which is now the most "official" adc in the sense that it has a new test, t64-write-able!) other notes: fix bug in 'able' (not setting $loc) --- contrib/adc/able | 7 ++ doc/3-faq-tips-etc.mkd | 28 ++++++ doc/admin-defined-commands.mkd | 21 +---- src/gitolite.pm | 10 +++ src/gl-auth-command | 3 + t/t64-write-able | 154 +++++++++++++++++++++++++++++++++ 6 files changed, 204 insertions(+), 19 deletions(-) create mode 100644 t/t64-write-able diff --git a/contrib/adc/able b/contrib/adc/able index 1b73819..b327815 100755 --- a/contrib/adc/able +++ b/contrib/adc/able @@ -18,6 +18,7 @@ do locs="$locs $HOME" ;; * ) + loc="$GL_REPO_BASE_ABS/$1.git" [ -d $loc ] && locs="$locs $GL_REPO_BASE_ABS/$1.git" [ -d $loc ] || echo "ignoring $1..." ;; @@ -25,6 +26,8 @@ do shift done +[[ -z "$locs" ]] && die "give me '@all' or some reponame" + case $op in en|enable ) for l in $locs @@ -35,9 +38,13 @@ case $op in dis|disable ) # bashism read msg <<<$(cat) + echo disabling following locations with message: + echo $msg + echo for l in $locs do echo $msg > $l/.gitolite.down + echo $l done ;; * ) diff --git a/doc/3-faq-tips-etc.mkd b/doc/3-faq-tips-etc.mkd index 8ae1530..1f86bd6 100644 --- a/doc/3-faq-tips-etc.mkd +++ b/doc/3-faq-tips-etc.mkd @@ -28,6 +28,7 @@ In this document: * "personal" branches * custom hooks and custom git config * bypassing gitolite + * disabling write access to take backups * INconvenience features * deleting a repo * helping with gitweb @@ -540,6 +541,33 @@ is NOT available if you bypass gitolite. Mucking with that repo in this manner is strongly discouraged, as in "are you feeling lucky today?". Use `gl-dont-panic` if you need to do some server-side surgery for that repo. + + +##### disabling write access to take backups + +If you want to take normal, OS-level, backups of the system, you might want +git to be quiescent during that time, so that the backup is clean. The best +way to do this is to disable write-access to the server for the duration of +the backup. + +Here's how: + + cd $HOME # if running as "git" user, else "cd ~git" or whatever + echo writes disabled during backup window > .gitolite.down + + # << RUN YOUR BACKUP COMMAND(s) HERE >> + + rm .gitolite.down + +I leave it to you to + + * make sure that if the backup script fails, the `.gitolite.down` file is + still removed (or not; maybe your policy is that if the backup failed, no + further writes are allowed. Whatever...) + * if you're extremely paranoid (even I wouldn't worry about this!) make sure + that no push is *in progress* by checking for any `git-receive-pack` + processes in a `ps` output. + #### INconvenience features diff --git a/doc/admin-defined-commands.mkd b/doc/admin-defined-commands.mkd index 1ee04ce..32fe267 100644 --- a/doc/admin-defined-commands.mkd +++ b/doc/admin-defined-commands.mkd @@ -189,26 +189,9 @@ 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: This needs a specific secondary update hook**. Creating a secondary -update hook is described in the sections on "custom hooks" and "hook chaining" -in doc/2. You need code like this in `update.secondary` (don't forget to -`chmod +x` the file): +Note: please see [this][diswr] for more on this. - #!/bin/bash - - for f in $HOME/.gitolite.down $PWD/.gitolite.down - do - if [ -f $f ] - then - echo >&2 - echo '*** ABORT ***' >&2 - echo >&2 - cat $f >&2 - exit 1 - fi - done - - exit 0 +[diswr]: http://github.com/sitaramc/gitolite/blob/pu/doc/3-faq-tips-etc.mkd#_disabling_write_access_to_take_backups diff --git a/src/gitolite.pm b/src/gitolite.pm index 8b16641..29fb26c 100644 --- a/src/gitolite.pm +++ b/src/gitolite.pm @@ -787,6 +787,16 @@ sub can_read { ); } +# helper to manage "disabling" a repo or the whole site for "W" access +sub check_repo_write_enabled { + my ($repo) = shift; + for my $d ("$ENV{HOME}/.gitolite.down", "$ENV{GL_REPO_BASE_ABS}/$repo.git/.gitolite.down") { + next unless -f $d; + die $ABRT . `cat $d` if -s $d; + die $ABRT . "writes are currently disabled\n"; + } +} + # ---------------------------------------------------------------------------- # setup the ~/.ssh/authorized_keys file # ---------------------------------------------------------------------------- diff --git a/src/gl-auth-command b/src/gl-auth-command index 0db6862..46cea7d 100755 --- a/src/gl-auth-command +++ b/src/gl-auth-command @@ -246,6 +246,9 @@ my $aa = ($verb =~ $R_COMMANDS ? 'R' : 'W'); die "$aa access for $repo DENIED to $user (Or there may be no repository at the given path. Did you spell it correctly?)\n" unless $perm =~ /$aa/; +# check if repo is write-enabled +&check_repo_write_enabled($repo) if $aa eq 'W'; + # ---------------------------------------------------------------------------- # over to git now # ---------------------------------------------------------------------------- diff --git a/t/t64-write-able b/t/t64-write-able new file mode 100644 index 0000000..528a4af --- /dev/null +++ b/t/t64-write-able @@ -0,0 +1,154 @@ +# vim: syn=sh: +for bc in 0 1 +do + cd $TESTDIR + $TESTDIR/rollback || die "rollback failed" + editrc GL_BIG_CONFIG $bc + editrc GL_WILDREPOS 1 + + rm -rf /tmp/glt-adc + mkdir /tmp/glt-adc || die "mkdir /tmp/glt-adc failed" + cp ../contrib/adc/* /tmp/glt-adc + echo "\$GL_ADC_PATH = '/tmp/glt-adc';" | addrc + runremote rm -f .gitolite.down + + # ---------- + + name "INTERNAL" + echo " + @leads = u1 u2 + @devs = u1 u2 u3 u4 + + repo foo + RW+ = u1 + + @gbar = bar/CREATOR/..* + repo @gbar + C = @leads + RW+ = @leads + RW = @devs + " | ugc + expect_push_ok "master -> master" + + name "u1 push foo" + cd ~/td + rm -rf foo + runlocal git clone u1:foo + expect "warning: You appear to have cloned an empty repository." + cd foo + mdc; mdc + runlocal git push origin master + expect_push_ok "master -> master" + + name "u2 create and push bar/u2/r1" + cd ~/td + runlocal git clone u2:bar/u2/r1 + expect "Initialized empty Git repository in /home/gitolite-test/repositories/bar/u2/r1.git/" + expect "warning: You appear to have cloned an empty repository." + cd r1 + mdc; mdc + runlocal git push origin master + expect_push_ok "master -> master" + + name "disable entire site" + runremote ls -al .gitolite.down + expect "ls: cannot access .gitolite.down: No such file or directory" + (echo first line; echo second line) | runlocal ssh gitolite able dis + expect "give me '@all' or some reponame" + (echo first line; echo second line) | runlocal ssh gitolite able dis @all + expect "disabling following locations with message:" + expect "first line second line" + expect "/home/gitolite-test" + runremote ls -al .gitolite.down + expect "^.rw------- 1 gitolite-test gitolite-test .. ... .. ..:.. .gitolite.down" + + name "u1 push foo fail" + cd ~/td/foo + mdc; mdc + runlocal git push origin master + expect ABORTING + expect "first line second line" + expect "fatal: The remote end hung up unexpectedly" + + name "u2 create and push bar/u2/r1 fail" + cd ~/td/r1 + mdc; mdc + runlocal git push origin master + expect ABORTING + expect "first line second line" + expect "fatal: The remote end hung up unexpectedly" + + name "enable entire site" + runlocal ssh gitolite able en + expect "give me '@all' or some reponame" + runlocal ssh gitolite able en @all + expect "removed ./home/gitolite-test/.gitolite.down." + runremote ls -al .gitolite.down + expect "ls: cannot access .gitolite.down: No such file or directory" + + name "u1 push foo" + cd ~/td/foo + mdc; mdc + runlocal git push origin master + expect_push_ok "master -> master" + + name "u2 create and push bar/u2/r1" + cd ~/td/r1 + mdc; mdc + runlocal git push origin master + expect_push_ok "master -> master" + + name "disable foo" + runlocal ssh u1 able dis foo + expect "just .what. are you trying to pull, young man" + echo foo down|runlocal ssh gitolite able dis foo + expect "disabling following locations with message:" + expect "foo down" + expect "/home/gitolite-test/repositories/foo.git" + runremote ls -al /home/gitolite-test/repositories/foo.git/.gitolite.down + expect ".rw------- 1 gitolite-test gitolite-test . ... .. ..:.. /home/gitolite-test/repositories/foo.git/.gitolite.down" + + name "u1 push foo fail" + cd ~/td/foo + mdc; mdc + runlocal git push origin master + expect ABORTING + expect "foo down" + expect "fatal: The remote end hung up unexpectedly" + + name "u2 create and push bar/u2/r1" + cd ~/td/r1 + mdc; mdc + runlocal git push origin master + expect_push_ok "master -> master" + + name "enable foo, disable bar/u2/r1" + runlocal ssh u1 able en foo + expect "just .what. are you trying to pull, young man" + runlocal ssh gitolite able en foo + expect "removed ./home/gitolite-test/repositories/foo.git/.gitolite.down." + echo bar/u2/r1 down | runlocal ssh u1 able dis foo + expect "just .what. are you trying to pull, young man" + echo bar/u2/r1 down | runlocal ssh gitolite able dis bar/u2/r1 + expect "disabling following locations with message:" + expect "bar/u2/r1 down" + expect "/home/gitolite-test/repositories/bar/u2/r1.git" + runremote ls -al /home/gitolite-test/repositories/bar/u2/r1.git/.gitolite.down + expect ".rw------- 1 gitolite-test gitolite-test .. ... .. ..:.. /home/gitolite-test/repositories/bar/u2/r1.git/.gitolite.down" + + name "u1 push foo" + cd ~/td/foo + mdc; mdc + runlocal git push origin master + expect_push_ok "master -> master" + + name "u2 create and push bar/u2/r1i fail" + cd ~/td/r1 + mdc; mdc + runlocal git push origin master + expect ABORTING + expect "bar/u2/r1 down" + expect "fatal: The remote end hung up unexpectedly" + + name "INTERNAL" +done