From d36c5f7baf4f11c7d6765afac2f5fb9449b0e0a7 Mon Sep 17 00:00:00 2001 From: Ian Jackson Date: Sat, 22 May 2010 17:25:07 +0100 Subject: [PATCH] WIP entirely new git approach with config parsers --- .gitignore | 11 ++++ git-daemon/Makefile | 19 ++++-- git-daemon/README | 53 +++++++++++++---- git-daemon/chiark-urlmap | 32 ++-------- git-daemon/{git-daemon => git-daemon.in} | 27 +++++---- git-daemon/{git-service => git-service.in} | 29 ++++----- git-daemon/git-urlmap | 48 --------------- git-daemon/read-urlmap | 69 ++++++++++++++++++++++ 8 files changed, 173 insertions(+), 115 deletions(-) create mode 100644 .gitignore rename git-daemon/{git-daemon => git-daemon.in} (68%) rename git-daemon/{git-service => git-service.in} (53%) delete mode 100644 git-daemon/git-urlmap create mode 100644 git-daemon/read-urlmap diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..59c56ee --- /dev/null +++ b/.gitignore @@ -0,0 +1,11 @@ +*~ + +dist_tmp +userv-utils-*.tar.gz +build + +git-daemon/git-upload-pack +git-daemon/inetd.conf +git-daemon/git-daemon +git-daemon/git-service +git-daemon/sedscript diff --git a/git-daemon/Makefile b/git-daemon/Makefile index 07969cd..2d24274 100644 --- a/git-daemon/Makefile +++ b/git-daemon/Makefile @@ -1,20 +1,29 @@ # Makefile for userv-git-daemon # -# This was written by Tony Finch +# This was written by Tony Finch and subsequently +# heavily modified by Ian Jackson # You may do anything with it, at your own risk. # http://creativecommons.org/publicdomain/zero/1.0/ include ../settings.make -TARGETS= git-upload-pack inetd.conf +TARGETS= git-upload-pack inetd.conf git-daemon git-service SUBSTVARS= libuserv etcuserv -SEDSCRIPT= '$(foreach f, $(SUBSTVARS), s,@$f@,$($f),g; )' all: $(TARGETS) -%: %.in - sed $(SEDSCRIPT) <$< >$@.new && mv -f $@.new $@ +sedscript: Makefile + echo >$@.new '$(foreach f, $(SUBSTVARS), s,@$f@,$($f),g; )' + echo >>$@.new '/@@READ_URLMAP@@/c\' + perl >>$@.new -pe 's/$$/\\/' $@.new; \ + if test -x $<; then chmod +x $@.new; fi; \ + mv -f $@.new $@ install: all mkdir -p $(libuserv) $(etcuserv) $(services) diff --git a/git-daemon/README b/git-daemon/README index d8e6a7a..116a6b3 100644 --- a/git-daemon/README +++ b/git-daemon/README @@ -33,31 +33,64 @@ Operation: ---------- The userv-git-daemon is invoked by inetd which also tells it where to -find its global git-urlmap script. +find its global git-urlmap config. The git-daemon parses the request from the network and uses the global -git-urlmap script to determine which user will run the requested -service. It invokes userv for the request to be performed. The most +git-urlmap config to determine which user will run the requested +service. It invokes userv for the request to be performed. The most common service is git-upload-pack, which is confusingly named: it uploads from the repository to the network; other services supported by git are git-upload-archive and git-receive-pack. -The git-daemon will pass any service beginning git- to userv. The +The git-daemon will pass any service beginning git- to userv. The userv configuration determines which services may be requested. This package includes example git-upload-pack service configurations. The service configuration uses the git-service script to run the -service. It passes the global and per-user git-urlmap scripts to the +service. It passes the global and per-user git-urlmap configs to the git-service script to determine where in the filesyetem the requested -repository is. Later urlmap scripts override the choices made by -earlier ones. See the sample git-urlmap script for details of the -variables they can examine and set. +repository is. Later urlmap entries override the choices made by +earlier ones. If a repository is located, the git-service script runs the requested service, which is simply the git program with the same name. +Configuration: +-------------- + +See "chiark-urlmap" for an example. + +Each line is one of: + + single-user [/] [] + matching requests will be handled by + and unless overridden by handled by + serving subdirectories of + + multi-user [/] + matching requests are only those those next + path element starts with ~. The + request will be handled by and unless + overridden by will be handled by + serving subdirectories of + ( must be a relative path) + + repo-regexp + For per-user service. Subrepos must match this + regexp, which must contain a single matching + group which is the filesystem pathname inside + the . The default is (Tcl syntax): + repo-regexp {^(\w[-+._0-9A-Za-z]*)$} + + [no-]require-git-daemon-export-ok + For per-user service. Default is no-. + +Last match, or last setting, wins. +s may start with ~ + + ---------------------------------------------- -This was written by Tony Finch -You may do anything with it, at your own risk. +This was written by Tony Finch and subsequently +heavily modified by Ian Jackson http://creativecommons.org/publicdomain/zero/1.0/ diff --git a/git-daemon/chiark-urlmap b/git-daemon/chiark-urlmap index 499f21c..09e2a11 100644 --- a/git-daemon/chiark-urlmap +++ b/git-daemon/chiark-urlmap @@ -1,29 +1,5 @@ -# chiark's configuration for the userv git daemon. -# -# This was written by Tony Finch -# You may do anything with it, at your own risk. -# http://creativecommons.org/publicdomain/zero/1.0/ -if ($host eq 'git.chiark.greenend.org.uk') { - if ($path =~ m{^~([^/]*)/(.*)}) { - $user = $1; - $dir = '.userv/public-git'; - $repo = $2; - } else { - $user = 'webmaster'; - $dir = '/u2/git-repos'; - $repo = $path; - } -} elsif ($server_addr eq '172.31.80.8' and - $host eq 'cabal.greenend.org.uk' and - $path =~ m|^~([^/]*)/(.*)$|) { - $user = $1; - $dir = 'cabal-git'; - $repo = $2; -} elsif ($host eq 'dotat.at') { - $user = 'fanf'; - $dir = 'public-git'; - $repo = $path; -} - -# end +single-user dotat.at fanf dotat-git +multi-user git.chiark.greenend.org.uk public-git +multi-user cabal.greenend.org.uk cabal-git +single-user git.chiark.greenend.org.uk webmaster /u2/git-repos diff --git a/git-daemon/git-daemon b/git-daemon/git-daemon.in similarity index 68% rename from git-daemon/git-daemon rename to git-daemon/git-daemon.in index 645cbc5..8fe8ca1 100755 --- a/git-daemon/git-daemon +++ b/git-daemon/git-daemon.in @@ -2,8 +2,8 @@ # # A git daemon with an added userv security boundary. # -# This was written by Tony Finch -# You may do anything with it, at your own risk. +# This was written by Tony Finch and subsequently +# heavily modified by Ian Jackson # http://creativecommons.org/publicdomain/zero/1.0/ use strict; @@ -22,7 +22,7 @@ sub ntoa { } our ($client,$client_addr,$client_port) = ntoa getpeername STDIN; our ($server,$server_addr,$server_port) = ntoa getsockname STDIN; -our ($service,$path,$host,$user); +our ($service,$specpath,$spechost); openlog 'userv-git-daemon', 'pid', 'daemon'; sub fail { syslog 'err', "$client @_"; exit } @@ -44,21 +44,28 @@ sub xread { my $hex_len = xread 4; fail "Bad hex in packet length" unless $hex_len =~ m|^[0-9a-fA-F]{4}$|; my $line = xread -4 + hex $hex_len; -unless (($service,$path,$host) = $line =~ +unless (($service,$specpath,$spechost) = $line =~ m|^(git-[a-z-]+) /*([!-~]+)\0host=([!-~]+)\0$|) { $line =~ s|[^ -~]+| |g; fail "Could not parse \"$line\"" } -our $uri = $_ = "git://$host/$path"; -for my $cf (@ARGV) { do $cf } -fail "No user for $uri" unless defined $user; -syslog 'notice', "$client $service $uri"; +@@READ_URLMAP@@ + +fail "No mapping for $uri" unless defined $serve_user; +syslog 'notice', "$client $service $serve_user $uri"; + +my ($hn,$ha,$at,$naddrs,@addrs) = gethostbyname $host; +die "hostname/address mismatch ($spechost $server_addr)" unless grep { + $server_addr eq inet_ntoa $_ + } @addrs; my @opts = map "-D$_=${$::{$_}}", - grep defined ${$::{$_}} && /^[a-z_]+$/, keys %::; + qw(service path host + client client_addr client_port + server server_addr server_port); -my @cmd = ('userv', @opts, $user, $service); +my @cmd = ('userv', '-t300', @opts, $user, $service); no warnings; # suppress errors to stderr exec @cmd or fail "exec userv: $!"; diff --git a/git-daemon/git-service b/git-daemon/git-service.in similarity index 53% rename from git-daemon/git-service rename to git-daemon/git-service.in index aeb4e0e..43ff340 100755 --- a/git-daemon/git-service +++ b/git-daemon/git-service.in @@ -2,8 +2,8 @@ # # userv-git-daemon service script # -# This was written by Tony Finch -# You may do anything with it, at your own risk. +# This was written by Tony Finch and subsequently +# heavily modified by Ian Jackson # http://creativecommons.org/publicdomain/zero/1.0/ use strict; @@ -11,29 +11,30 @@ use warnings; use Sys::Syslog; -${$::{$_}} = $ENV{"USERV_U_$_"} - for grep s|^USERV_U_([a-z_]+)$|$1|, keys %ENV; +our ($client,$service,$specpath,$spechost,@opts); -our ($client,$service,$path,$host,@opts); +${$::{$_}} = $ENV{"USERV_U_$_"} + for qw(service path host) openlog "userv-$service", 'pid', 'daemon'; sub fail { syslog 'err', "$client @_"; exit } -our ($check_repo,$check_export,$dir,$repo) = (1,0); -our $uri = $_ = "git://$host/$path"; -for my $cf (@ARGV) { do $cf } +@@READ_URLMAP@@ + +fail "No mapping for $uri ($ENV{USERV_USER})" unless defined $serve_user; + +$1 = undef; +fail "Bad subdirectory $serve_dir" unless $serve_dir =~ m/$repo_regexp/o; +our $dir = $1; + +$dir = "$ENV{HOME}/$dir" unless $dir =~ m|^/|; -my $home = (getpwuid $<)[7]; -$dir = "$home/$dir" if $dir =~ m|^[^/]| - or $dir =~ s|^~/||; -fail "Bad filename $repo" if $check_repo - and $repo !~ m|^\w[\w.=+-]*\.git$|; $dir = "$dir/$repo" if defined $repo; $path = $check_export ? "$dir/git-daemon-export-ok" : $dir; fail "$! $path" unless -e $path; syslog 'notice', "$client $dir"; -@opts = qw( --strict --timeout-30 ) +@opts = qw( --strict ) if @opts == 0 and $service eq 'git-upload-pack'; my @cmd = ($service =~ m|^(git)-(.*)$|, @opts, $dir); diff --git a/git-daemon/git-urlmap b/git-daemon/git-urlmap deleted file mode 100644 index a72b48c..0000000 --- a/git-daemon/git-urlmap +++ /dev/null @@ -1,48 +0,0 @@ -# Configuration file for the userv git daemon. -# -# This was written by Tony Finch -# You may do anything with it, at your own risk. -# http://creativecommons.org/publicdomain/zero/1.0/ -# -# This is a perl script which is expected to turn the information -# found in the request variables into the location of the -# corresponding repository, which it stores in the output variables. -# Some information about the network connection is also available. -# -# REQUEST VARIABLES -# $service - normally "git-upload-pack" -# `git daemon --help` describes other possible services -# $host -# $path -# $uri = git://$host/$path -# $_ = $uri -# -# OUTPUT VARIABLES -# $user - who runs the service -# $dir - may be: -# * an absolute path -# * a path relative to the user's home directory -# * equivalently, a path starting ~/ -# $repo - if set, the repository's path is $dir/$repo -# otherwise, the repository's path is just $dir -# $check_export - whether to check for a git-daemon-export-ok file -# (default false) -# $check_repo - whether to restrict the syntax of $repo -# (default true) -# @opts - options for the service command -# -# CONNECTION VARIABLES -# $client_addr -# $client_port -# $client = [$client_addr]:$client_port -# $server_addr -# $server_port -# $server = [$server_addr]:$server_port - -if ($path =~ m{^~([^/]*)/(.*)}) { - $user = $1; - $dir = 'public-git'; - $repo = $2; -} - -# end diff --git a/git-daemon/read-urlmap b/git-daemon/read-urlmap new file mode 100644 index 0000000..6ae3237 --- /dev/null +++ b/git-daemon/read-urlmap @@ -0,0 +1,69 @@ +# -*- perl -*- + +# uses: +# $specpath whole path from caller, minus any leading /s +# $spechost host from caller +# +# sets: +# +# always: +# $uri +# +# if match found for this host and path: +# $serve_user username, or undef if no match (then other serve_* invalid) +# $serve_dir directory as specified in config +# $serve_repo subpath under $serve_dir _including_ leading / +# +# for use by user's service program +# $repo_regexp +# $require_exportok + +die "no config" unless @ARGV; + +sub remain_path ($) { + # return value matches {( / [^/]+ )+}x + my ($p) = @_; + return "/$specpath" if !defined $p; + return "" if $p eq $specpath; + return substr($specpath,length($p)) + if substr($specpath,0,length($p)+1) eq "$p/"; + return undef; +} + +die unless length $specpath; + +our $uri = "git://$spechost/$specpath"; +our $repo_regexp= '^/*(\\w[-+._0-9A-Za-z]*)$'; +our $check_export= 0; + +while (<>) { + s/^\s*//; + s/\s+$//; + next unless m/\S/; + next if m/^\#/; + + if (m{^ single-user \s+ (\S+?) (/\S*)? \s+ (\S+) (?: \s+ (\S+) )? $ }x) { + my ($h,$p,$u,$d) = ($1,$2,$3,$4); + next unless $h ne $host; + $serve_repo= remain_path($p); + next unless defined $serve_repo; + $serve_user= $u; + $serve_dir= $d; + } elsif (m{^ multi-user \s+ (\S+?) (/\S*)? \s+ (\S+) $ }x) { + my ($h,$p,$d) = ($1,$2,$3); + next unless $1 ne $host; + $serve_repo= remain_path($p); + next unless defined $serv_repo; + next unless $serve_repo =~ s{ ^/\~( [a-z][-+_0-9a-z] )/$ }{ / }xi; + $serve_user= $u; + $serve_dir= $d; + } elsif (m{^ repo-regexp \s+ (\S.*) $ }x) { + $repo_regexp= $1; + } elsif (m{^ (no-)?require-git-daemon-export-ok $ }x) { + $check_export= !defined $1; + } else { + die "bad config"; + } +} + +# end -- 2.30.2