From 59623336cd4b18b8167af4d154ac8ce436cdbe47 Mon Sep 17 00:00:00 2001 From: "Kyle J. McKay" Date: Tue, 21 Jun 2022 12:12:19 -0700 Subject: [PATCH] various: add read-only mode support By creating a file named "readonly" located at $Girocco::Config::chroot/etc/readonly, external pushes and project modifications via the web UI will be prohibited. Modifications via local tools such as projtool or usertool are NOT prohibited by this read-only mode, nor does this mode stop jobd or taskd from running. The text on the home page of the UI may be automatically altered as well provided an alternate version of the index text file has been made available via the gitweb_config.perl $home_text_ro setting. If the $Girocco::Config::chroot/etc/readonly file is not empty, its contents will also be included in the messages denying any changes. Except for the home page, which will only show a change if $home_text_ro points at an alternate file. --- Girocco/Util.pm | 28 +++++++++++++++++++++++++++- bin/git-http-backend-verify | 13 +++++++++++++ bin/git-shell-verify | 14 ++++++++++++++ cgi/delproj.cgi | 5 +++++ cgi/deluser.cgi | 5 +++++ cgi/editproj.cgi | 5 +++++ cgi/edituser.cgi | 5 +++++ cgi/mirrorproj.cgi | 5 +++++ cgi/pwproj.cgi | 5 +++++ cgi/regproj.cgi | 5 +++++ cgi/reguser.cgi | 5 +++++ cgi/tagproj.cgi | 6 ++++++ gitweb/gitweb_config.perl | 12 +++++++++++- install.sh | 7 +++++++ 14 files changed, 118 insertions(+), 2 deletions(-) diff --git a/Girocco/Util.pm b/Girocco/Util.pm index 4039a90..4c0afcc 100644 --- a/Girocco/Util.pm +++ b/Girocco/Util.pm @@ -31,7 +31,7 @@ BEGIN { json_bool from_json ref_indicator get_token_key get_timed_token get_token_field check_timed_token valid_branch_name get_project_from_dir - get_git_chomp); + get_git_chomp check_readonly is_readonly); } BEGIN {require "Girocco/extra/capture_command.pl"} @@ -1085,6 +1085,32 @@ sub is_git_dir { return $hv =~ /^[0-9a-f]{40}/; } +# quick check that only tests for the existence of the file but +# does not attempt to actually open it and read the contents +sub is_readonly { + return -f "$Girocco::Config::chroot/etc/readonly" ? 1 : 0; +} + +# return string containing read-only message if in read-only mode +# otherwise return empty string +# if the first argument is true, include a
tag before the second +# but only if there is a second line +sub check_readonly { + my $brtag = $_[0] ? "
" : ""; + my $msg = ""; + if (-f "$Girocco::Config::chroot/etc/readonly") { + $msg = "$Girocco::Config::name is currently read-only, please try again later."; + my $romsg = ""; + if (open my $fd, '<', "$Girocco::Config::chroot/etc/readonly") { + local $/; + $romsg = <$fd>; + close $fd; + } + $romsg ne "" and $msg = $msg . "$brtag\n" . $romsg; + } + return $msg; +} + # Returns a PATH properly prefixed which guarantees that Git is found and the # basedir/bin utilities are found as intended. $ENV{PATH} is LEFT UNCHANGED! # Caller is responsible for assigning result to $ENV{PATH} or otherwise diff --git a/bin/git-http-backend-verify b/bin/git-http-backend-verify index ce0a4b8..67a5c07 100755 --- a/bin/git-http-backend-verify +++ b/bin/git-http-backend-verify @@ -377,6 +377,19 @@ if ! [ -f "$dir/.nofetch" ]; then exit 1 fi +if [ -f "$cfg_chroot/etc/readonly" ]; then + msgro="$cfg_name is currently read-only, please try again later." + msgro2="" + if [ -s "$cfg_chroot/etc/readonly" ]; then + msgro2="$(cat "$cfg_chroot/etc/readonly" 2>/dev/null)" || : + fi + if [ -n "$msgro2" ]; then + forbidden "$msgro" "$msgro2" + else + forbidden "$msgro" + fi +fi + # Set up the correct backend command depending on cfg_max_file_size512 if [ "${cfg_max_file_size512:-0}" = "0" ]; then GIT_HTTP_BACKEND='"$cfg_git_http_backend_bin"' diff --git a/bin/git-shell-verify b/bin/git-shell-verify index ef0d7bd..76180a0 100755 --- a/bin/git-shell-verify +++ b/bin/git-shell-verify @@ -33,12 +33,14 @@ if ! [ -x @perlbin@ ]; then XDG_CONFIG_HOME=/var/empty HOME=/etc/girocco GIT_ASKPASS=/bin/git-askpass-password + rofile=/etc/readonly else # We are NOT INSIDE the chroot reporoot=@reporoot@ XDG_CONFIG_HOME=@chroot@/var/empty HOME=@chroot@/etc/girocco GIT_ASKPASS=@basedir@/bin/git-askpass-password + rofile=@chroot@/etc/readonly fi mob=@mob@ webadmurl=@webadmurl@ @@ -50,6 +52,7 @@ var_upload_window=@upload_pack_window@ cfg_fetch_stash_refs=@fetch_stash_refs@ cfg_suppress_git_ssh_logging=@suppress_git_ssh_logging@ cfg_max_file_size512=@max_file_size512@ +cfg_name=@cfg_name@ export XDG_CONFIG_HOME export HOME @@ -247,6 +250,17 @@ if [ "$type" = 'receive-pack' ] && ! [ -f "$dir/.nofetch" ]; then exit 3 fi +if [ -f "$rofile" ]; then + msgro="$cfg_name is currently read-only, please try again later." + msgro2="" + if [ -s "$rofile" ]; then + msgro2="$(cat "$rofile" 2>/dev/null)" || : + fi + echo "$msgro" >&2 + [ -z "$msgro2" ] || echo "$msgro2" >&2 + exit 3 +fi + GIT_SHELL='git-shell' if [ "$type" = 'receive-pack' ]; then git_add_config 'receive.unpackLimit=1' diff --git a/cgi/delproj.cgi b/cgi/delproj.cgi index f7dec4f..ccfc0ce 100755 --- a/cgi/delproj.cgi +++ b/cgi/delproj.cgi @@ -31,6 +31,11 @@ if (!Girocco::Project::does_exist($name,1)) { exit; } +if (my $romsg=check_readonly(1)) { + print "

$romsg

\n"; + exit; +} + my $proj = Girocco::Project->load($name); if (!$proj) { print "

not found project $name, that's really weird!

\n"; diff --git a/cgi/deluser.cgi b/cgi/deluser.cgi index beaa452..ac78d28 100755 --- a/cgi/deluser.cgi +++ b/cgi/deluser.cgi @@ -31,6 +31,11 @@ if ($cgi->param('mail')) { exit; } +if (my $romsg=check_readonly(1)) { + print "

$romsg

\n"; + exit; +} + sub _auth_form { my ($name, $submit) = @_; print <$romsg

\n"; + exit; +} + my $proj = Girocco::Project->load($name); if (!$proj) { print "

not found project $name, that's really weird!

\n"; diff --git a/cgi/edituser.cgi b/cgi/edituser.cgi index d303edb..d738570 100755 --- a/cgi/edituser.cgi +++ b/cgi/edituser.cgi @@ -25,6 +25,11 @@ if ($cgi->param('mail')) { exit; } +if (my $romsg=check_readonly(1)) { + print "

$romsg

\n"; + exit; +} + sub _auth_form { my $name = shift; my $submit = shift; diff --git a/cgi/mirrorproj.cgi b/cgi/mirrorproj.cgi index 4b1062d..b949e32 100755 --- a/cgi/mirrorproj.cgi +++ b/cgi/mirrorproj.cgi @@ -32,6 +32,11 @@ if (!Girocco::Project::does_exist($name,1)) { exit; } +if (my $romsg=check_readonly(1)) { + print "

$romsg

\n"; + exit; +} + my $proj = Girocco::Project->load($name); if (!$proj) { print "

not found project $name, that's really weird!

\n"; diff --git a/cgi/pwproj.cgi b/cgi/pwproj.cgi index 72cc5a0..f7676ee 100755 --- a/cgi/pwproj.cgi +++ b/cgi/pwproj.cgi @@ -37,6 +37,11 @@ if (!Girocco::Project::does_exist($name,1)) { exit; } +if (my $romsg=check_readonly(1)) { + print "

$romsg

\n"; + exit; +} + my $proj = Girocco::Project->load($name); if (!$proj) { print "

not found project $name, that's really weird!

\n"; diff --git a/cgi/regproj.cgi b/cgi/regproj.cgi index 60965ae..9b58798 100755 --- a/cgi/regproj.cgi +++ b/cgi/regproj.cgi @@ -14,6 +14,11 @@ use Girocco::Util; my $gcgi = Girocco::CGI->new('Project Registration'); my $cgi = $gcgi->cgi; +if (my $romsg=check_readonly(1)) { + print "

$romsg

\n"; + exit; +} + my $name = $cgi->param('name'); defined($name) or $name = ''; diff --git a/cgi/reguser.cgi b/cgi/reguser.cgi index 81d91b4..98989a5 100755 --- a/cgi/reguser.cgi +++ b/cgi/reguser.cgi @@ -24,6 +24,11 @@ if ($cgi->param('mail')) { exit; } +if (my $romsg=check_readonly(1)) { + print "

$romsg

\n"; + exit; +} + my $y0 = $cgi->param('y0') || ''; if ($cgi->param('name') && $y0 eq 'Register' && $cgi->request_method eq 'POST') { # submitted, let's see diff --git a/cgi/tagproj.cgi b/cgi/tagproj.cgi index 7f6bdaf..2a7b5db 100755 --- a/cgi/tagproj.cgi +++ b/cgi/tagproj.cgi @@ -27,6 +27,12 @@ if ($cgi->request_method ne 'POST' || $pname eq '') { exit; } +if (my $romsg=check_readonly(1)) { + print $cgi->header(-status=>403); + print "

$romsg

\n"; + exit; +} + my $proj = Girocco::Project::does_exist($pname, 1) && Girocco::Project->load($pname); if (not $proj) { print $cgi->header(-status=>404); diff --git a/gitweb/gitweb_config.perl b/gitweb/gitweb_config.perl index c2ae41e..a39041e 100644 --- a/gitweb/gitweb_config.perl +++ b/gitweb/gitweb_config.perl @@ -1,7 +1,7 @@ # Pull Girocco config use lib "__BASEDIR__"; use Girocco::Config; -use Girocco::Util qw(url_path git_add_config); +use Girocco::Util qw(url_path git_add_config is_readonly); use Digest::MD5 qw(md5_hex); BEGIN { eval { require HTML::Email::Obfuscate; 1 } or @@ -174,6 +174,12 @@ our $site_name = $Girocco::Config::title; ## html text to include at home page our $home_text = "$Girocco::Config::basedir/gitweb/indextext.html"; +## read-only version of text, but only when in read-only mode and this exists +## note that when running in a fastcgi mode, changes to read-only mode will +## not show up with regards to this text until the next fastcgi instance +## spawns -- use a "graceful" web server restart to force an immediate effect +our $home_text_ro = "$Girocco::Config::basedir/gitweb/indextext_readonly.html"; + ## URI of stylesheets our @stylesheets = ("@{[url_path($Girocco::Config::gitwebfiles)]}/gitweb.css"); @@ -251,6 +257,10 @@ $feature{'actions'}{'default'}=[ # have already been completed by now it's safe to "cd /" at this point. chdir "/"; +# If in read-only mode and $home_text_ro exists, set $home_text to it +defined($home_text_ro) && $home_text_ro ne "" && is_readonly() && -f $home_text_ro and + $home_text = $home_text_ro; + # Stuff extra Git configuration options into GIT_CONFIG_PARAMETERS # This mirrors what shlib.sh does (mostly) # Only the options that are appropriate for gitweb are included here diff --git a/install.sh b/install.sh index 7073586..842a361 100755 --- a/install.sh +++ b/install.sh @@ -1058,8 +1058,15 @@ rm -rf "$basedir/cgi" [ -z "$webreporoot" ] || { rm -f "$webreporoot" && ln -s "$cfg_reporoot" "$webreporoot"; } if [ -z "$cfg_httpspushurl" ] || [ -n "$cfg_pretrustedroot" ]; then grep -v 'rootcert[.]html' gitweb/indextext.html >"$basedir/gitweb/indextext.html" + if [ -f gitweb/indextext_readonly.html ]; then + grep -v 'rootcert[.]html' gitweb/indextext_readonly.html \ + >"$basedir/gitweb/indextext_readonly.html" + fi else cp gitweb/indextext.html "$basedir/gitweb" + if [ -f gitweb/indextext_readonly.html ]; then + cp gitweb/indextext_readonly.html "$basedir/gitweb" + fi fi mv "$basedir"/html/*.css "$basedir"/html/*.js "$webroot" cp mootools.js "$webroot" -- 2.11.4.GIT