git_server.html (10655B)
1 <h1>Gitサーバーの設定 on OpenBSD</h1> 2 <time>2024-02-15</time> 3 4 <h2>はじめに</h2> 5 <p> 6 GitHubがMicrosoft傘下になり久しい。\ 7 MincraftがMicrosoftアカウントなしでは遊べなくなった。\ 8 このままではGitHubもそのうちMicrosoftアカウントを要求するように\ 9 なるかもしれない。\ 10 ということでGitサーバーを自前で持つことにした。\ 11 </p> 12 <p> 13 ところでOpenBSDの開発者がGotという別のgit実装を作成しているので、\ 14 この記事は近いうちにいらなくなりそう。 15 </p> 16 17 <h2>手順</h2> 18 <p> 19 以下ではssh接続によるpull/push及び、httpsによるpullを設定の上、\ 20 stagitというウェブフロントエンドを導入する。 21 </p> 22 23 <h3>概要</h3> 24 <ol> 25 <li>ドメインの設定(任意)</li> 26 <li><code>httpd(8)</code>の設定</li> 27 <li>gitパッケージのインストールとchroot環境の整備</li> 28 <li>stagitの導入</li> 29 </ol> 30 31 <h3>ドメインの設定(任意)</h3> 32 <p> 33 ドメインを設定する。\ 34 IPアドレスでアクセスできればいいならこの設定はいらない。\ 35 </p> 36 <p> 37 今回はgitというサブドメインを登録した: 38 </p> 39 <table> 40 <thead> 41 <tr> 42 <th>サブドメイン</th> 43 <th>種別</th> 44 <th>内容</th> 45 <th>優先度</th> 46 </tr> 47 </thead> 48 <tbody> 49 <tr> 50 <td>git</td> 51 <td>A</td> 52 <td><i>サーバーのIPアドレス</i></td> 53 <td></td> 54 </tr> 55 </tbody> 56 </table> 57 58 <h3><code>httpd(8)</code>の設定</h3> 59 <p> 60 httpsで接続する場合、<code>acme-client(8)</code>を設定する\ 61 (IPアドレスで接続するなら自己証明書を発行することになる)。\ 62 まず<code>/etc/acme-client.conf</code>にドメインを追加する: 63 </p> 64 <pre><code>\ 65 domain git.mtkn.jp { 66 domain key "/etc/ssl/private/git.mtkn.jp.key" 67 domain full chain certificate "/etc/ssl/git.mtkn.jp.fullchain.pem" 68 sign with letsencrypt 69 } 70 </code></pre> 71 72 <p> 73 続いて<code>/etc/httpd.conf</code>を設定する。\ 74 </p> 75 <pre><code>\ 76 server "git.mtkn.jp" { 77 listen on * port 80 78 location "/.well-known/acme-challenge/*" { 79 root "/acme" 80 request strip 2 81 } 82 location "*" { 83 block return 302 "https://$HTTP_HOST$REQUEST_URI" 84 } 85 } 86 87 server "git.mtkn.jp" { 88 listen on * tls port 443 89 tls { 90 certificate "/etc/ssl/git.mtkn.jp.fullchain.pem" 91 key "/etc/ssl/private/git.mtkn.jp.key" 92 } 93 location "/.well-known/acme-challenge/*" { 94 root "/acme" 95 request strip 2 96 } 97 location "/*/git-receive-pack" { 98 block 99 } 100 location "/*/git-upload-pack" { 101 fastcgi { 102 param SCRIPT_FILENAME "/usr/local/libexec/git/git-http-backend" 103 param GIT_PROJECT_ROOT "/git" 104 param GIT_HTTP_EXPORT_ALL "true" 105 } 106 } 107 location "/*/info/refs" { 108 fastcgi { 109 param SCRIPT_FILENAME "/usr/local/libexec/git/git-http-backend" 110 param GIT_PROJECT_ROOT "/git" 111 param GIT_HTTP_EXPORT_ALL "true" 112 } 113 } 114 } 115 </code></pre> 116 <p> 117 <code>location "/*/git-receive-pack"</code>はpushの設定。\ 118 今回はhttpsでのpushを受付けないので<code>block</code>する。\ 119 </p> 120 <p> 121 <code>location "/*/git-upload-pack"</code>は手元のパソコンから\ 122 <code>git clone</code>や<code>git pull</code>したときのもの。\ 123 <code>location "/*/info/refs"</code>は上記を含むアクセスがあったときのもの。\ 124 ここではCGIを使って<code>git-http-backend</code>を呼んでいるだけである。\ 125 <code>SCRIPT_FILENAME</code>は<code>httpd(8)</code>のchroot環境でのパスである。\ 126 必要なファイルは後でこのchroot環境にコピーする。 127 </p> 128 <p> 129 この後同じURLでフロントエンドをホストしたいので、上記のように必要なURLからのみ\ 130 CGIを実行するようにした。\ 131 gitのhttpクライアントがどのURLを利用しているのかは[2]に書いていた。\ 132 </p> 133 134 135 136 <p> 137 httpsが必要ない場合は1つ目の<code>server</code>に各<code>location</code>を書くだけ\ 138 でいい。 139 </p> 140 <p> 141 <code>httpd(8)</code>と<code>slowcgi(8)</code>を起動する: 142 </p> 143 <pre><code>\ 144 # echo httpd_flags= >> /etc/rc.conf.local 145 # echo slowcgi_flags= >> /etc/rc.conf.local 146 # rcctl start httpd 147 # rcctl enable httpd 148 # rcctl start slowcgi 149 # rcctl enable slowcgi 150 </code></pre> 151 <p> 152 サーバー証明書の発行: 153 </p> 154 <pre><code>\ 155 # acme-client -v git.mtkn.jp 156 </code></pre> 157 <p> 158 <code>crontab(1)</code>にサーバー証明書の自動更新を登録: 159 </p> 160 <pre><code>\ 161 #minute hour mday month wday [flags] command 162 ~ 2 * * * acme-client git.mtkn.jp && rcctl reload httpd 163 </code></pre> 164 165 <h3>gitパッケージのインストールとchroot環境の整備</h3> 166 <p> 167 gitパッケージをインストールし、ssh接続用のユーザーを作成する。\ 168 httpsでも公開するので、gitユーザーのホームディレクトリは<code>/var/www</code>\ 169 下にする: 170 </p> 171 <pre><code>\ 172 # pkg_add git 173 # useradd -b /var/www -m -s /usr/local/bin/git-shell git 174 </code></pre> 175 <p> 176 ssh接続用の公開鍵を<code>/var/www/git/.ssh/authorized_keys</code>に登録する。\ 177 ところでこのファイルに公開鍵を書き込むとgitユーザーとしてsshでログインできるので、\ 178 部外者がこのファイルの編集をできないようにしておく必要がある。\ 179 一応所有者は<code>git:git</code>、権限は<code>-rw-------</code>なので大丈夫\ 180 だと思うが、心配なら<code>httpd(8)</code>のchroot環境の外にこのファイルを移動させて\ 181 おいてもいいかもしれない。\ 182 </p> 183 <p> 184 httpsでアクセスするためにchroot環境を整備する。\ 185 <code>httpd(8)</code>は既定では<code>/var/www</code>にchrootして実行されるので、\ 186 CGIに必要なファイルをこのディレクトリ以下に用意する必要がある。\ 187 まずはgitのコマンドをコピー: 188 </p> 189 <pre><code>\ 190 # mkdir -p /var/www/usr/local/libexec/git 191 # cp /usr/local/libexec/git/git-{http-backend,receive-pack,upload-pack} /var/www/usr/local/libexec/git/ 192 # chown www:www /var/www/usr/local/libexec/git/git-{http-backend,receive-pack,upload-pack} 193 # chmod 0500 /var/www/usr/local/libexec/git/git-{http-backend,receive-pack,upload-pack} 194 # mkdir -p /var/www/usr/local/bin 195 # cp /usr/local/bin/git /var/www/usr/local/bin/ 196 # chown www:www /var/www/usr/local/bin/git 197 # chmod 0500 /var/www/usr/local/bin/git 198 </code></pre> 199 <p> 200 続いてコマンドの実行に必要なライブラリをコピー: 201 </p> 202 <pre><code>\ 203 # mkdir -p /var/www/usr/lib /var/www/usr/local/lib /var/www/usr/libexec 204 # 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 205 </code></pre> 206 <p> 207 <code>/dev/null</code>をコピーする(<code>mknod(8)</code>参照): 208 </p> 209 <pre><code>\ 210 # mkdir /var/www/dev 211 # mknod -m 666 /var/www/dev/null c 2 2 212 </code></pre> 213 <p> 214 最後に、<code>/var/www/dev/null</code>を作成するために\ 215 <code>/etc/fstab</code>の<code>/var</code>エントリーから\ 216 <code>nodev</code>オプションを削除し、再起動する。 217 </p> 218 219 <p> 220 gitパッケージやシステムの更新後、chroot環境のコマンドやライブラリも\ 221 更新しないといけないのでそのためのスクリプトを適当に作った: 222 </p> 223 <pre><code>\ 224 #!/bin/sh -xe 225 226 oso=$(find /var/www/usr -type f -name '*.so*') 227 rm $oso 228 229 bin=$(find /var/www/bin /var/www/usr -type f ! -name '*.so*' | 230 grep -v bgpctl | 231 sed 's|^/var/www||' 232 ) 233 echo "$bin" | sed 's|.*|cp & /var/www&|' | sh -s 234 echo "$bin" | sed 's|.*|chown www:www /var/www&|' | sh -s 235 echo "$bin" | sed 's|.*|chmod 0500 /var/www&|' | sh -s 236 237 nso=$(echo "$bin" | sed 's|^|/var/www|' | 238 xargs ldd | awk '{print $7}' | 239 grep -v -e '^/var/www/' -e '^$' -e 'Name' | 240 sort | uniq 241 ) 242 echo "$nso" | sed 's|.*|cp & /var/www&|' | sh -s 243 echo "$nso" | sed 's|.*|chown www:www /var/www&|' | sh -s 244 echo "$nso" | sed 's|.*|chmod 0400 /var/www&|' | sh -s 245 </code></pre> 246 247 <h3><code>stagit(1)</code>の導入</h3> 248 <p> 249 ウェブフロントエンドとしてstagitを導入する: 250 </p> 251 <pre><code>\ 252 # pkg_add stagit 253 </code></pre> 254 <p> 255 <code>httpd.conf(5)</code>の<code>server "git.mtkn.jp"</code>の中に\ 256 以下の設定を追加する\ 257 (<code>location</code>のマッチは上から順番に評価されるので、上で設定した\ 258 gitのhttpクライアント用の<code>location</code>よりも下に記入する): 259 </p> 260 <pre><code> location "/" { 261 directory index index.html 262 root "/htdocs/git.mtkn.jp" 263 } 264 location "*" { 265 directory index log.html 266 root "/htdocs/git.mtkn.jp" 267 } 268 </code></pre> 269 <p> 270 stagit用のディレクトリを作成して<code>httpd(8)</code>を再読込する: 271 </p> 272 <pre><code>\ 273 # mkdir /var/www/htdocs/git.mtkn.jp 274 # chown git:git /var/www/htdocs/git.mtkn.jp 275 # rcctl reload httpd 276 </code></pre> 277 278 <p> 279 gitリポジトリが更新されたときにウェブページも更新するように設定する。\ 280 gitリポジトリはなにか更新があった場合、そのリポジトリのディレクトリの中の\ 281 <code>hooks/post-receive</code>というファイルを自動で実行する。\ 282 そのためこのファイルに、stagitの更新をするスクリプトを書いておけばいい: 283 </p> 284 <pre><code>\ 285 #!/bin/sh 286 287 git_root="/var/www/git" 288 stagit_root="/var/www/htdocs/git.mtkn.jp" 289 repo="$(basename "$(pwd)" | sed 's/\.git$//')" 290 src="$(pwd)" 291 stagit_dst="$stagit_root/$repo" 292 293 mkdir -p "$stagit_dst" 294 (cd "$stagit_dst" && stagit -l 64 "$src") 295 (cd "$stagit_root" && stagit-index $git_root/*.git > index.html) 296 </code></pre> 297 298 <h2>レポジトリの作成</h2> 299 <p> 300 レポジトリを作成するにはサーバーで以下のようにする。\ 301 </p> 302 <pre><code>\ 303 $ cd /var/www/git 304 $ doas -u git mkdir <i>repo</i>.git 305 $ cd <i>repo</i>.git 306 $ doas -u git git init --bare 307 </code></pre> 308 309 <p> 310 これで手元のパソコンからクローンできる: 311 </p> 312 <pre><code>\ 313 $ git clone git@git.mtkn.jp:<i>repo</i>.git 314 </code></pre> 315 <p> 316 または 317 </p> 318 <pre><code>\ 319 $ git clone https://git.mtkn.jp/<i>repo</i>.git 320 </code></pre> 321 322 <h2>参考</h2> 323 <ul> 324 <li>[1] <a href="https://git-scm.com/book/en/v2/Git-on-the-Server-The-Protocols">Git - The Protocols.Git</a></li> 325 <li>[2] <a href="https://git-scm.com/docs/http-protocol">Git - http-protocol Documentation.Git</a></li> 326 <li>[3] <a href="https://codemadness.org/stagit.html">Stagit: a static git page generator - Codemadness</a></li> 327 </ul>