diff options
-rw-r--r-- | Makefile.PL | 3 | ||||
-rwxr-xr-x | gitaclhook | 36 | ||||
-rw-r--r-- | lib/GitACL.pm | 18 |
3 files changed, 47 insertions, 10 deletions
diff --git a/Makefile.PL b/Makefile.PL index db3859d..3f18606 100644 --- a/Makefile.PL +++ b/Makefile.PL @@ -27,10 +27,11 @@ WriteMakefile( 'LICENSE' => 'gpl', 'FIRST_MAKEFILE' => 'Makefile', 'VERSION' => '1.00', 'PM' => \%pm, 'EXE_FILES' => [ 'gitaclhook' ], 'PREREQ_PM' => { 'Getopt::Long' => 2.34, - 'File::Spec' => 3.39 } + 'File::Spec' => 3.39, + 'Net::CIDR' => 0.17 } ); @@ -83,20 +83,29 @@ The name of the project. It is obtained by removing the directory and suffix parts of the repository pathname. Thus, if the repository is located in B</var/gitroot/foobar.git>, then the corresponding name of the project is B<foobar>. An asterisk matches any project name. -=item I<USER> +=item I<USER>[B<@>I<CIDRLIST>] Name of the user. The word B<all> stands for any user, the word B<none> matches no one at all. Otherwise, if this part begins with a percent sign (B<%>), the rest of characters are treated as the name of the UNIX group to check and the rule matches any user in that group. Otherwise, the literal match is assumed. +The I<CIDRLIST> part, if present, restricts the rule to users coming from +IP addresses that match one of the elements in the list. I<CIDRLIST> is a +comma-separated list of IP addresses, ranges or CIDRs. An IP range is +defined as two IP addresses separated by a minus sign. A CIDR is defined as +network address, followed by a slash and length of the netmask in decimal. +For example: + + gray@10.0.0.0/16,192.168.1.0-192.168.10.255 + =back The optional parts are: =over 4 @@ -182,13 +191,13 @@ The project this entry applies to. =item B<gitAclVerb> [mandatory] The control verb. =item B<gitAclUser> [optional] -The user name or group (B<%>I<GROUPNAME>) specification. +The user name specification (see description in the B<ACL FILE> section). =item B<gitAclOp> [optional] The list of operation codes. =item B<gitAclRef> [optional] @@ -218,50 +227,59 @@ all project names. If no matching entry is found, the update is allowed. =head1 CONFIGURATION SETTINGS =over 4 -=item B<hooks.acl.type> STRING +=item B<hooks.acl.type> I<STRING> Type of the storage engine. Valid values are B<File> (default) and B<LDAP>. -=item B<hooks.acl.file> STRING +=item B<hooks.acl.file> I<STRING> For the B<File> storage engine, name of the ACL file. -=item B<hooks.acl.ldapconf> STRING +=item B<hooks.acl.ldapconf> I<STRING> For the B<LDAP> storage engine, the name of the configuration file to use instead of B</etc/ldap.conf>. -=item B<hooks.acl.log> STRING +=item B<hooks.acl.log> I<STRING> Send log info to this file. -=item B<hooks.acl.debug> NUMBER +=item B<hooks.acl.debug> I<NUMBER> Enable debugging. The bigger the number, the more debugging info will be displayed. -=item B<hooks.acl.quiet> BOOL +=item B<hooks.acl.quiet> I<BOOL> Suppress diagnostics on stderr. =item B<hooks.acl.default> B<allow>|B<deny> Sets the default rule, i.e. the one that will be executed if no other rule matched the request. Unless defined, B<deny> is assumed. -=item B<hooks.acl.httpd-user> STRING +=item B<hooks.acl.httpd-user> I<STRING> Name of the user httpd runs as. Define it if the repository can be accessed via HTTP(S). If B<gitaclhook> is run as this user, it will get the name of the user on behalf of which the update is performed from the environment variable B<REMOTE_USER>. +=item B<hooks.acl.ip-env-var> I<STRING> + +Name of the environment variable from where to retrieve remote IP address. +Default is B<REMOTE_ADDR>, if B<hooks.acl.httpd-user> is defined and current +user matches it, and B<SSH_CLIENT> otherwise. + +The part of the string up to the first space character (if any) is taken as +the IP address. + =back =head1 TEST MODE The B<--test> (B<-t>) option provides a mechanism for testing access control lists from the command line. The syntax is: diff --git a/lib/GitACL.pm b/lib/GitACL.pm index cde9624..4214b3f 100644 --- a/lib/GitACL.pm +++ b/lib/GitACL.pm @@ -15,12 +15,13 @@ # along with gitaclhook. If not, see <http://www.gnu.org/licenses/>. package GitACL; use strict; use File::Spec; +use Net::CIDR qw (cidrlookup); my %opstr = ('C' => 'create', 'D' => 'delete', 'U' => 'update', 'R' => 'rewind/rebase'); @@ -113,16 +114,26 @@ sub match_primary_group($$) { my ($name,$passwd,$uid,$gid) = getpwnam($user_name) or return 0; ($name) = getgrgid($gid) or return 0; return 1 if $name eq $group_name; return 0; } +sub match_host($$) { + my ($ip,$iplist) = @_; + return 0 unless defined($ip); + return cidrlookup($ip, split /,/, $iplist); +} + sub match_user($$) { my ($self, $expr) = @_; return 1 if ($expr eq 'all'); return 0 if ($expr eq 'none'); + if ($expr =~ /(.+)@(.+)/) { + return 0 unless match_host($self->{ip}, $2); + $expr = $1; + } if ($expr =~ /^%(.+)/) { return 1 if match_primary_group($self->{user_name}, $1); my ($name,$passwd,$gid,$members) = getgrnam($1) or return 0; my @a = split(/\s+/,$members); for (my $i = 0; $i <= $#a; $i++) { return 1 if $a[$i] eq $self->{user_name}; @@ -217,12 +228,19 @@ sub new { } $obj->deny("no such user") unless $obj->{user_name}; my $httpdusr = git_value('config', 'hooks.acl.httpd-user'); if (defined($httpdusr) and $obj->{user_name} eq $httpdusr) { $obj->deny("need authenticated user") unless $ENV{AUTH_TYPE}; $obj->{user_name} = $ENV{REMOTE_USER}; + $obj->{ip} = $ENV{REMOTE_ADDR}; + } else { + my $ipvar = git_value('config', 'hooks.acl.ip-env-var') or 'SSH_CLIENT'; + if (defined($ENV{$ipvar})) { + my @a = split /\S/, $ENV{$ipvar}, 2; + $obj->{ip} = $a[0]; + } } $obj->{project_name} = get_project_name($obj->{git_dir}); $obj->deny("need a ref name") unless defined($args{ref}); $obj->deny("bogus ref $args{ref}") unless $args{ref} =~ s,^refs/,,; $obj->{ref} = $args{ref}; |