Showing posts with label memo. Show all posts
Showing posts with label memo. Show all posts

Wednesday, March 11, 2020

2020年にDSRロードバランス環境を作る方法

TCPの時代もそうだったんですが、QUICにおいても、Linux等でソフトウェアロードバランサをipvsadmとDSR (direct server return, a.k.a. direct routing) を実現することは有効な手段です。

なんだけど、ipvsadm界隈はHA前提の込み入った設定か古いドキュメントしか見つからなかったので、最低限の検証環境のセットアップ方法を、ここにまとめる次第です。

ロードバランサ:
echo 1 | sudo tee /proc/sys/net/ipv4/ip_forward
sudo ifconfig eth0:0 $VIP netmask 255.255.255.255 broadcast $VIP up
sudo route add -host $VIP dev eth0:0
sudo ipvsadm -A -t $VIP:443 -s rr
sudo ipvsadm -a -t $VIP:443 -r $SERVER1 -g
sudo ipvsadm -a -t $VIP:443 -r $SERVER2 -g
sudo ipvsadm -a -t $VIP:443 -r $SERVER3 -g
...

要は、フォワーディングを有効にして、自分自身へのパケットを他のサーバにフォワードするような変態行為をするからspoofing対策フィルタをオフにして、次にipvsadmでテーブル作ってサーバ追加する。

サーバ:
echo 1 | sudo tee /proc/sys/net/ipv4/ip_forward
echo 0 | sudo tee /proc/sys/net/ipv4/conf/eth0/rp_filter 
echo 2 | sudo tee /proc/sys/net/ipv4/conf/eth0/arp_announce
echo 1 | sudo tee /proc/sys/net/ipv4/conf/eth0/arp_ignore
sudo ifconfig lo:0 $VIP netmask 255.255.255.255 broadcast $VIP up
sudo route add -host $VIP dev lo:0

要は、フォワーディング有効にして、VIPがARPでアナウンスされたり、応答したりしないように設定して、loにVIPをつけて、eth0で受信したパケットがlo:0に転送されるように設定する。

これで動く。

Thursday, June 14, 2018

Git で全ブランチから検索

ググったけどmacOSでぱっと動くのがなかったのでメモがてら書く。

全ローカルブランチから検索
% git grep keyword $(git branch | colrm 1 2)

リモート含む全ブランチから検索
% git grep keyword $(git branch -a | colrm 1 2)

Thursday, December 28, 2017

CI 用 Docker イメージ作成におけるベストプラクティス

H2O の CI では長らく、秘伝のタレ的な .travis.yml を使い続けてきたのですが、なにぶん依存関係が多いもので、だいぶメンテナンスが辛い感じになってきていました。また、CI テストで発生したエラーの調査の度に、時間のかかる CI を回さなければならないことが、開発者のストレスとなっていました。

そこで一念発起して、Docker イメージを使った CI に切り替えることにしました(実行環境としては引き続き Travis を使います)。

その際に、要件として以下のようなことを考えました。
  • CI以外に、コミット前のテストにも使えるようなイメージにすること
  • コマンド一発でビルドとテストが動作すること
  • 無駄な処理をしないこと

その結果、以下のような実装になりました。
  • テストに必要なソフトウェア群をインストールしたイメージを Docker Hub にアップロードしておく
  • テストには、1. テスト用コンテナにコードのあるディレクトリをマウントし、2. アウトオブトリービルド注1とテストを実行するスクリプトを書く
  • CI環境では、そのスクリプトを実行する
  • 開発者がローカルでそのスクリプトを実行してテストしてもよい

Dockerfile および CI 実行用のスクリプトは、H2O のレポジトリにある以下のファイルです。

結果、Docker 導入前に90行あった .travis.yml は23行にまで短くなり、また、CI に必要な時間も2割ほど減少しました(CI を実行するたびに、必要なソフトウェアをビルドする手間が不要になったため)。

また、開発者が各個人の環境において make -f misc/docker-ci/check.mkmake を実行するだけで、いつでも(コミット前でも)CI 環境と同様のテストが実行できるようになりました。テストが失敗したら、コンテナを立ち上げっぱなしにして、ローカルでコード修正&特定のテスト再実行を繰り返すことも可能なので、バグ修正が捗ります。

まとめ:CI 用の Docker イメージを作るのではなく、テスト用の CI イメージを作り、それを CI に展開すべき

言わずもがなかもしれませんが、備忘録としてまとめておきます。


注1: ソースディレクトリ以外の場所に生成物を配置するビルド手法のこと。アウトオブトリービルドができない場合はaufsを使っても良いのかもしれない

Wednesday, February 10, 2016

Name Constraints を使った独自CAの運用手順

ウェブブラウザが新機能をHTTPSでしか有効にしないことが多くなってきたので、開発環境でもHTTPSを使いたい。でも、開発環境用にサーバ証明書を買うのは手間。Let's Encryptも運用がめんどくさいとか、社内からしかアクセスできないサーバへの証明書発行が難しいとかいろいろあるし…ってそこでName Constraintsを使った独自CAですよ奥さん。

Name Constraints が何であるかについては、以前オレオレ認証局の適切な運用とName Constraintsに書いたとおり。

本稿では、Name Constraintsを使うCAの運用手順を説明する。

1. CA鍵と証明書の作成

1.1. CAの秘密鍵を作成
% openssl genrsa -out ca.key 2048

1.2. openssl.cnfにCA証明書に設定する属性を指定するセクションを追記
[private_ca]
subjectKeyIdentifier=hash
authorityKeyIdentifier=keyid:always,issuer
basicConstraints=critical,CA:true
nameConstraints=critical,permitted;DNS:.example.com
注:nameConstraintsの項がCAが証明書を発行できる対象のドメインを指定。この例では.example.com。

1.3. CA証明書の作成
% openssl req -new -x509 -days 3650 -key ca.key -out ca.crt \
  -extensions private_ca

2. 証明書管理用ディレクトリを作成
% mkdir -p demoCA/newcerts
% touch demoCA/index.txt
% echo 00 > demoCA/serial
注:ディレクトリ名(demoCA)はopenssl.cnfの設定で変更可能。

3. 証明書発行要求を受け取って証明書を発行
% openssl ca -keyfile ca.key -cert ca.crt \
  -extensions usr_cert -policy policy_anything -days 365 \
  -md sha256 -in foo.example.com.csr -out foo.example.com.crt

以上です。

難点としては、SafariおよびOSXで動作するChromeではこのルート証明書で署名されたサーバ証明書を正当なものとして認識してくれないところ(手動でサーバ証明書を登録すれば使えるはず)。他のウェブブラウザ+モダンなOSなら問題ないはず。

Tuesday, October 6, 2015

[メモ] OS XのホストからVMにnfsでファイル共有

普段OS X上で作業しつつ、開発ディレクトリをOS X上のVMで動いているLinuxやFreeBSDからもアクセスできるようにしてあると、互換性検証がはかどる。

どう設定するか、備忘録をかねてメモ。

1. ゲスト側で通常使用するアカウントのuser-id,group-idを、OS Xのそれに揃える

2. OS Xの/etc/exportsと/etc/nfs.confを以下のように、TCP経由でVMの仮想ネットワークにだけファイルを公開するよう設定
/etc/exports:
/shared-dir -mapall=user:group -network netaddr -mask netmask

/etc/nfs.conf:
nfs.server.udp=0
nfs.server.tcp=1
3. ゲスト側の/etc/fstabにマウント情報を設定
192.168.140.1:/shared-dir /shared-dir nfs rw,noatime 1 0
これで、ホストでも、どのVMでも、/shared-dirの中身が同じになる。

注. VMware Fusion 10の場合は、環境設定から、新しいネットワークを作成し、「NATを使用する」を外し、そのネットワークを使う必要がある。そうしない限り、ホストに見えるTCPのソースアドレスがローカルネットワークのものにならない。

できるだけ単純に構成しようとすると、こんな感じになるかな、と。設定に問題があるようだったら教えてください。

Thursday, May 21, 2015

How to properly spawn an external command in C (or not use posix_spawn)

When spawning an external command, as a programmer, you would definitely want to determine if you have succeeded in doing so.

Unfortunately, posix_spawn (and posix_spawnp) does not provide such a feature. To be accurate, there is no guaranteed way to synchronously determine if the function has succeeded in spawning the command synchronously.

In case of Linux, the function returns zero (i.e. success) even if the external command does not exist.

The document suggests that if the function succeeded in spawning the command should be determined asynchronously by checking the exit status of waitpid. But such approach (that waits for the termination of the sub-process) cannot be used if your intension is to spawn a external command that is going to run continuously.

Recently I have faced the issue while working on H2O, and have come up with a solution; a function that spawns an external command that synchronously returns an error if it failed to do so.

What follows is the core logic I implemented. It is fairly simple; it uses the traditional approach of spawning an external command: fork and execvp. And at the same time uses a pipe with FD_CLOEXEC flag set to detect the success of execvp (the pipe gets closed), which is also used for returning errno in case the syscall fails.

pid_t safe_spawnp(const char *cmd, char **argv)
{
    int pipefds[2] = {-1, -1}, errnum;
    pid_t pid;
    ssize_t rret;

    /* create pipe, used for sending error codes */
    if (pipe2(pipefds, O_CLOEXEC) != 0)
        goto Error;

    /* fork */
    if ((pid = fork()) == -1)
        goto Error;

    if (pid == 0) {
        /* in child process */
        execvp(cmd, argv);
        errnum = errno;
        write(pipefds[1], &errnum, sizeof(errnum));
        _exit(127);
    }

    /* parent process */
    close(pipefds[1]);
    pipefds[1] = -1;
    errnum = 0;
    while ((rret = read(pipefds[0], &errnum, sizeof(errnum))) == -1
           && errno == EINTR)
        ;
    if (rret != 0) {
        /* spawn failed */
        while (waitpid(pid, NULL, 0) != pid)
            ;
        pid = -1;
        errno = errnum;
        goto Error;
    }

    /* spawn succeeded */
    close(pipefds[0]);
    return pid;

Error:
    errnum = errno;
    if (pipefds[0] != -1)
        close(pipefds[0]);
    if (pipefds[1] != -1)
        close(pipefds[1]);
    errno = errnum;
    return -1;
}

The actual implementation used in H2O does more; it has a feature to remap the file descriptors so that the caller can communicate with the spawned command via pipes. You can find the implementation here.

I am not sure if this kind of workaround is also needed for other languages, but I am afraid it might be the case.

Anyways I wrote this blogpost as a memo for myself and hopefully others. Happy hacking!

Wednesday, November 5, 2014

[memo] Installing nghttp2 onto OSX

Following the steps below should install nghttp2 with all of its useful apps for testing into /usr/local/http2-15 of your OS X machine.

-- openssl
% wget --no-check-certificate https://www.openssl.org/source/openssl-1.0.2-beta3.tar.gz
% tar xzf openssl-1.0.2-beta3.tar.gz
% cd openssl-1.0.2-beta3
% KERNEL_BITS=64 ./config shared enable-ec_nistp_64_gcc_128 --prefix=/usr/local/http2-15
% make
% make install

-- libevent
% wget https://github.com/downloads/libevent/libevent/libevent-2.0.21-stable.tar.gz
% CFLAGS=-I/usr/local/http2-15/include CXXFLAGS=-I/usr/local/http2-15/include LDFLAGS=-L/usr/local/http2-15/lib ./configure --prefix=/usr/local/http2-15
% make
% make install

-- zlib-1.2.8
% wget http://zlib.net/zlib-1.2.8.tar.gz
% tar xzf zlib-1.2.8.tar.gz
% cd zlib-1.2.8
% ./configure --prefix=/usr/local/http2-15
% make
% make install

--  spdylay
% git clone https://github.com/tatsuhiro-t/spdylay.git
% cd spdylay
% (cd m4 && wget http://keithr.org/eric/src/whoisserver-nightly/m4/am_path_xml2.m4)
% autoreconf -i
% PKG_CONFIG_PATH=/usr/local/http2-15/lib/pkgconfig ./configure --prefix=/usr/local/http-15
% make
% make install

-- nghttp2
% git clone https://github.com/tatsuhiro-t/nghttp2.git
% cd nghttp2
% (cd m4 && wget http://keithr.org/eric/src/whoisserver-nightly/m4/am_path_xml2.m4)
% autoreconf -i
% PKG_CONFIG_PATH=/usr/local/http2-15/lib/pkgconfig ./configure --prefix=/usr/local/http2-15 --enable-app --disable-threads
% make
% make install

note: I have gathered this from my command history. Please forgive me if any of them are wrong.
EDIT: added libevent as pointed out by @tatsuhiro-t.

Friday, October 10, 2014

[メモ] root権限でrsyncする方法

サーバの移転作業時など、rootしかアクセスできない設定ファイルやアクセス権を保ったままrsyncしたいことってありませんか?

そういった際には、sudo の -A オプションと rsync の --rsync-path オプションを使うと良いようです。

まず、リモートサーバに、パスワードを標準出力に出力するスクリプトファイルを配置します(ファイルのパーミッションを厳しくするのを忘れずに)。
% cat > bin/askpass
#! /bin/sh
echo "{{my_password}}"
% chmod 700 bin/askpass
% 
そして、rsync を実行する際には --rsync-path オプションを使い、リモートサーバの rsync を sudo -A 経由で起動するようにすれば良いのです。
% sudo rsync -avz -e ssh \
    --rsync-path='SUDO_ASKPASS=/home/remote-user/bin/askpass sudo -A rsync' \
    remote-user@remote-host:remote-dir local-dir

これは簡単!

sudo -Aオプションはパスワードを記述したファイルを設置する必要があるという点でNOPASSWD同様セキュリティの懸念はありますが、使いどころを誤らなければ便利だなと思いました。

Tuesday, May 20, 2014

[メモ] Apache+mod_sslでSIGBUSが発生した件

@hirose31さんと、Apache HTTPDからHTTPSでファイルダウンロード中にサーバプロセスがSIGBUSで死ぬって件にぶちあたり、

「OpenSSLの中でmemcpyがSIGBUSしてます」「な、なんだってー!」

って調べたのですが、理由は以下のとおりだった。

  • HTTPSの場合、デフォルト設定だとファイル読込にmmap(2)が使われる
    • mmapされたファイルのサイズが変更されてもApacheはそれを検知しようがない
    • そして、ファイル末尾以降のデータを読もうとするとセグメンテーションエラー(SIGBUS)が発生し、Apacheのサーバプロセスは異常終了する
  • HTTPの場合は、ローカルファイルシステムの場合sendfile(2)が使われるので、ファイルサイズが変更になってもApacheは異常終了しない
    • ただし、mod_deflateのような出力フィルタを使っている場合は、HTTPS同様に異常終了する可能性がある注1

この動作はApacheの「仕様」のようです注1日本語版だとNFS特有の現象のように読めるけど、英語版は以下のように書かれています。
Deleting or truncating a file while httpd has it memory-mapped can cause httpd to crash with a segmentation fault.
EnableMMAP Directive - core - Apache HTTP Server
言われてみれば当然だけど、mmapやsendfile等を自動的に使い分けているサーバの場合、これが原因だとは気づきにくいですよね。

というわけで、Apacheを使う場合はEnableMMAP Offにしておきましょう注1、さもないとアラートが上がってきたりしてウザいですよという話でした。本当は配信ファイルを全てアトミックに更新できばいいのですが、運用的にできないこともありますからね。

Apache以外のサーバでも同様の問題があるかどうかは知りません。

注1: HTTPSに限らない問題なので加筆修正 (17:06)。参照: Bug 46688 – Child segfault when mmaped file truncated

Tuesday, April 22, 2014

[perl][memo] File::Tempのバッドノウハウ

■まとめ
  • tempfile(...)が作成したテンポラリファイルは、環境によってはflockされていることがある
  • tempfile(CLEANUP => 1)は、テンポラリファイルへの参照をretainする
  • つまり、CLEANUPを指定している場合、参照カウントに頼った自動closeは機能しないので、明示的にcloseする必要がある
  • また、明示的にcloseしないとflock可能にならない場合がある

■ログ
16:23:30 <kazuho_> あれ perl って file handle への refcnt がゼロになったら自動的に close してくれますよね
16:23:43 <tokuhirom> してくれますね
16:23:48 <tokuhirom> しなきゃおかしいw
16:32:33 <kazuho_> https://gist.github.com/kazuho/11168660
16:32:37 <kazuho_> こういうことだった
16:32:53 <tokuhirom> あー。それな。
16:33:01 <tokuhirom> なんか File::Temp さんごちゃごちゃやってんすよね
16:42:37 <kazuho_> linux で perl -MFile::Temp=tempfile -e '(undef, my $fn) = tempfile(UNLINK => 1); sleep 100'
16:42:47 <kazuho_> ってやっても、テンポラリファイルが開きっぱになるなー
16:49:41 <kazuho_> _deferred_unlink って関数が $fh にぎにぎしちゃうのかー > File::Temp
16:50:50 <tokuhirom> UNLINK => 1 するとなかなか UNLINK されなくなるの、だいぶアレゲですねw
16:51:16 <kazuho_> というより、
16:51:22 <kazuho_> > # close the filehandle without checking its state
16:51:23 <kazuho_> > # in order to make real sure that this is closed
16:51:30 <kazuho_> という理由で $fh をにぎりっぱにしてるから
16:51:38 <kazuho_> refcnt 減らしても自動でcloseされない
16:52:13 <tokuhirom> なんか UNLINK => 0 してやり過ごすってのを昔見た気がした
16:52:27 <kazuho_> そしたら自動削除してくれないじゃんw
16:52:33 <kazuho_> 明示的にcloseするわ…
16:53:03 <kazuho_> Starlet で select を、テンポラリファイルの flock で囲おうとしてるんだけど
16:53:15 <kazuho_> osx だと tempfile が EXLOCK 指定する
16:53:23 <kazuho_> → UNLINK してるとファイル開いたままになる
16:53:28 <kazuho_> → ロックできない!!!
16:53:34 <kazuho_> という問題なので
16:54:01 <tokuhirom> 明示的にクローズが正解かー
16:54:07 <kazuho_> ですね
16:54:09 <kazuho_> まあ、UNLINK => 1 してるとファイル開きっぱになるの、バグだと思うけどなー
16:54:15 <kazuho_> file descriptorたりなくなるじゃん!!
16:54:52 <kazuho_> まあそういう場合は tempdir してその中に手動でファイル作ろうね、なんだろうな
16:54:53 <tokuhirom> なんかでもそこ今更変えられなさそうw
16:54:57 <kazuho_> 変えようがないでしょうね
16:54:58 <tokuhirom> a-
16:55:40 <kazuho_> あざますあざます