VALUE-SERVERでgitリポジトリの公開

概要

apache2.2で.htaccessしか使えない状況での設定で手間取りましたが、VALUE-SERVERでgitリポジトリを公開することが出来たので、その方法を記事にしました。本来こういう用向きはGitHubでいいのですが、やっぱりやってみたいでしょ?

なお、今回はgitコマンドで扱えるhttpsのリポジトリを作成するところまでで、WebUIの設定は次回予定です。

設定の検討

今回はGitのマニュアルをベースに設定を検討していきます。

Smart HTTPで公開

Git – Smart HTTP

Gitのリポジトリ公開方法は、2020年1月31日現在、gitプロトコル、ssh、httpの3種類で、httpはさらにdum/smartの2種類に分かれます。gitプロトコルはVALUE-SERVERでは使えないし、sshだと公開してもVALUE-SERVER上ではアカウント作れないので自分しか使えません。 今回はhttpのより洗練されたsmartで公開する、ということです。

Apache自体のインストール

マニュアルではここからやっているのですが、これはVALUE-SERVERでは出来ません。

$ sudo apt-get install apache2 apache2-utils
$ a2enmod cgi alias env rewrite

https://git-scm.com/book/ja/v2/Git%E3%82%B5%E3%83%BC%E3%83%90%E3%83%BC-Smart-HTTP

最初に行っているのはdebian系Linuxでのapache2とapache2-utilのインストールです。apache2相当は絶対にインストールされてます。apache2-utilは、恐らくhtpasswdコマンドをインストールするためだと思うので、これはVALUE-SERVERにも入っており、問題ありません。

次に行っているのは、mod_cgi、mod_alias、mod_env、mod_rewriteなどのモジュールの有効化です。これらもVALUE-SERVERで有効になってます。apachectl -Mで確認できるので、気になる人はやってみてください。

つまりApache自体のインストールについては、すでにやってあるので問題ないということです。

gitリポジトリの所有グループ変更

次にやってるのはリポジトリ内のファイル・ディレクトリの所有グループ変更です。

$ chgrp -R www-data /opt/git

https://git-scm.com/book/ja/v2/Git%E3%82%B5%E3%83%BC%E3%83%90%E3%83%BC-Smart-HTTP

これはapacheからリポジトリを読み書きするために必要なことなのですが、VALUE-SERVERの設定では、ユーザーが扱える$HOME/public_html/*のファイルを実行するときにユーザーのアカウントで実行されるため設定の必要がありません。逆に言うとWebから自分のアカウントで出来ることは何でも出来るということなのですが…そういうセキュリティのプランなのです。

Apache の設定

いよいよ本題に近付いてきました。マニュアルで次に記載があるのは、httpd.confとかapache2.confやその分散された設定ファイルの内容です。これはディストリビューションごとにファイル名やディレクトリ構成が違いますが、そもそも管理者権限のないレンタルサーバでは変更できない部分です。

SetEnv GIT_PROJECT_ROOT /opt/git
SetEnv GIT_HTTP_EXPORT_ALL
ScriptAlias /git/ /usr/lib/git-core/git-http-backend/

https://git-scm.com/book/ja/v2/Git%E3%82%B5%E3%83%BC%E3%83%90%E3%83%BC-Smart-HTTP

これらは、ユーザーが唯一編集可能な設定ファイルである、.htaccessで記述する必要があります。内容とともに解説すると…

SetEnvは環境変数を設定するもの(ディレクティブ)です。ここではGIT_PROJECT_ROOT(リポジトリの場所)とGIT_HTTP_EXPORT_ALL( GIT_PROJECT_ROOT以下のリポジトリを全て公開する意思表示)を設定しています。これらは.htaccessに記載が可能であり、またVALUE-SERVERではOverrideが許可されている(つまり普通に使える)ので問題ありません。

ScriptAliasは、スクリプトの位置とURLの紐付けを行うもの(ディレクティブ)です。これは.htaccessには記載できないので、代替手段が必要になります。

ScriptAliasの代替

今回の設定は、URLパスとして/git/以下でアクセスされた場合に、/usr/lib/git-core/git-http-backendが処理をし、環境変数PATH_INFOに/git/をルートとしたパスが入るということを意味します。git-http-backendはgitパッケージに付属するコマンドで、VALUE-SERVERにも /usr/libexec/git-core/git-http-backend に入っています。ユーザーがWebから直接 git-http-backend を処理させる手段はありませんが、CGIスクリプトから呼び出すことは可能です。ということはつまり、$HOME/public_html/ドメイン/git ディレクトリの.htaccessに以下の記述を入れて、

AddHandler cgi-script .cgi

git-http-backend.cgiという名前で

#!/bin/sh
/usr/libexec/git-core/git-http-backend

とすれば、 URLパスとして/git/git-http-backend.cgi/以下でアクセスされた場合に、/usr/libexec/git-core/git-http-backendが処理をし、環境変数PATH_INFOに/git/git-http-backend.cgi/ をルートとしたパスが入る、を実現できます。

BASIC認証の設定

マニュアルで次に行っているのはBASIC認証の設定です。

RewriteEngine On
RewriteCond %{QUERY_STRING} service=git-receive-pack [OR]
RewriteCond %{REQUEST_URI} /git-receive-pack$
RewriteRule ^/git/ - [E=AUTHREQUIRED]

<Files "git-http-backend">
    AuthType Basic
    AuthName "Git Access"
    AuthUserFile /opt/git/.htpasswd
    Require valid-user
    Order deny,allow
    Deny from env=AUTHREQUIRED
    Satisfy any
</Files>

解説

  1. Rewriteを有効にします
  2. URLのクエリ文字列(?以降の部分)が"service=git-receive-pack"である場合、もしくは
  3. URLのパス文字列が"/git-receive-pack"で終わっている場合
  4. URLのパス文字列が"/git/"で始まっているなら環境変数AUTHREQUIREDを設定する
  5. ファイルシステム内のファイル名がgit-http-backendである場合
    1. 認証方式をBASIC認証とする
    2. realmを"Git Access"とする
    3. BASIC認証のパスワードを/opt/git/.htpasswdから読むものとする
    4. ユーザーの認証を「必要」とする
    5. 全てを許可した上で、Deny処理→Allow処理の順で処理する
    6. 環境変数 AUTHREQUIRED が設定されていたらDenyする
    7. Allowされているか、「必要」を満たせばアクセスを許可する

つまり git-http-backendで処理するケースでは、「URLのクエリ文字列(?以降の部分)が"service=git-receive-pack"である」か「URLのパス文字列が"/git-receive-pack"で終わっている」場合はBASIC認証を要求し、それ以外の場合は無条件にアクセスを許可するという設定。ようはgit cloneやpullなど読み取りは無条件アクセスで、git pushなど書き込みはBASIC認証を付けたいということ。

VALUE-SERVERでの対応検討

この部分は全て.htaccessに記述可能です。しかし、VALUE-SERVERのapache2.2では期待したとおりに機能しません。理由は以下の記事のとおりです。

ようは、適用順番のせいで「環境変数 AUTHREQUIRED が設定されていたらDenyする」が機能しないということなので、常に「無条件にアクセスを許可するという設定」になってしまいます。さすがに認証なしで無制限に書き込みが出来てしまったら何が起きるか分かりません(デフォルトでは無条件にアクセスしても書き込みはエラー(403)になりますが)。

RewriteでダメならSetEnvIfではどうか?

Rewriteは主にリダイレクトや、読み替え、つまりURLの書き換えに似た機能です。しかし、今回はURLの書き換えは行っておらず、HTTPリクエストのパラメータを解析してBASIC認証の必要判定を環境変数に入れたいだけです。であれば、SetEnvIfでも出来そうな気がしますが…しかしSetEnvIfではQUERY_STRINGパラメータを扱えません。今回の用件では使用できないということです。

じゃあQUERY_STRINGを使わない方法はないの?

ありました。

If you do not have mod_rewrite available to match against the query string, it is sufficient to just protect git-receive-pack itself, like:

https://git-scm.com/docs/git-http-backend
<LocationMatch "^/git/.*/git-receive-pack$">
 AuthType Basic
 AuthName "Git Access"
 Require group committers
 ...
</LocationMatch>

BASIC認証をするタイミングは遅れますが、これでもpushなど書き込みに制限付けられます。ただし、

In this mode, the server will not request authentication until the client actually starts the object negotiation phase of the push, rather than during the initial contact. For this reason, you must also enable the http.receivepack config option in any repositories that should accept a push. The default behavior, if http.receivepack is not set, is to reject any pushes by unauthenticated users; the initial request will therefore report 403 Forbidden to the client, without even giving an opportunity for authentication.

https://git-scm.com/docs/git-http-backend

遅れた分のアクセスはデフォルトだとエラーになるので、 git-http-backendには「認証してなくてもエラーにしない設定」(http.receivepack)にする必要があります。apacheが許可したらOKということです。

ただURLとのマッチングを行うLocationMatchセクションは.htaccessでは使用できません

解: LocationMatchをSetEnvIfで置き換える

こうしてしまえばいいのです。

SetEnvIf Request_URI "/git-receive-pack$" AUTHREQUIRED=yes
AuthType Basic
AuthName "Git Access"
Require group committers
...
Order deny,allow
Deny from env=AUTHREQUIRED
Satisfy any

これが今回使用した方法の骨子になります。

パスワードの設定

マニュアルの続きです。htpasswd -c パスワードファイル ユーザー名 して、パスワードファイルを作成して認証可能なユーザーを1人作ってるだけです。 htpasswdの使い方はそこら中にあるので各自で調べてください。

実際の設置方法

URIのベースとなるディレクトリを決めて作成

マニュアルだと/git/になっていた部分ですね。そのまま使うと悪戯されそうなので、ちょっと変えました。

$ cd $HOME/public_html/(ドメイン名)/
$ mkdir gitrepos

.htaccessの設置

$HOME/public_html/(ドメイン名)/gitrepos/に以下の.htaccessを置きました。

SetEnv GIT_PROJECT_ROOT (ホーム)/(リポジトリの設置場所)
SetEnv GIT_HTTP_EXPORT_ALL
AddHandler cgi-script .cgi

SetEnvIf Request_URI "/git-receive-pack$" AUTHREQUIRED=yes

Order Deny,Allow
Deny from env=AUTHREQUIRED
AuthType Basic
AuthName "Git Access"
AuthUserFile (ホーム)/(リポジトリの設置場所)/.htpasswd
AuthGroupFile /dev/null
Require valid-user
Satisfy Any

CGIスクリプトの設置

$HOME/public_html/(ドメイン名)/gitrepos/に以下のgit-http-backend.cgiを置きました。

#!/bin/sh
/usr/libexec/git-core/git-http-backend

実行可能属性を付けておきます。

$ cd $HOME/public_html/(ドメイン名)/gitrepos/
$ chmod u+x git-http-backend.cgi

gitリポジトリの作成

このディレクトリはセキュリティ上の理由から、$HOME/public_html/(ドメイン名)/ 配下には置きません。ここでは $HOME/(リポジトリの設置場所)/に複数のリポジトリを置くものとし、今回はsample1という名前のリポジトリを作成します。

$ mkdir -p $HOME/(リポジトリの設置場所)
$ cd $HOME/(リポジトリの設置場所)
$ mkdir sample1
$ cd sample1
$ git init --bare
$ git config http.receivepack true

最後のコマンドが、「認証してなくてもエラーにしない設定」をしています。今回の認証方式を使う場合は、公開リポジトリを作成する度に必要な操作になります。

パスワード認証ファイルの作成

$ cd $HOME/(リポジトリの設置場所)
$ htpasswd -c .htpasswd ユーザー名
$ chmod og-rwx .htpasswd

ユーザーは必要な数だけ作成してください。VALUE-SERVERでは、パスワード認証ファイルは自分以外読めない設定で十分です。

最後に

BASIC認証はパスワードが暗号化されません。必ずhttpsでアクセスするようにしてください。

公開リポジトリへの接続確認

早速リポジトリにアクセスしてみます。

$ git clone https://(ドメイン名)/gitrepos/git-http-backend.cgi/sample1
Cloning into 'sample1'...
warning: You appear to have cloned an empty repository.
$ 

取得成功したようです。それでは適当に何かcommitしてpushしてみます。

$ cd sample1
$ touch hoge
$ git add .
$ git commit -m "initial commit."
[master (root-commit) 23b1fde] initial commit.
 1 file changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 hoge
$ git push origin master
Enumerating objects: 3, done.
Counting objects: 100% (3/3), done.
Writing objects: 100% (3/3), 205 bytes | 205.00 KiB/s, done.
Total 3 (delta 0), reused 0 (delta 0)
Username for 'https://(ドメイン名)': (htpasswdで設定したユーザー名)
Password for 'https://(ユーザー名)@(ドメイン名)': (htpasswdで設定したパスワード)
To https://(ドメイン名)/gitrepos/git-http-backend.cgi/sample1
 * [new branch]      master -> master
$

pushの途中でちゃんとBASIC認証が入り、pushできました。

→成功