Gitのコミット単位で動的にDockerイメージをデプロイするプロキシサーバpool

Gitのコミット単位で動的にDockerイメージをデプロイするプロキシサーバpoolというソフトウェアがあります。

poolとは

poolは、WebアプリとDockerfileをGitで管理している場合に、コミットidをサブドメインとして( http://<commit-id>.pool.dev/ )poolにアクセスするだけで、そのGitレポジトリのコミット時の状態でWebアプリのDockerイメージをデプロイし、Webアプリのポートへとリバースプロキシして、Webアプリのレスポンスを返します。もちろん、コミットidをキーに複数の状態にどんどんアクセスできます。(mod_mrubyのユースケースを調査していてたまたま見つけました)。

このpoolというソフトウェアはシンプルですが、ある種Dockerの差分管理やコンテナ型の利点をうまく利用していて、この構造を利用すると効率の良いDockerのデプロイ環境が構築できるように思いました。また、DockerとVagrantの利点もうまく組み合わせていて、このpool自体のデプロイも非常に簡単になっています。この辺りの設計の良さに関しては最後にまとめます。

例えば、poolで対象にするレポジトリをmod_mrubyにしていた場合、mod_mrubyのレポジトリにはDockerfileも置いてあって、ビルド後のDockerイメージは/mruby-helloにアクセスするとレスポンスが返るようにしています。

つまり、poolのREADMEの通り、例えば http://1d4980db6f9f90a3e4954b126044db623f498917.poo.dev/mruby-hello にアクセスすると、そのGitコミットID単位でDockerfileからDockerビルドして、動的にDockerのポートをプロキシサーバであるApacheが検出してそのポートにリバースプロキシしてくれます。

このpoolで動作しているリバースプロキシはApache+mod_mrubyで実装されていて、こういう動的なポート割り当てやDockerデプロイみたいな事をしたい場合は、mod_mrubyngx_mrubyは使いやすいと思います。ngx_mrubyもほとんど同様の事を実装できそうですね。

VagrantとDockerとGitの組み合わせを活かした設計

この設計の良い所は、

  • GitとDockerの差分管理を利用
  • 一旦Dockerデプロイされるとイメージの差分をキャッシュするので別コミットIDでのデプロイが高速
  • デプロイしたDockerイメージのサービスポートとドメインを動的に紐付ける(設定変更が不要)
  • Vagrantでpool自体を様々な環境に簡単にデプロイし、デプロイされた仮想マシン内でDockerのサービスを動的かつ高速にデプロイする(仮想マシンとコンテナの良さをうまく利用)

だと思います。

この設計をうまく使うと、VagrantとDockerの良さをうまく両立したWebシステムが作れそうですね。

Rubyで設定が書けるTrusterd HTTP/2 WebサーバのDockerイメージ公開

mrubyを組み込む事によりRubyで設定を書く事ができるTrusterd HTTP/2 Web ServerDockerイメージを作成・公開しました。

Docker環境のある方は、以下のコマンドで簡単にHTTP/2を喋るWebサーバTrusterdを動かす事ができます。

[program lang=”bash” escaped=”true”]

sudo docker run -d -p 8080:8080 matsumotory/trusterd

[/program]

その後、HTTP/2クライアントであるnghttp(これはnghttp2をビルドして準備してください)コマンドで以下のようにアクセスすると、

[program lang=”bash” escaped=”true”]

$ nghttp http://127.0.0.1:8080/index.html

Hello Trusterd HTTP/2 world on Docker.

[/program]

というように、HTTP/2によるコンテンツGETができると思います。

また、Trusterdの設定を弄りたい人は、GitHubのTrusterdレポジトリをcloneしてきて

[program lang=”bash” escaped=”true”]

trusterd/docker/conf/trusterd.conf.rb

[/program]

のファイルに設定を書き、さらにコンテンツを追加する場合は、

[program lang=”bash” escaped=”true”]

trusterd/docker/htdocs/

[/program]

以下にファイルを放りこんで、それぞれ以下のようなコマンドでDockerビルドし直してください。

[program lang=”bash” escaped=”true”]

sudo docker build -t local/trusterd .

[/program]

ではでは以上のように非常に簡単にHTTP/2なWebサーバTrusterdを動かして、設定をRubyで色々書いてみたり、ベンチマークをとるなどして遊んでみてください!

共有WebホスティングでVitrualHost設定に手を入れずにシンボリックリンクのTOCTOU問題を解決するApacheモジュールを作った

共有Webホスティングを提供している業者の皆様は、シンボリックリンクのTOCTOU問題というかなりクリティカルで必ず対応すべき問題をおそらく様々な方法によって解決されていると思います。

しかし一方で、TOCTOU問題をなんとなく放置されている、あるいは、誤解されている場合もあるかもしれません。この問題は、Apacheでシンボリックリンクを使えない(シンボリックリンクにアクセスがあったら4系エラーを返す)設定にしていたとしても、シンボリックリンクファイルを作る事さえできれば、攻撃プログラムを使う事により、apache権限でアクセス可能なファイルにシンボリックリンク経由でアクセスすることが可能となります。また、Apache本体のコアに直接パッチを当てて対応されている方も多いでしょう。

その場合、

  • コアのパッチは今後Apacheのバージョンアップに伴いメンテが面倒になる
  • 幾つか公開されているモジュールは設定の変更箇所が多いので困っている
  • TOCTOU問題をあまりよくわかってない

という状況があると思います。

そこで、あまり既存の共有ホスト、例えばVirtualHostの設定等を変える事なく簡単な設定を追記するのみで共有WebホスティングのTOCTOU問題を解決するApacheモジュールmod_fileownercheckを作りました。

追記1:SetOutputFilterの設定をしなくて良いようにした

インストールは簡単で、apxsでビルドした後いつも通りモジュールをLoadModuleするだけで良いです。

モジュールをロードするだけで、シンボリックリンクのTOCTOU問題を解決できます。ホスティング業者によっては、共有ホスティングの構成が様々でこのモジュールだけでは対応出来ない場合もあると思います。ですので、一度攻撃プログラムを用いて試した上で利用するのが良いと思います。VirtualHost + suEXEC + Directoryのパーミッションのような定番の共有ホスティングの場合は、大抵このモジュールだけで解決できると思います。

仕組み的には単純で、リクエストのあったファイルがApacheによってopenされた時点で、そのopenされたファイルのownerとリクエストのあったファイルパスのownerが一致するかをチェックするものです。

追記2:.htaccessでの上書き防止処理やリクエストパスの途中にシンボリックリンクを含んでいる場合の対処を追加

さらに、.htaccessで上書きできないようモジュールで強制的にoutput filterに追加するようにしました。また、リクエストパスの途中に他のユーザのディレクトリを指すシンボリックリンクが含まれている場合も対応するために、suEXECで設定しているownerとopenしているファイルのownerチェックも行うようにしました。

もしまだApacheのバーチャルホスト等で構築した共有Webホスティングを提供しながら、このTOCTOU問題の対応をされていなかったり、コアに手を入れていたりする場合は是非このmod_fileownercheckをご活用下さい。また、問題がありそうでしたらレポジトリにissueやpull-requestをして頂けると幸いです。

Apacheのシンボリックリンク検査に関するTOCTOU問題をmod_mrubyで解決してみた

共有ホスティングにおいて、各利用ユーザに対してシンボリックリンクの使用を許可していた場合に、他のユーザ領域のコンテンツを閲覧できたりする問題があるため、SymLinksIfOnwerMatchの設定によりリンクのownerとリンク先のファイルのownerが一致していない限りはリンクを辿らないようにする対処がされてきましたが、そもそもリンクの検査とファイル作成のタイミングによっては、検査をすり抜け、閲覧を可能にするTOCTOU問題がありました。

この件に関しては、「Apache HTTPD: `Options -FollowSymLinks` は不完全」に詳しく解説されているのでそちらを見て頂くとして、リンク使用は許可しつつもそのTOCTOU問題を解決するための一つの方法として、open()したコンテンツファイルのfdから情報を検査してownerに不一致があった場合はレスポンスを返さないようにする、というアプローチがあります。

実装として、Apache本体に手を入れたり、Apacheモジュールで対応したり、といくつか方法がありますが、今日はWebホスティングのセキュリテイとmod_mrubyの実装の復習がてら、単純な静的コンテンツへのリクエストにおけるこの問題をmod_mrubyで楽に解決できるようにしてみました。

具体的には、コンテンツがopen()されているアウトプットフィルターフェイズにおいてmod_mrubyで介入し、任意のuidと、open()されているコンテンツのuidが異なっていたらエラーを返す、という処理を書きました。

まずは、Apacheの設定を以下のようにして、

[program lang=”apache” escaped=”true”]

SetOutputFilter mruby
mrubyOutputFilter /path/to/solved_link_toctou.rb

[/program]

/path/to/solved_link_toctou.rbを以下のようにします。

[program lang=”ruby” escaped=”true”]

uid = 1000

f = Apache::Filter.new

if f.uid != uid
  f.error_create Apache::HTTP_SERVICE_UNAVAILABLE
end

[/program]

このように実装することで、f.uidメソッドから既にopen()されたファイルのuid情報を取得できるので、TOCTOU問題を気にすることなくownerのチェックを行えます。上記の例の場合では、openしたコンテンツのuidが1000じゃなければ503エラーを返し、一致すれば何もせずにリクエストのあったコンテンツの内容をそのまま返します。後は幾つか細かい点があるのですが、それを書くと内容が広がりすぎるので一旦はこの程度の検査に留めておきます。

というわけで、mod_mrubyのリハビリがてら、SymLinksIfOnwerMatchTOCTOU問題をmod_mrubyで解決してみました。mod_mrubyだと非常に簡単に書けるので楽でした。

chmodやchownのreferenceオプションを知った時は目から鱗だった話

元々ホスティング会社で働いていたので、その特性上ownerやpermissionを色々と弄る事が多く、数年前の社会人時代にchmodやchownをもっと楽に使えないかなぁと調べた時に目から鱗だったのがchmodやchownのreferenceオプションでした。

今回は単にreferenceオプション楽ですよね、という記事なのでご存知の方は退屈な記事だと思いますが、まわりに聞いてみた所意外と知られていなかったりしたので、ブログエントリにしておこうと思います。

referenceオプションを使うと、任意のファイルを指定することで、変更対象のownerやpermissionを指定したファイルと同じ設定にすることができます。

例えば、/bin/pingみたいなpermissionって僕は覚えられなくて通常の実行方法ではわりと面倒だと思うのですが、それと同じowner、permissionにしたい場合は、

[program lang=”bash” escaped=”true”]

$ ls -l /bin/ping
-rwsr-xr-x 1 root root 44168 May  7 14:51 /bin/ping
$ touch myprogram
$ ls -l myprogram
-rw-rw-r--  1 matsumotory matsumotory         0 Jun 22 21:10 myprogram
$ sudo chown --reference=/bin/ping myprogram
$ sudo chmod --reference=/bin/ping myprogram
$ ls -l myprogram
-rwsr-xr-x 1 root root 0 Jun 22 21:05 myprogram

[/program]

みたいな事ができるので、大変楽になります。本来は、chmodとchownを使いまわす際に755とかmatsumotry.matsumotryとか引数を書きなおして実行する必要があったのに対し、referenceオプションを使うとchownとchmodのown文字列とmod文字列を3文字変更するだけで達成したい変更処理が実現できるのも、キーボード的にも省エネで気に入っています。

というわけで、単なるネタ記事になってしまいましたが、どうぞreferenceオプションをよろしくお願いします。