www.mtkn.jp

Manuscripts for my personal webpage.
git clone https://git.mtkn.jp/www.mtkn.jp
Log | Files | Refs | LICENSE

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= &gt;&gt; /etc/rc.conf.local
    145 # echo slowcgi_flags= &gt;&gt; /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 &quot;cp %s /var/www%s &amp;&amp; chown www:www /var/www%s &amp;&amp; chmod 0400 /var/www%s\n&quot;, $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 &quot;$bin&quot; | sed 's|.*|cp &amp; /var/www&amp;|' | sh -s
    234 echo &quot;$bin&quot; | sed 's|.*|chown www:www /var/www&amp;|' | sh -s
    235 echo &quot;$bin&quot; | sed 's|.*|chmod 0500 /var/www&amp;|' | sh -s
    236 
    237 nso=$(echo &quot;$bin&quot; | 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 &quot;$nso&quot; | sed 's|.*|cp &amp; /var/www&amp;|' | sh -s
    243 echo &quot;$nso&quot; | sed 's|.*|chown www:www /var/www&amp;|' | sh -s
    244 echo &quot;$nso&quot; | sed 's|.*|chmod 0400 /var/www&amp;|' | 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 &quot;/&quot; {
    261 		directory index index.html
    262 		root &quot;/htdocs/git.mtkn.jp&quot;
    263 	}
    264 	location &quot;*&quot; {
    265 		directory index log.html
    266 		root &quot;/htdocs/git.mtkn.jp&quot;
    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=&quot;/var/www/git&quot;
    288 stagit_root=&quot;/var/www/htdocs/git.mtkn.jp&quot;
    289 repo=&quot;$(basename &quot;$(pwd)&quot; | sed 's/\.git$//')&quot;
    290 src=&quot;$(pwd)&quot;
    291 stagit_dst=&quot;$stagit_root/$repo&quot;
    292 
    293 mkdir -p &quot;$stagit_dst&quot;
    294 (cd &quot;$stagit_dst&quot; &amp;&amp; stagit -l 64 &quot;$src&quot;)
    295 (cd &quot;$stagit_root&quot; &amp;&amp; stagit-index $git_root/*.git &gt; 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>