【FreeBSD】 GitLabサーバーをFreeBSDのjailシステムを使って構築した話

## はじめに

ここ最近、リポジトリ管理しているプロジェクトが増えてきたこともありつつ、プライベートリポジトリを構築するモチベーションも増えつつあります。
無料でプライベートリポジトリを作成したいのであれば、[Bitbucket](https://ja.atlassian.com/software/bitbucket/pricing?tab=cloud)の無料プランでも、`無制限`にプライベートリポジトリが作成可能ですが、どうせならリポジトリサーバーを自分でも運用したいと思い、借りたVPS(OSはFreeBSD)にGitLabサーバーを構築した時の記事を書こうと思います。

## OS

借りているVPSにFreeBSDをインストールしています。

```bash
% uname -a
FreeBSD ns1.jabug.jp 11.1-RELEASE-p1 FreeBSD 11.1-RELEASE-p1 #2 r313908+91baad1bb58b(releng/11.1): Tue Oct 17 00:34:17 JST 2017     root@ns1.jabug.jp:/usr/obj/usr/src/sys/MYKERNEL  amd64
```

## jail

`jail`とは、OSレベル仮想化機構実装の一つであり、ホストマシンとは独立した小さなシステムでFreeBSDを動かすことができる機能です。LinuxではDockerが有名になってしまいましたが、jailは古くからFreeBSDで実現されています。今回はホストマシンではなく、その中で小さなFreeBSDマシンを構築して、その上でGitLabサーバーを構築しようと思います。

### qjailによるjailシステムの構築

jailはFreeBSDの標準機能ですので、最新のFreeBSDマシンであれば `jail` コマンドであらかたの設定が行えます。しかし、jailシステムが多くなるにつれてどうしても管理が煩雑化してしまう可能性があるため、今回は `qjail` という jailマネージャーをインストールし、その上でjailを作成・管理しようと思います。

```bash
% sudo pkg install qjail
& sudo qjail install
% sudo qjail create -n lo0 -4 127.0.0.1 git
Successfully created  git
```

### VIMAGEによるjailネットワークの設定

jailでFreeBSDのシステムを小さく分けられるのですが、これは作成したjailシステムをホストマシンや他のjailから独立させているだけです。そのため、ネットワークスタックも分割している訳ではありません。
jail専用に独自にネットワークスタックを作成したいのであれば、VIMAGEによって他のシステムからは独立したネットワークを作成することができます。

```bash
% sudo ifconfig epair create
```

FreeBSDでは、epairというインタフェースを作成することができます。これはその名前の通りペアとなっているインタフェースであり、上記のコマンドで `epair0a`, `epair0b` の二つのインタフェースを作成することができます。

このペアのインタフェースは相互に通信することが可能です。この内、一つをホストマシン、もう一方をjailシステムに紐付けさせることにより、ホストマシンとjailシステムの相互通信が可能となります。しかし、単にホストマシンと紐づけるだけでは、やはり管理が煩雑化するかと思われます。
ここでは、仮想インタフェースと同じく仮想スイッチも一緒に作成することにします。

```bash
% sudo ifconfig bridge create
```

以上の設定をホストマシンの起動スクリプト、およびjailシステムの設定に紐付けさせる様にします。

```bash:/etc/rc.conf
qjail_enable="YES"
cloned_interfaces="bridge0 epair0"
ifconfig_epair0a="up"
ifconfig_bridge0="up"
ifconfig_bridge0="addm epair0a addm vtnet0"
ifconfig_bridge0_alias0="inet 192.168.0.1/24"
pf_enable="YES"
pf_flags=""
pf_rules="/etc/pf.conf"
pflog_logfile="/var/log/pflog"
pflog_enable="YES"
gateway_enable="YES"
```

qjailで各jailシステムの起動スクリプトを設定するには、 `qjail.config/` 配下にあるjail名のものが設定ファイルとなります。


```bash:/usr/local/etc/qjail.config/git

git { 
host.hostname       =  "git";
path                =  "/usr/jails/git";
mount.fstab         =  "/usr/local/etc/qjail.fstab/git";
exec.consolelog     =  "/var/log/qjail.www.console.log";
mount.devfs;
devfs_ruleset       =  "4";
vnet                =  "new";
vnet.interface      =  "epair0b";
exec.start   = "ifconfig epair0b 192.168.0.2 netmask 255.255.255.0";
exec.start  += "route add default 192.168.0.1";
exec.start  += "/bin/echo "epair0b" > /etc/epair";
exec.start  += "/bin/sh /etc/rc";
exec.stop    = "/bin/sh /etc/rc.shutdown";
}

% sudo qjail restart git
```

このあたりは非常に面倒な設定かと思われますので、分かりにくい場合は後日コメント・執筆します。
以上で、jailシステムが構築されます。
なお、qjail経由でjailシステムにログインするには `qjail console` を使います。

```bash
% sudo qjail console git
```

## GitLabのインストール準備作業

では、これよりGitLabをjailシステムにインストールしてみましょう。インストール手順ですが、[GitLabのバージョン毎にインストール手順書が用意されている](https://gitlab.fechner.net/mfechner/Gitlab-docu/blob/master/install/11.1-freebsd.md)ため、これに沿った形で必要な設定を行なっていこうと思います。
以降は、jailシステムにログインした後に操作するコマンド群です。

### UTF-8の設定

まずはじめに、jailシステムのロケールをUTF-8に変更します。

```bash
$ vi /etc/login.conf
default:\
...
:charset=UTF-8:\
:lang=en_US.UTF-8:
```

### GitLabのインストール

pkgより、GitLabnのバイナリパッケージを入手することができます。
今回はGitLabの最新版を手に入れたいため、カスタムリポジトリからGitLabをインストールするようにします。

```bash
% mkdir -p /usr/local/etc/pkg/repos
% vi /usr/local/etc/pkg/repos/FreeBSD.conf

# add the following to FreeBSD.conf:

FreeBSD: {
  url: "pkg+http://pkg.FreeBSD.org/${ABI}/latest"
}

% pkg update
% pkg upgrade
% pkg install www/gitlab-ce
% sysrc gitlab_enable=YES
```

### PostgreSQLのインストール

次にデータベースの初期化を行います。デフォルトのjailシステムでは System V IPC(以下、sysvpic)リソースの操作ができないようになっているため、DBの初期化の際に共有メモリを操作するシステムコールが呼び出せません。

```bash
% pkg install postgresql95-server postgresql95-contrib
% sysrc postgresql_enable=YES
```

```bash
service postgresql initdb
creating configuration files ... ok
running bootstrap script ... 2018-08-19 11:07:15.669 JST [80355] FATAL:  could not create shared memory segment: Function not implemented
2018-08-19 11:07:15.669 JST [80355] DETAIL:  Failed system call was shmget(key=1, size=48, 03600).
child process exited with exit code 1
```

そのため、先ほどのqjail.configにsysvipcのリソースを操作できるように許可します。

```bash:/usr/local/etc/qjail.config/git
allow.sysvipc       =  "1";

% sudo qjail restart git
```

以上でDBの初期化が行えます。

```bash
% service postgresql initdb
creating directory /var/db/postgres/data10 ... ok
creating subdirectories ... ok
selecting default max_connections ... 100
selecting default shared_buffers ... 128MB
selecting dynamic shared memory implementation ... posix
creating configuration files ... ok
running bootstrap script ... ok
performing post-bootstrap initialization ... ok
syncing data to disk ... ok

WARNING: enabling "trust" authentication for local connections
You can change this by editing pg_hba.conf or using the option -A, or
--auth-local and --auth-host, the next time you run initdb.

Success. You can now start the database server using:

    /usr/local/bin/pg_ctl -D /usr/local/pgsql/data -l logfile start

% service postgresql start
2018-08-19 11:10:33.594 JST [80852] LOG:  listening on IPv6 address "::1", port 5432
2018-08-19 11:10:33.594 JST [80852] LOG:  listening on IPv4 address "127.0.0.1", port 5432
2018-08-19 11:10:33.598 JST [80852] LOG:  listening on Unix socket "/tmp/.s.PGSQL.5432"
2018-08-19 11:10:33.612 JST [80852] LOG:  ending log output to stderr
2018-08-19 11:10:33.612 JST [80852] HINT:  Future log output will go to log destination "syslog".
```

DBの設定(PostgreSQL < 9.6 の場合)

```bash
% psql -d template1 -U pgsql -c "CREATE DATABASE gitlabhq_production OWNER git;"
% psql -d template1 -U pgsql -c "CREATE DATABASE gitlabhq_production OWNER git;"
% psql -U git -d gitlabhq_production
% psql -U pgsql -d gitlabhq_production -c "CREATE EXTENSION IF NOT EXISTS pg_trgm;"
```

### Redisの設定

続いてRedisの設定を行います。

```bash
% echo 'unixsocket /var/run/redis/redis.sock' >> /usr/local/etc/redis.conf
% echo 'unixsocketperm 770' >> /usr/local/etc/redis.conf
% sysrc redis_enable=YES
% service redis restart
% pw groupmod redis -m git
```


## GitLabの設定


### Gitユーザのホームディレクトリの変更

パッケージでインストールしたGitLab経由で作成されたgitユーザーのホームディレクトリを変更します。

```bash
vipw -d /etc
git:*:211:211::0:0:gitosis user:/usr/local/git:/bin/sh
git:*:211:211::0:0:gitosis user:/usr/home/git:/bin/sh
```

次にgitlab.ymlを変更します。

```bash
% cd /usr/local/www/gitlab-ce
```

```bash:config/gitlab.yml
gitlab:
  host: git.example.com
```

```bash
% sysctl hw.ncpu
% su -l git -c "git config --global core.autocrlf input"
% su -l git -c "git config --global gc.auto 0"
% su -l git -c "git config --global repack.writeBitmaps true"
% su -l git -c "git config --global receive.advertisePushOptions true"
% su -l git -c "mkdir -p /usr/home/git/.ssh"
```

```bash
% chown git /usr/local/share/gitlab-shell
% su -l git -c "cd /usr/local/www/gitlab-ce && rake gitlab:setup RAILS_ENV=production"
% chown root /usr/local/share/gitlab-shell
```

GitLab rootユーザーのパスワード設定

```bash
% su -l git -c "cd /usr/local/www/gitlab-ce && rake gitlab:setup RAILS_ENV=production GITLAB_ROOT_PASSWORD=yourpassword"
```

### Applicationステータスのチェック

```bash
% su -l git -c "cd /usr/local/www/gitlab-ce && rake gitlab:env:info RAILS_ENV=production"
% su -l git -c "cd /usr/local/www/gitlab-ce && rake gettext:compile RAILS_ENV=production"
% su -l git -c "cd /usr/local/www/gitlab-ce && rake yarn:install gitlab:assets:clean gitlab:assets:compile RAILS_ENV=production NODE_ENV=production"
```

### Assets Compile

```bash
% su -l git -c "cd /usr/local/www/gitlab-ce && rake yarn:install gitlab:assets:clean gitlab:assets:compile RAILS_ENV=production NODE_ENV=production"
```

### PostgreSQLのスーパーユーザーを消す

```bash
% psql -d template1 -U pgsql -c "ALTER USER git WITH NOSUPERUSER;"
```

### GitLabの起動

```bash
% service gitlab start
```

```bash
% cd /home/git
% su - git
% cd gitlab/
% cp config/gitlab.yml.example config/gitlab.yml
```

### Nginxのインストール

jailシステムから直接unicornにアクセスするのも面倒なので、nginx経由でGitlabにアクセスすることができるようにNginxの設定を行います。
Gitlabをインストールした時にNginxのサンプル設定も一緒に入っているので、httpの設定に設定ファイルを読み込めるようにします。


```bash
% pkg install nginx
```

```bash:/usr/local/etc/nginx/nginx.conf
http {
  include       /usr/local/www/gitlab-ce/lib/support/nginx/gitlab;
}
```

サンプルファイルはserver_nameがまだ明示的に設定されていないため、ここにアクセスする際の名前を指定します。

```bash:/usr/local/www/gitlab-ce/lib/support/nginx/gitlab
## Normal HTTP host
server {
  ## Either remove "default_server" from the listen line below,
  ## or delete the /etc/nginx/sites-enabled/default file. This will cause gitlab
  ## to be served if you visit any address that your server responds to, eg.
  ## the ip address of the server (http://x.x.x.x/)n 0.0.0.0:80 default_server;
  listen 0.0.0.0:80 default_server;
  listen [::]:80 default_server;
  server_name git.example.com; ## Replace this with something like gitlab.example.com
```

以上で、ホストマシンからNginx経由でGitLabサーバーにアクセスすることが可能となりました。

```bash
% sysrc nginx_enable="YES"
% service nginx restart
```

## ホストシステムのリバースプロキシとNAT設定

最後にサーバ外からGitLabへアクセスがあった場合、ホストマシンへアクセスするのではなく、jailシステムにアクセスする必要があるため、`Nginx` のリバースプロキシ機能と `PF` というファイアウォールのNAT機能を設定し、外からアクセスできるようにします。

```bash:/usr/local/etc/nginx/vhosts/gitlab.conf

server {
	listen 80;
	listen [::]:80;
	server_name git.example.com;
	
	# Redirect all HTTP requests to HTTPS with a 301 Moved Permanently response.
    	#return 301 https://$host$request_uri;
	location / {
                    proxy_redirect     off;
                    proxy_set_header   X-FORWARDED_PROTO http;
                    proxy_set_header   Host              $http_host;
                    proxy_set_header   X-Real-IP         $remote_addr;
                    proxy_set_header   X-Forwarded-For   $proxy_add_x_forwarded_for;
                    proxy_pass http://192.168.0.2:80;
	}
}

```

```bash:/etc/pf.conf
wan_if="vtnet0"
jail_subnet = "192.168.0.0/24"
nat on $wan_if inet from $jail_subnet to ! $jail_subnet -> ($wan_if)

pass proto udp to any port domain keep state
pass inet proto icmp from any to any
```

```bash
% sudo service nginx restart
% sudo service pf restart
```

これでやっとVPS外からのネットワークからでもGitLabサーバーへアクセスすることが可能となりました。

![スクリーンショット 2018-08-19 14.11.54.png](https://qiita-image-store.s3.amazonaws.com/0/144991/7c2412da-c46d-d7f1-9cdd-0eec80782b5b.png)

## 最後に

長くなりましたが、FreeBSDでGitLabサーバーを構築する話でした。
今回は、かなり必要な設定の説明を省いてしまっています。
本来は jail、 VIMAGE、 PF、 リバースプロキシ、 GitLabと最低5つの説明を詳細にしなければならなかったのですが、それはまた今度にします。
また、GitLabのHTTPS化についても今回省いてしまったので、その話も後日執筆したいと思います。
それにしても、pkgでGitLabがインストールできるようになっているので、そのことだけ考えても、随分と構築手順が綺麗にまとめられたのではないかと思われます。
その分、選択肢が色々あり、かなりたくさんのツールを使っている感は否めないですが、、、

## その他参考文献

1.GitLab運用のノウハウについて

* [https://techracho.bpsinc.jp/morimorihoge/2018_06_04/57628](https://techracho.bpsinc.jp/morimorihoge/2018_06_04/57628)

2.jailについて

* [https://www.freebsd.org/cgi/man.cgi?query=jail&sektion=&n=1](https://www.freebsd.org/cgi/man.cgi?query=jail&sektion=&n=1)

3.qjailについて

* [https://www.freebsd.org/cgi/man.cgi?query=qjail&sektion=8&apropos=0&manpath=FreeBSD+9.0-RELEASE+and+Ports](https://www.freebsd.org/cgi/man.cgi?query=qjail&sektion=8&apropos=0&manpath=FreeBSD+9.0-RELEASE+and+Ports)

4.vnetについて

* [https://www.freebsd.org/cgi/man.cgi?query=vnet&sektion=9](https://www.freebsd.org/cgi/man.cgi?query=vnet&sektion=9)

5.epairについて

* [https://www.freebsd.org/cgi/man.cgi?query=epair&sektion=4&manpath=FreeBSD+8.0-RELEASE](https://www.freebsd.org/cgi/man.cgi?query=epair&sektion=4&manpath=FreeBSD+8.0-RELEASE)

6.bridgeについて

* [https://www.freebsd.org/cgi/man.cgi?bridge(4)](https://www.freebsd.org/cgi/man.cgi?bridge(4))