[ Home on 246net ] [ Git index ]

Git HTTPサーバー構築

Gitのリポジトリを、HTTP経由で読み書きするサーバーを構築します。

本記事では、CentOS 6の上でHTTPサーバーとGitに含まれるCGIコマンド git-http-backend を用いたGit HTTPサーバー環境の構築例を紹介します。

なお、WebDAVを使ってHTTP越しにリポジトリを変更する方法もありますが、ユーザー認証/性能の点で使用を見合わせました。

CentOS 6のgitについては、OS標準搭載の1.7.1の問題と回避策、新しいバージョンのgitを入れる方法の1つを紹介しています。

Gitリポジトリの認証については、HTTPの認証を利用する方法に加えて、チケット管理ツールRedmineのユーザー認証と統合する設定(必要なモジュール追加)を紹介します。

環境構築

Git

Git 1.7.1(CentOS 6標準搭載版)

CentOS標準のgitをインストールします。なお、開発ツール一式と一緒に入れておきます。

# yum groupinstall "Development Tools"

Gitのバージョンについて(メモ)

CentOS 6(RHEL 6)標準搭載gitのバージョンは、1.7.1と古いものです。Gitは同じ1.7.xでも末尾の数字で大きな変更がある他、HTTPアクセスでBasic認証に問題があるため(本記事で後述)、できれば新しいバージョンのGitに差し替えたいところです。

CentOS 6の標準搭載gitを、Repoforgeのgitに差し替える(オプション)

Repoforge(旧RPMforge)で提供される新しいバージョンのgitをインストールします。

ただし、Repoforgeには極力依存したくないので、yumのリポジトリには追加せず、必要なrpmを個別にダウンロードして適用します。

Repoforgeが提供するRPMパッケージをインストールする場合、通常Repoforgeのyum設定ファイルをCentOS上に配置し、オンラインでインストールする手順が紹介されます。しかし、著者のCentOS設定ポリシーは、極力外部のyumリポジトリには依存しないこととし、CentOS自体とepelだけ有効にしています(ローカルにそれぞれのyumリポジトリのミラーを置いてます)。それ以外のパッケージを入れる場合は、面倒ですが手動でrpmパッケージをダウンロードしてからインストールしています。これは、インターネットと切り離された環境でのCentOSセットアップを可能にする意図をもっているからです。
# yum erase perl-Git
Loaded plugins: fastestmirror
Setting up Remove Process
Resolving Dependencies
--> Running transaction check
---> Package perl-Git.noarch 0:1.7.1-2.el6_0.1 will be erased
--> Processing Dependency: perl(Git) for package: git-1.7.1-2.el6_0.1.x86_64
--> Processing Dependency: perl-Git = 1.7.1-2.el6_0.1 for package: git-1.7.1-2.el6_0.1.x86_64
--> Running transaction check
---> Package git.x86_64 0:1.7.1-2.el6_0.1 will be erased
--> Finished Dependency Resolution

Dependencies Resolved

================================================================================
 Package          Arch           Version                    Repository     Size
================================================================================
Removing:
 perl-Git         noarch         1.7.1-2.el6_0.1            @base          35 k
Removing for dependencies:
 git              x86_64         1.7.1-2.el6_0.1            @base          15 M

Transaction Summary
================================================================================
Remove        2 Package(s)

Installed size: 15 M
Is this ok [y/N]: y
Downloading Packages:
Running rpm_check_debug
Running Transaction Test
Transaction Test Succeeded
Running Transaction
  Erasing    : git-1.7.1-2.el6_0.1.x86_64                                   1/2
  Erasing    : perl-Git-1.7.1-2.el6_0.1.noarch                              2/2
  Verifying  : perl-Git-1.7.1-2.el6_0.1.noarch                              1/2
  Verifying  : git-1.7.1-2.el6_0.1.x86_64                                   2/2

Removed:
  perl-Git.noarch 0:1.7.1-2.el6_0.1

Dependency Removed:
  git.x86_64 0:1.7.1-2.el6_0.1

Complete!
# yum localinstall git-1.7.11.3-1.el6.rfx.x86_64.rpm perl-Git-1.7.11.3-1.el6.rfx.x86_64.rpm
Loaded plugins: fastestmirror
Setting up Local Package Process
Examining git-1.7.11.3-1.el6.rfx.x86_64.rpm: git-1.7.11.3-1.el6.rfx.x86_64
Marking git-1.7.11.3-1.el6.rfx.x86_64.rpm to be installed
Loading mirror speeds from cached hostfile
 * base: ftp.riken.jp
 * epel: ftp.kddilabs.jp
 * extras: ftp.riken.jp
 * updates: ftp.riken.jp
Examining perl-Git-1.7.11.3-1.el6.rfx.x86_64.rpm: perl-Git-1.7.11.3-1.el6.rfx.x86_64
Marking perl-Git-1.7.11.3-1.el6.rfx.x86_64.rpm to be installed
Resolving Dependencies
--> Running transaction check
---> Package git.x86_64 0:1.7.11.3-1.el6.rfx will be installed
--> Processing Dependency: perl(DBI) for package: git-1.7.11.3-1.el6.rfx.x86_64
---> Package perl-Git.x86_64 0:1.7.11.3-1.el6.rfx will be installed
--> Processing Dependency: perl(SVN::Client) for package: perl-Git-1.7.11.3-1.el6.rfx.x86_64
--> Processing Dependency: perl(SVN::Core) for package: perl-Git-1.7.11.3-1.el6.rfx.x86_64
--> Processing Dependency: perl(SVN::Delta) for package: perl-Git-1.7.11.3-1.el6.rfx.x86_64
--> Processing Dependency: perl(SVN::Ra) for package: perl-Git-1.7.11.3-1.el6.rfx.x86_64
--> Running transaction check
---> Package perl-DBI.x86_64 0:1.609-4.el6 will be installed
---> Package subversion-perl.x86_64 0:1.6.11-7.el6 will be installed
--> Finished Dependency Resolution

Dependencies Resolved

================================================================================
 Package    Arch   Version            Repository                           Size
================================================================================
Installing:
 git        x86_64 1.7.11.3-1.el6.rfx /git-1.7.11.3-1.el6.rfx.x86_64       17 M
 perl-Git   x86_64 1.7.11.3-1.el6.rfx /perl-Git-1.7.11.3-1.el6.rfx.x86_64 116 k
Installing for dependencies:
 perl-DBI   x86_64 1.609-4.el6        base                                705 k
 subversion-perl
            x86_64 1.6.11-7.el6       base                                796 k

Transaction Summary
================================================================================
Install       4 Package(s)

Total size: 19 M
Total download size: 1.5 M
Installed size: 22 M
Is this ok [y/N]: y
Downloading Packages:
(1/2): perl-DBI-1.609-4.el6.x86_64.rpm                   | 705 kB     00:00
(2/2): subversion-perl-1.6.11-7.el6.x86_64.rpm           | 796 kB     00:00
--------------------------------------------------------------------------------
Total                                           1.2 MB/s | 1.5 MB     00:01
Running rpm_check_debug
Running Transaction Test
Transaction Test Succeeded
Running Transaction
  Installing : perl-DBI-1.609-4.el6.x86_64                                  1/4
  Installing : subversion-perl-1.6.11-7.el6.x86_64                          2/4
  Installing : perl-Git-1.7.11.3-1.el6.rfx.x86_64                           3/4
  Installing : git-1.7.11.3-1.el6.rfx.x86_64                                4/4
  Verifying  : git-1.7.11.3-1.el6.rfx.x86_64                                1/4
  Verifying  : perl-Git-1.7.11.3-1.el6.rfx.x86_64                           2/4
  Verifying  : subversion-perl-1.6.11-7.el6.x86_64                          3/4
  Verifying  : perl-DBI-1.609-4.el6.x86_64                                  4/4

Installed:
  git.x86_64 0:1.7.11.3-1.el6.rfx      perl-Git.x86_64 0:1.7.11.3-1.el6.rfx

Dependency Installed:
  perl-DBI.x86_64 0:1.609-4.el6      subversion-perl.x86_64 0:1.6.11-7.el6

Complete!

HTTPサーバー

非認証、Basic認証、Digest認証

CentOS標準のhttpd(Apache 2)と、httpdからPerlを実行するmod_perl、そしてダイジェスト認証時に使用するハッシュ perl-Digest-SHAをインストールしておきます。

# yum install httpd httpd-develop mod_perl perl_Digest_SHA

Redmine認証(LDAP認証を使わない)

Redmine側でLDAP認証しているユーザーをGitでは使用しない場合は、追加モジュールは不要です。

Redmineをインストールした中に含まれるextra/svn/Redmine.pmを、Perlの検索パスのどこかに配置します。ここでは、/etc/httpd/の下に次のディレクトリ構造でシンボリックリンクを貼っておきます。

# mkdir -p /etc/httpd/Apache/Authn
# ln -s /var/lib/redmine-2.2.2/extra/svn/Redmine.pm /etc/httpd/Apache/Authn/Redmine.pm

※ /var/lib/redmine のシンボリックリンクを作っておくのがバージョンアップ時に作業漏れがなくてよいかも。

Redmine認証(LDAP認証を使う)

まず上述のRedmine認証(LDAP認証を使わない)の手順にあるシンボリックリンクを作成します。

Redmine側でLDAP認証しているユーザーをGitで使用する場合、Perlの追加モジュールが必要になります。CentOS標準およびEPELから極力インストールしますが、どうしても不足するモジュールが2つ残ります(Auth::SimpleAuthen::Simple::LDAP)。

この2つのモジュールについては、それぞれ次のいずれかの方法でインストールします。

CPANサイトからインストールすると、後々アンインストールが大変なので、OSの管理上できればRPM化したいところです。

CentOS標準およびEPELに用意されるPerlモジュールのインストール

CPANからインストールするとき、依存するモジュールも一緒にインストールしますが、極力RPMパッケージを使用したいので、あらかじめCentOS 6用にパッケージ化されているperlモジュールはCPANサイトからのインストールに先立ってインストールしておきます。

# yum install perl-LDAP perl-Params-Validate perl-Module-Runtime \
perl-Module-Implementation perl-Class-Accessor perl-Class-Data-Inheritable \
perl-Crypt-PasswordMD5 perl-Test-Simple 

CPANサイトからPerlモジュールをインストール

次に、CPANからインストールするためのパッケージをインストールします。

# yum install perl-CPAN perl-YAML

CPANからインストールを行います。cpan[n]> というプロンプトが表示されるので下線部のコマンドを入力し実行します。

# perl -MCPAN -e shell
...
cpan[1]> o conf prerequisites_policy follow
cpan[2]> o conf commit
cpan[3]> install Authen::Simple::LDAP
...

途中、何回も次のような確認を求めてきます。実行時には不要なモジュールなので、いずれも no と入力します。

J/JP/JPEACOCK/version-0.9901.tar.gz is just needed temporarily during building or
 testing. Do you want to install it permanently? [yes] no

無事ビルド(makeとinstall)が完了すると、デフォルトのインストール先である/usr/local下にモジュールが展開されます。

$ find /usr/local -type f
/usr/local/lib64/perl5/auto/Authen/Simple/LDAP/.packlist
/usr/local/lib64/perl5/auto/Authen/Simple/.packlist
/usr/local/share/perl5/Authen/Simple/Adapter.pm
/usr/local/share/perl5/Authen/Simple/Password.pm
/usr/local/share/perl5/Authen/Simple/ActiveDirectory.pm
/usr/local/share/perl5/Authen/Simple/Apache.pm
/usr/local/share/perl5/Authen/Simple/Log.pm
/usr/local/share/perl5/Authen/Simple/LDAP.pm
/usr/local/share/perl5/Authen/Simple.pm
/usr/local/share/man/man3/Authen::Simple::Password.3pm
/usr/local/share/man/man3/Authen::Simple.3pm
/usr/local/share/man/man3/Authen::Simple::Apache.3pm
/usr/local/share/man/man3/Authen::Simple::LDAP.3pm
/usr/local/share/man/man3/Authen::Simple::ActiveDirectory.3pm
/usr/local/share/man/man3/Authen::Simple::Adapter.3pm
/usr/local/share/man/man3/Authen::Simple::Log.3pm
$

EPEL testingリポジトリからAuth::Simpleをインストール

Authen::Simpleモジュールについては、EPEL testingリポジトリperl-Authen-Simpleパッケージが提供されています。これをダウンロードしてインストールします。

# yum localinstall perl-Authen-Simple-0.4-5.el6.noarch.rpm
...

Auth::Simple::LDAPのRPMパッケージ作成とインストール

Authen::Simple::LDAPのRPMパッケージを、rpmbuildコマンドでビルドして作成することとします。

RPM作成時に必要なSPECファイルの記述は、ゼロから書くのは大変なので、類似したRPMパッケージのものを流用します。

最初、上述のEPEL testingにあるperl-Auth-Simple-0.4-5.el6用のSPECファイルを流用しようとしたところ、perl-Authen-Simple.specではBuild.PLファイルを使ってビルドする記述になってましたが、perl-Authen-Simple-LDAPではMakefile.PLを使います。そこで、より新しいfedora 18のperl-Authen-Simple-0.5-3.fc18のspecファイルを参照したところ、こちらはMakefile.PLを使うようになっていました。そこで、fedora 18のものを参考にして今回perl-Authen-Simple-LDAP.specを作成しました。

CPANサイトから、Authen::Simple::LDAPのソースアーカイブをダウンロードします。CPANサイトを開き、"Authen::Simple::LDAP"を検索、Authen-Simple-LDAP-0.3ページから[Download]でモジュールアーカイブファイルをダウンロードします。

rpmbuildを初回に実施するユーザーであればrpmbuild初期設定を行います。

~$ mkdir -p rpm/{BUILD,RPMS,SOURCES,SPECS,SRPMS}
~$ vi .rpmmacros
~$ cat .rpmmacros
%_topdir /home/torutk/rpm
%packager Toru Takahashi <torutk@example.org>
%dist .el6
~$

ダウンロードしたperl-Authen-Simple-0.5-3.fc18.src.rpmからspecファイルを取り出します。

work$ rpm2cpio ~/perl-Authen-Simple-0.5-3.fc18.src.rpm | cpio -i
work$ ls
Authen-Simple-0.5.tar.gz  perl-Authen-Simple.spec
work$

このspecファイルを参考に、~/rpm/SPECS/perl-Authen-Simple-LDAP.spec を作成します。

Name:           perl-Authen-Simple-LDAP
Version:        0.3
Release:        1%{?dist}
Summary:        Simple LDAP authentication
License:        GPL+ or Artistic
Group:          Development/Libraries
URL:            http://search.cpan.org/dist/Authen-Simple-LDAP/
Source0:        http://www.cpan.org/authors/id/C/CH/CHANSEN/Authen-Simple-LDAP-%{version}.tar.gz
BuildRoot:      %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n)
BuildArch:      noarch
BuildRequires:  perl(Authen::Simple)
BuildRequires:  perl(Class::Accessor::Fast)
BuildRequires:  perl(Class::Data::Inheritable)
BuildRequires:  perl(Crypt::PasswdMD5)
BuildRequires:  perl(Module::Implementation)
BuildRequires:  perl(Params::Validate)
BuildRequires:  perl(Test::Fatal)
BuildRequires:  perl(Test::Requires)
BuildRequires:  perl(Try::Tiny)
Requires:       perl(:MODULE_COMPAT_%(eval "`%{__perl} -V:version`"; echo $version))

%description
Authenticate against a LDAP service.

%prep
%setup -q -n Authen-Simple-LDAP-%{version}

%build
%{__perl} Makefile.PL installdirs=vendor
make %{?_smp_mflags}

%install
make pure_install PERL_INSTALL_ROOT=$RPM_BUILD_ROOT

find $RPM_BUILD_ROOT -type f -name .packlist -exec rm -f {} \;
find $RPM_BUILD_ROOT -depth -type d -exec rmdir {} 2>/dev/null \;

%{_fixperms} $RPM_BUILD_ROOT/*

%check
make test

%clean
rm -rf $RPM_BUILD_ROOT

%files
%defattr(-,root,root,-)
%doc Changes README
%{perl_vendorlib}/*
%{_mandir}/man3/*

%changelog
* Mon Jan 28 2013 Toru Takahashi <torutk@example.org> - 0.3-1
- Specfile created based on perl-Authen-Simple-0.4-5, and modified.

※ このspecファイルは、こちらのgithubに挙げています。

RPMパッケージを作成します。

~$ cp Authen-Simple-LDAP-0.3.tar.gz ~/rpm/SOURCES
~$ rpmbuild -ba rpm/SPECS/perl-Authen-Simple-LDAP.spec
...
~$ ls rpm/RPMS/noarch
perl-Authen-Simple-LDAP-0.3-1.el6.noarch.rpm
~$

インストールします。

~$ yum groupinstall rpm/RPMS/noarch/perl-Authen-Simple-LDAP-0.3-1.el6.noarch.rpm
...
~$

GitリポジトリアクセスのHTTPサーバー(非認証)構築

認証不要でリポジトリを読み書きします。

サーバー設定

サーバー側で共有リポジトリ作成

共有のgit リポジトリを作成します。

# mkdir /var/lib/git
# git init --bare --shared /var/lib/git/anonymous.git
# cd /var/lib/git/anonymous.git
# git update-server-info
# mv hooks/post-update.sample hooks/post-update

http.receivepackをtrueにします。

# git config http.receivepack true

HTTPサーバー設定

HTTPサーバーの設定で/gitへのアクセスをCGI呼び出しに置き換えます。

/etc/httpd/conf.d/git.conf
SetEnv GIT_PROJECT_ROOT /var/lib/git
SetEnv GIT_HTTP_EXPORT_ALL

ScriptAlias /git/ /usr/libexec/git-core/git-http-backend/

クライアント側からアクセス

クローンしてくる

$ mkdir work
$ cd work
$ git clone http://cisterna/git/anonymous.git
Initialized empty Git repository in /home/hogehoge/work/anonymous/.git/
warning: You appear to have cloned an empty repository.
$ ls
anonymous
$ cd anonymous
$ vi README.txt
$ git add README.txt
$ git commit
...
$ git push origin master
Counting objects: 3, done.
Writing objects: 100% (3/3), 262 bytes, done.
Total 3 (delta 0), reused 0 (delta 0)
To http://cisterna/git/anonymous.git
 * [new branch]      master -> master
$

GitリポジトリアクセスHTTPサーバー(Basic認証)構築

Basic認証で認証されたユーザーのみアクセス可能な設定を行います。

トラブルシュート記録

試行(第1回)

/etc/httpd/conf.d/git.conf にBasic認証設定を以下のように記載

SetEnv GIT_PROJECT_ROOT /var/lib/git
SetEnv GIT_HTTP_EXPORT_ALL

ScriptAlias /git/ /usr/libexec/git-core/git-http-backend/

<LocationMatch "^/git/basicauthen.git/.*">
    AuthType    Basic
    AuthName    "Git Repository"
    AuthUserFile /etc/httpd/conf.d/git_auth.basic
    Require valid-user
</LocationMatch>

リモートからクローンを試みるとエラーとなった

$ git clone http://foo.example.com/git/basicauthen.git
Initialized empty Git repository in /home/torutk/work/basicauthen/.git/
error: The requested URL returned error: 401 while accessing http://foo.example.com/git/basicauthen.git/info/refs

HTTPサーバーのログは

192.168.123.146 - - [27/Jan/2013:10:09:00 +0900] "GET /git/basicauthen.git/info/refs?service=git-upload-pack HTTP/1.1" 401 475 "-" "git/1.7.1"
192.168.123.146 - - [27/Jan/2013:10:09:00 +0900] "GET /git/basicauthen.git/info/refs HTTP/1.1" 401 475 "-" "git/1.7.1"

試行(第2回)

git.confを以下のように修正した。

- <LocationMatch "^/git/basicauthen.git/.*">
+ <LocationMatch "^/git/.*">

エラーは変わらなかった。

原因と対策

Windows上でCygwinのgit(バージョン1.7.9)では問題なくアクセスできることが分かった。原因は、CentOS 6.3 の git 1.7.1がBasic認証を要求するHTTPに対してアクセスするとき、ユーザーにユーザー/パスワードを入力するよう要求せず結果401エラーとなっています。Fedora 17のgit 1.7.11ではユーザー/パスワード入力を促してきます。

CentOS 6.xのgit 1.7.1のこの問題を回避する手段が2つあります。

GitリポジトリアクセスHTTPサーバー(Redmine認証)構築

Gitのリポジトリアクセス認証を、Redmineユーザー認証と統合する方法があります。ソフトウェア開発基盤にRedmineを使用している場合、認証はRedmineに統合するのが便利です。アクセス制御も、プロジェクトに対するRedmineのユーザーのアクセス権をGitリポジトリに引き継ぐので、ユーザー認証とアクセス権の管理を一元化することができます。また、RedmineユーザーをLDAP(Active Directory)で認証していた場合も対応できています。

サーバー側設定

httpd用の設定ファイルを作成します。ここでは、/etc/httpd/conf.d/redmine_git.conf としますが、設定ファイル名は拡張子.confであれば任意でOKです。

PerlLoadModule Apache::Authn::Redmine
PerlLoadModule Authen::Simple::LDAP

SetEnv GIT_PROJECT_ROOT /var/lib/git
SetEnv GIT_HTTP_EXPORT_ALL
SetEnv REMOTE_USER=$REDIRECT_REMOTE_USER

ScriptAlias /git/ /usr/libexec/git-core/git-http-backend/

<LocationMatch /git/>
  PerlAccessHandler Apache::Authn::Redmine::access_handler
  PerlAuthenHandler Apache::Authn::Redmine::authen_handler
  AuthType Basic
  AuthName Git

# for Redmine Authentication
  RedmineDSN "DBI:mysql:database=redminedb;host=localhost"
  RedmineDbUser "redmine"
  RedmineDbPass "foobar"
  RedmineGitSmartHttp yes

  Require valid-user
</LocationMatch>

クライアント側設定

クライアント側の設定(アカウントの指定方法)は、Basic認証と同じです。

Gitリポジトリにアクセスするユーザーは、Redmineの該当プロジェクトにメンバーとして登録し適切なアクセス権が設定されている必要があります。

$ git clone http://cisterna/git/mabinogion.pwyll
Cloning into 'mabinogion.pwyll'...
warning: You appear to have cloned an empty repository.
$ ls
mabinogion.pwyll
$ git push origin master
Username for 'http://cisterna': hogehoge
Password for 'http://hogehoge@cisterna': fugafuga
Counting objects: 3, done.
Compressing objects: 100% (2/2), done.
Writing objects: 100% (3/3), 326 bytes, done.
Total 3 (delta 0), reused 0 (delta 0)
To http://cisterna/git/mabinogion.pwyll
 * [new branch]      master -> master
$

トラブルシュート

Redmine認証の場合、リポジトリの閲覧権は匿名でもあるため、git cloneはユーザー名/パスワードを入力することなく成功することが多いです。一方、git pushについては、perlモジュールの不足、設定ファイルの記述ミス、ディレクトリのアクセス権などの様々な要因でエラーになることがあります。