Tech Notes

Docker上のGiteaでデプロイ環境を作ったメモ

色々めんどかったので忘れないうちに。

環境はCentOS7。HTTPサーバはH2O。目指す状況はこう。

テスト環境と本番環境の二つに分けるのが普通だろうが、今回は練習なのでこのようにした。

Dockerイメージを使用したGiteaの立ち上げは主にここを参考にした。

https://qiita.com/k8uwall/items/63c6424a976012122d18

GitのSSHのポートだが、この記事に倣って10022番ポートということにする。5桁台なら大方どこでもいいと思う。

ローカル開発環境からはこのポートに接続してpush/pullなどしたいので、このポートはちゃんと接続できるようサーバ側の設定で開放してやらないといけない。

firewall-cmd --add-port=10022/tcp --permanent

firewalld用のサービスを定義して––add-serviceで追加したい派の方はそのようにすること。

それから、Giteaのサーバーのドメイン設定には気を付けた方が良い。自分はGiteaをリバースプロキシを使って外部に公開する仕様にして、セットアップ時に何も考えずに進めた結果、Giteaのリポジトリのページにあるクローン用URLが「ssh://git@localhost:10022/....」というような表示になってしまった。当然ローカル開発環境でこのURLをクローンしようとしてもホスト部がlocalhostなのでローカル開発環境の10022番ポートを叩くだけになる。セットアップでやらかした場合はGiteaのインストールディレクトリのgitea-data/gitea/conf/app.iniを頑張っていじればどうにかできる。

[server]
SSH_DOMAIN       = example.com
ROOT_URL         = http://example.com/
SSH_PORT         = 10022
DOMAIN           = example.com

この辺の項目をいじればいい。

リポジトリの作成やSSH鍵の登録とかはGithubとかと同じようなもんなので特段の説明する部分もない。今回はtest_gitというリポジトリ名にする。

git cloneでサーバ上に本番環境として動かすためのローカルリポジトリを作る。パスは/var/www/test_git/repoとした。

cd /var/www/test_git
git clone ssh://git@example.com:10022/user1/test_git.git repo

デプロイ環境を作るにあたって、リモートリポジトリへのpushがあったらこのローカルリポジトリに自動でpullされるようにしたい。そこでWebHookという機能を使う。

リポジトリの設定からWebHook→WebHookを追加→Giteaを選ぶとURLの入力欄が出てくる。Pushされるなどリモートリポジトリに動きがあった場合に、指定のURLへとPOSTを飛ばしてくれるらしい。ということで「このURLを踏めばスクリプトが起動してgit pullをやってくれるよ」みたいな状況を整備すればいい。

/var/www/test_git/hookというディレクトリを作成してその下にwebhook.phpというファイルを置く。中身はこう。

<?php
exec("cd ../repo && git pull origin master");
?>

repoに移動してpullするだけ。GiteaからのWebHookのPOSTには色々な情報が含まれているらしいので工夫すればもうちょっと面白いこともできるのかもしれない。

このPHPファイルをHTTP POSTで叩いて起動できるようにする。URLはhttp://example.com:10080/test_git/webhook.phpということにする。別にHTTPで叩ければ本当にどこでもいいのだが、普通のWebページ用の80番ポートとは分ける意味で10080番ポートを使用した。それから開発するプロジェクトが複数に増えた場合を考えて、URLにリポジトリ名を入れた。

H2Oの場合ならこう(PHPのFastCGIの設定とかは省略)

hosts:
  "localhost:10080":
    listen:
      port: 10080
    paths:
      "/test_git":
        file.dir: /var/www/test_git/hook

firewalldの設定も忘れずに。

firewall-cmd --add-port=10080/tcp

これであとはhttp://example.com:10080/test_git/webhook.phpをWebHookに指定すればオッケーかと思うとそうではない。

このままにするとSELinuxのセキュリティによってPHPからexecで起動されたGitが弾かれてしまう。exit code 1で終わるようだ。SELinux:disabledだという意識低い系の環境ならこれでも通ると思われる。

変更する必要がある設定は3つ。

  • repo以下のファイル/ディレクトリをhttpd_sys_rw_content_tにする /var/www/test_gitに移動して以下のコマンドを実行
semanage fcontext -a -t httpd_sys_rw_content_t '/var/www/test_git/repo(/.*)?'
restorecon -R .

デフォルトだと多分httpd_sys_content_tになっていると思う。(ls -Zで確かめられる) そのタイプだとhttpd(ApacheとかnginxとかH2Oとか)によるファイル変更は許されないらしい。httpd_sys_rw_content_tならOK。

  • httpd_read_user_contentをオンにする
setsebool -P httpd_read_user_content 1

httpdはPHPを起動し、PHPはGitを起動し、GitはリモートからのpullのためにSSHを起動するわけだが、そのSSH用の鍵は.ssh下にある訳で、つまりユーザーの個人的なファイルな訳である。普通httpdが触れていいのはHTTPで見せるつもりのあるファイルとかだけだから、ユーザーの個人ファイルに触れるのはダメでしょ、という話のようだ。鍵ファイルがユーザーの個人ファイルであるのが問題なのだから、鍵をユーザーディレクトリではないどこかに置いた上でGIT_SSHとかで鍵の位置を指定するみたいな手段の方が良いのかもしれない。(参考)

  • httpd_can_network_connectをオンにする
setsebool -P httpd_can_network_connect 1

これは今一つ分かっていない。httpdは外部と通信するのもけしからんことらしい。もっとも、今回通信する相手は同じコンピュータ上のdockerコンテナなのだが。

これでSELinuxをEnforceにしても通った。

最後にもうすこしセキュリティを高める目的で、WebHook用の10080ポートを外部からアクセスできないようにしてみる。

特定IPアドレスからの通信だけ通すようにすればいいはずだが、ホストOSから見たとき、dockerコンテナ相手の通信は誰と通信しているように見えているのか?127.0.0.1ではない。dockerコンテナは172.17.なんちゃらとか172.19.なんちゃらみたいなアドレスに見える。これはdocker-compose.yml内の記述で固定することもできるので、そのようにした。

services:
  web:
    ...
    networks:
      app_net:
        ipv4_address: 172.18.12.34

networks:
  app_net:
    ipam:
      driver: default
      config:
        - subnet: 172.18.0.0/16

これでIPアドレスが固定できる。dockerのネットワークの仕組みをよく理解していないからうかつなことは言えないが、アドレスはぶつかる心配が無ければ何でも良いと思う。むしろデフォルトの172.なんちゃらみたいなのが衝突するからイヤみたいな話まである。

firewalldで先ほど指定したIPアドレスからのアクセスだけ通すように設定する。

sudo firewall-cmd --add-rich-rule="rule family="ipv4" source address="172.18.12.34" port protocol="tcp" port="10080" accept" --permanent

これでgiteaのdockerコンテナ以外にはwebhook.phpを叩けなくなる。

追記:

giteaのWebHookにはSecret Keyという機能があるのでそれも併用するとより安全だと思われる。