主頁 | 自己紹介 | 日記 | 農業 | 台所 | 電算機 | | 本棚 | | Git

Gitサーバーの設定 on OpenBSD

はじめに

GitHubがMicrosoft傘下になり久しい。MincraftがMicrosoftアカウントなしでは遊べなくなった。このままではGitHubもそのうちMicrosoftアカウントを要求するようになるかもしれない。ということでGitサーバーを自前で持つことにした。

ところでOpenBSDの開発者がGotという別のgit実装を作成しているので、この記事は近いうちにいらなくなりそう。

手順

以下ではssh接続によるpull/push及び、httpsによるpullを設定の上、stagitというウェブフロントエンドを導入する。

概要

  1. ドメインの設定(任意)
  2. httpd(8)の設定
  3. gitパッケージのインストールとchroot環境の整備
  4. stagitの導入

ドメインの設定(任意)

ドメインを設定する。IPアドレスでアクセスできればいいならこの設定はいらない。

今回はgitというサブドメインを登録した:

サブドメイン 種別 内容 優先度
git A サーバーのIPアドレス

httpd(8)の設定

httpsで接続する場合、acme-client(8)を設定する(IPアドレスで接続するなら自己証明書を発行することになる)。まず/etc/acme-client.confにドメインを追加する:

domain git.mtkn.jp {
	domain key "/etc/ssl/private/git.mtkn.jp.key"
	domain full chain certificate "/etc/ssl/git.mtkn.jp.fullchain.pem"
	sign with letsencrypt
}

続いて/etc/httpd.confを設定する。

server "git.mtkn.jp" {
	listen on * port 80
	location "/.well-known/acme-challenge/*" {
		root "/acme"
		request strip 2
	}
	location "*" {
		block return 302 "https://$HTTP_HOST$REQUEST_URI"
	}
}

server "git.mtkn.jp" {
	listen on * tls port 443
	tls {
		certificate "/etc/ssl/git.mtkn.jp.fullchain.pem"
		key "/etc/ssl/private/git.mtkn.jp.key"
	}
	location "/.well-known/acme-challenge/*" {
		root "/acme"
		request strip 2
	}
	location "/*/git-receive-pack" {
		block
	}
	location "/*/git-upload-pack" {
		fastcgi {
			param SCRIPT_FILENAME "/usr/local/libexec/git/git-http-backend"
			param GIT_PROJECT_ROOT "/git"
			param GIT_HTTP_EXPORT_ALL "true"
		}
	}
	location "/*/info/refs" {
		fastcgi {
			param SCRIPT_FILENAME "/usr/local/libexec/git/git-http-backend"
			param GIT_PROJECT_ROOT "/git"
			param GIT_HTTP_EXPORT_ALL "true"
		}
	}
}

location "/*/git-receive-pack"はpushの設定。今回はhttpsでのpushを受付けないのでblockする。

location "/*/git-upload-pack"は手元のパソコンからgit clonegit pullしたときのもの。location "/*/info/refs"は上記を含むアクセスがあったときのもの。ここではCGIを使ってgit-http-backendを呼んでいるだけである。SCRIPT_FILENAMEhttpd(8)のchroot環境でのパスである。必要なファイルは後でこのchroot環境にコピーする。

この後同じURLでフロントエンドをホストしたいので、上記のように必要なURLからのみCGIを実行するようにした。gitのhttpクライアントがどのURLを利用しているのかは[2]に書いていた。

httpsが必要ない場合は1つ目のserverに各locationを書くだけでいい。

httpd(8)slowcgi(8)を起動する:

# echo httpd_flags= >> /etc/rc.conf.local
# echo slowcgi_flags= >> /etc/rc.conf.local
# rcctl start httpd
# rcctl enable httpd
# rcctl start slowcgi
# rcctl enable slowcgi

サーバー証明書の発行:

# acme-client -v git.mtkn.jp

crontab(1)にサーバー証明書の自動更新を登録:

#minute hour    mday    month   wday    [flags] command
~ 2 * * * acme-client git.mtkn.jp && rcctl reload httpd

gitパッケージのインストールとchroot環境の整備

gitパッケージをインストールし、ssh接続用のユーザーを作成する。httpsでも公開するので、gitユーザーのホームディレクトリは/var/www下にする:

# pkg_add git
# useradd -b /var/www -m -s /usr/local/bin/git-shell git

ssh接続用の公開鍵を/var/www/git/.ssh/authorized_keysに登録する。ところでこのファイルに公開鍵を書き込むとgitユーザーとしてsshでログインできるので、部外者がこのファイルの編集をできないようにしておく必要がある。一応所有者はgit:git、権限は-rw-------なので大丈夫だと思うが、心配ならhttpd(8)のchroot環境の外にこのファイルを移動させておいてもいいかもしれない。

httpsでアクセスするためにchroot環境を整備する。httpd(8)は既定では/var/wwwにchrootして実行されるので、CGIに必要なファイルをこのディレクトリ以下に用意する必要がある。まずはgitのコマンドをコピー:

# mkdir -p /var/www/usr/local/libexec/git
# cp /usr/local/libexec/git/git-{http-backend,receive-pack,upload-pack} /var/www/usr/local/libexec/git/
# chown www:www /var/www/usr/local/libexec/git/git-{http-backend,receive-pack,upload-pack}
# chmod 0500 /var/www/usr/local/libexec/git/git-{http-backend,receive-pack,upload-pack}
# mkdir -p /var/www/usr/local/bin
# cp /usr/local/bin/git /var/www/usr/local/bin/
# chown www:www /var/www/usr/local/bin/git
# chmod 0500 /var/www/usr/local/bin/git

続いてコマンドの実行に必要なライブラリをコピー:

# mkdir -p /var/www/usr/lib /var/www/usr/local/lib /var/www/usr/libexec
# find /var/www/{bin,usr} -type f | grep git | xargs ldd | awk '{print $7}' | grep -v -e '^/var/www/' -e '^$' -e 'Name' | sort | uniq | awk '{printf "cp %s /var/www%s && chown www:www /var/www%s && chmod 0400 /var/www%s\n", $1, $1, $1, $1}' | sh -s

/dev/nullをコピーする(mknod(8)参照):

# mkdir /var/www/dev
# mknod -m 666 /var/www/dev/null c 2 2

最後に、/var/www/dev/nullを作成するために/etc/fstab/varエントリーからnodevオプションを削除し、再起動する。

gitパッケージやシステムの更新後、chroot環境のコマンドやライブラリも更新しないといけないのでそのためのスクリプトを適当に作った:

#!/bin/sh -xe

oso=$(find /var/www/usr -type f -name '*.so*')
rm $oso

bin=$(find /var/www/bin /var/www/usr -type f ! -name '*.so*' |
	grep -v bgpctl |
	sed 's|^/var/www||'
	)
echo "$bin" | sed 's|.*|cp & /var/www&|' | sh -s
echo "$bin" | sed 's|.*|chown www:www /var/www&|' | sh -s
echo "$bin" | sed 's|.*|chmod 0500 /var/www&|' | sh -s

nso=$(echo "$bin" | sed 's|^|/var/www|' |
	xargs ldd | awk '{print $7}' |
	grep -v -e '^/var/www/' -e '^$' -e 'Name' |
	sort | uniq
	)
echo "$nso" | sed 's|.*|cp & /var/www&|' | sh -s
echo "$nso" | sed 's|.*|chown www:www /var/www&|' | sh -s
echo "$nso" | sed 's|.*|chmod 0400 /var/www&|' | sh -s

stagit(1)の導入

ウェブフロントエンドとしてstagitを導入する:

# pkg_add stagit

httpd.conf(5)server "git.mtkn.jp"の中に以下の設定を追加する(locationのマッチは上から順番に評価されるので、上で設定したgitのhttpクライアント用のlocationよりも下に記入する):

	location "/" {
		directory index index.html
		root "/htdocs/git.mtkn.jp"
	}
	location "*" {
		directory index log.html
		root "/htdocs/git.mtkn.jp"
	}

stagit用のディレクトリを作成してhttpd(8)を再読込する:

# mkdir /var/www/htdocs/git.mtkn.jp
# chown git:git /var/www/htdocs/git.mtkn.jp
# rcctl reload httpd

gitリポジトリが更新されたときにウェブページも更新するように設定する。gitリポジトリはなにか更新があった場合、そのリポジトリのディレクトリの中のhooks/post-receiveというファイルを自動で実行する。そのためこのファイルに、stagitの更新をするスクリプトを書いておけばいい:

#!/bin/sh

git_root="/var/www/git"
stagit_root="/var/www/htdocs/git.mtkn.jp"
repo="$(basename "$(pwd)" | sed 's/\.git$//')"
src="$(pwd)"
stagit_dst="$stagit_root/$repo"

mkdir -p "$stagit_dst"
(cd "$stagit_dst" && stagit -l 64 "$src")
(cd "$stagit_root" && stagit-index $git_root/*.git > index.html)

レポジトリの作成

レポジトリを作成するにはサーバーで以下のようにする。

$ cd /var/www/git
$ doas -u git mkdir repo.git
$ cd repo.git
$ doas -u git git init --bare

これで手元のパソコンからクローンできる:

$ git clone git@git.mtkn.jp:repo.git

または

$ git clone https://git.mtkn.jp/repo.git

参考