11月 242021
 

前の記事で紹介したパラレルESI(pesi)など、様々なVMODがあるのですが、残念なことにパッケージで提供されているVMODはほとんどありません。

自分がメンテしているカスタムでカウンタを作るもの(xcounter)とかは、勉強がてらGithub actionsでdebを作るようにしてみたのですが、なかなかめんどくさいのが実情でこれは提供したがらないよなぁと思ってました。

なんなら公式のvarnish-modulesも公式ではパッケージ提供されていません。

では、VMODを使いたい場合はどうしてたのかというと、自分でパッケージを作るか.so配布するかとか、利用者・作者共ににも辛い状態だったわけです。

誰かが作るんじゃないかなと思ってたんですが、だれも作らなかったのと急に思い立ったのでえいやでdebとかrpmが作れるツールを作りました。

https://github.com/xcir/vmod-packager

使いかたは簡単で、docker, curl, jqが使える環境で

xcir@build01:~/git/vmod-packager$ cd src
xcir@build01:~/git/vmod-packager/src$ git clone https://github.com/varnish/varnish-modules.git
xcir@build01:~/git/vmod-packager/src$ cd ..
xcir@build01:~/git/vmod-packager$ ./vmod-packager.sh -e 0.19 varnish-modules
...
##################################################
        docker image: vmod-packager/focal:7.0.0-1
                Dist: focal
     Varnish Version: 7.0.0
         Varnish VRT: 140
           VMOD name: varnish-modules
        VMOD Version: 140.0.19
              Status: SUCCESS

xcir@build01:~/git/vmod-packager$ ls pkgs/debs/varnish-modules/
varnish-modules_140.0.19~focal-1_amd64.build      varnish-modules_140.0.19~focal-1_amd64.changes  varnish-modules-dbgsym_140.0.19~focal-1_amd64.ddeb
varnish-modules_140.0.19~focal-1_amd64.buildinfo  varnish-modules_140.0.19~focal-1_amd64.deb

こんな感じでパッケージを作れます。

もちろんrpmも

xcir@build01:~/git/vmod-packager$ ./vmod-packager.sh -d centos8 -e 0.19 varnish-modules
...
##################################################
        docker image: vmod-packager/centos8:7.0.0-1
                Dist: centos8
     Varnish Version: 7.0.0
         Varnish VRT: 140
           VMOD name: varnish-modules
        VMOD Version: 140.0.19
              Status: SUCCESS

xcir@build01:~/git/vmod-packager$ ls pkgs/rpms/varnish-modules/
varnish-modules-140.0.19-1.el8.src.rpm  varnish-modules-140.0.19-1.el8.x86_64.rpm

作れます。

もちろん、このように簡単にビルドできるのは

  • ./autogen.sh(もしくは./bootstrap) + ./configure + makeで作れるもの
  • 依存パッケージがないもの

上記を満たす必要がありますが、追加パッケージが必要だったり、ちょっと特殊なビルドが必要なものでも
カスタムスクリプトでパッケージにすることが可能です。

実際に先の記事で紹介したlibvdp-pesiをパッケージにしてみましょう。

xcir@build01:~/git/vmod-packager$ cd src/
xcir@build01:~/git/vmod-packager/src$ git clone https://gitlab.com/uplex/varnish/libvdp-pesi.git

xcir@build01:~/git/vmod-packager/src$ cd libvdp-pesi/
xcir@build01:~/git/vmod-packager/src/libvdp-pesi$ git checkout -b 7.0 remotes/origin/7.0
xcir@build01:~/git/vmod-packager/src$ cat libvdp-pesi_init.sh 
#!/bin/sh

cp -rp ${VMP_ROOT_DIR}/src/m4 ${VMP_WORK_DIR}/src/m4

xcir@build01:~/git/vmod-packager/src$ cat libvdp-pesi_config.sh 
#!/bin/sh

./autogen.sh
./configure VARNISHSRC=/tmp/varnish/src

xcir@build01:~/git/vmod-packager$ ./vmod-packager.sh -f  src/libvdp-pesi
...

##################################################
        docker image: vmod-packager/focal:7.0.0-1
                Dist: focal
     Varnish Version: 7.0.0
         Varnish VRT: 140
           VMOD name: libvdp-pesi
        VMOD Version: 140.0.1
   Enable fixed mode
              Status: SUCCESS

xcir@build01:~/git/vmod-packager$ dpkg --info pkgs/debs/libvdp-pesi/libvdp-pesi_140.0.1~focal-1_amd64.deb 
 new Debian package, version 2.0.
 size 56760 bytes: control archive=816 bytes.
     309 bytes,    11 lines      control              
     711 bytes,    10 lines      md5sums              
 Package: libvdp-pesi
 Version: 140.0.1~focal-1
 Architecture: amd64
 Maintainer: libvdp-pesi <example@localhost>
 Installed-Size: 141
 Depends: libc6 (>= 2.14), varnish (= 7.0.0)
 Replaces: libvdp-pesi (<< 140)
 Section: web
 Priority: optional
 Homepage: https://github.com/xcir/
 Description: packed by vmod-packager

ビルド時に事前準備が必要な場合は[VMODの名前]_init.shにその内容を書きます。
今回はm4をコピーしていますが、ここでビルドに必要なパッケージをインストールすることも可能です。

この例では使っていませんが、dependsに追加が必要な場合は[VMODの名前]_env.shという別のファイルに入れる形になります。

また、libvdp-pesiはconfigureでvarnishのソースパスを指定する必要があるので [VMODの名前]_config.shで記述できるようにしています。

ちなみに-fオプションというのを使っていますが、これはlibvdp-pesiがVarnishのバージョンが厳密に一致する必要があるためでバージョンを固定するオプションになります。

こんな感じで割と複雑な手順が必要なVMODでも簡単にパッケージ化が可能です。

とはいえ、細かい指定はまぁいろいろできるんですが、自分は今のところsrc以下に使ってるvmodをcloneした後に

for i in `find src/  -mindepth 1 -maxdepth 1 -type d` ; do echo $i;cd $i ;git pull; cd ..;cd .. ; done

./vmod-packager.sh -v 7.0.1 -t -e `date +%Y%m%d` `find src/  -mindepth 1 -maxdepth 1 -type d`

find pkgs/debs/ -type f -name *.deb|grep `date +%Y%m%d`| xargs -i cp -p {} [コピー先]

みたいな感じで、更新した後に一気にバージョンを日付でビルドしてあとはよしなにするといった感じです。

何はともあれ、使いかたはREADMEを見てほしいです。
今のところよく使われてそうなものはビルドを試しており、大体いけるんじゃないかなと考えています。
が、もしビルド出来ないVMODや気になる点があったら指摘してもらえると嬉しかったりします。

なお、このツールでパッケージにしたものは外部に配布することを想定していません。

これはcopyrightとかdescriptionとかまでカスタマイズできるするのが面倒だったというのがあるので、そのうちなんとかするかもしれないです。
が、現時点では自身の環境にインストールするパッケージを作るのに使うぐらいが良いでしょう。


9月 252021
 

VarnishのEnterprise版で利用できるパラレルESIがcommunity版でも使えるものをUPLEXが公開してくれました。

ヘビーにESIを利用する人はそこまでいない気もするのですが、これがまたなかなか便利なので紹介します。

ビルド方法

このvmodはVarnishの内部関数に深く依存しているため、ビルド時にはVarnishのコードが必要でビルドに癖があります。

また、動作時にはVarnishとビルドにつかったVarnishのコミットハッシュ値が一致している必要があります。

なお以下の環境で確認しています

OSUbuntu 20.04 LTS
Varnish7.0.0 (454733b82a3279a1603516b4f0a07f8bad4bcd55)
vmod_pesimaster (52cd44e3d8944825d1d84a01c70002a509048c74)

pesiは最新のコードを使えばよいかなと思いますが、もしビルドができない場合はもしブランチで7.0があればそちらを(現時点ではないですが)、もしくはコミットログを眺めて適度に巻き戻るとよいでしょう。

とりあえずVarnishとpesiのコードをダウンロードします。

ubuntu@proxy:~/tmp$ ll
total 16
drwxrwxr-x  4 ubuntu ubuntu 4096 Sep 25 21:38 ./
drwxr-xr-x 12 ubuntu ubuntu 4096 Sep 25 21:37 ../
drwxrwxr-x  3 ubuntu ubuntu 4096 Sep 20 23:33 libvdp-pesi-master/
drwxrwxr-x 15 ubuntu ubuntu 4096 Sep 24 02:18 varnish-cache-master/

一旦こんな感じのパスで説明します。

事前準備

まずはVarnishをautogen.sh / configure / makeしておきます。

ubuntu@proxy:~/tmp$ cd varnish-cache-master/
ubuntu@proxy:~/tmp/varnish-cache-master$ ./autogen.sh
ubuntu@proxy:~/tmp/varnish-cache-master$ ./configure
ubuntu@proxy:~/tmp/varnish-cache-master$ make

なお、make installは不要です。

次にpesiの直下にvarnishのソースツリーにあるm4フォルダをコピーします。

ubuntu@proxy:~/tmp$ cd libvdp-pesi-master/
ubuntu@proxy:~/tmp/libvdp-pesi-master$ cp -rp ../varnish-cache-master/m4 ./

configure.acにあるVarnishのバージョン指定をtrunkからインストールしているVarnishのバージョンに変更します。(今回は7.0.0)

ubuntu@proxy:~/tmp/libvdp-pesi-master$ diff configure.ac ../org/configure.ac
56c56
< VARNISH_PREREQ([7.0.0])
---
> VARNISH_PREREQ([trunk])

次にsrc/Makefile.amに1行追加します。(こちらは今PR投げてるのでもしかしたら直るかもしれないです)

ubuntu@proxy:~/tmp/libvdp-pesi-master$ diff src/Makefile.am ../org/src/Makefile.am
72,73d71
< vmod_pesi_debug.lo: vcc_pesi_debug_if.h
<

とりあえずここまででビルド準備ができました。

ビルド/インストール

ここで注意がいるのはVarnishのソースの場所をフルパスで指定することです。

ubuntu@proxy:~/tmp/libvdp-pesi-master$ ./autogen.sh
ubuntu@proxy:~/tmp/libvdp-pesi-master$ ./configure VARNISHSRC=/home/ubuntu/tmp/varnish-cache-master

ubuntu@proxy:~/tmp/libvdp-pesi-master$ make
ubuntu@proxy:~/tmp/libvdp-pesi-master$ make check
ubuntu@proxy:~/tmp/libvdp-pesi-master$ sudo make install

これでインストール完了しました。

使ってみる

使いかたは簡単で

import pesi;

sub vcl_backend_response {
  set beresp.do_esi=true;
}

sub vcl_deliver{
  pesi.activate();
}

これだけです。(細かい使いかたは README に)

とりあえずこの状態で通常のESIとパラレルESIを比較してみましょう。

$ cat esi1.html
<html>
<body>
        <esi:include src="./time.php?t=1&s=1"/>
        <esi:include src="./time.php?t=2&s=2"/>
        <esi:include src="./time.php?t=3&s=3"/>

</body>
</html>


$ cat time.php
<?php
$s=(int)$_GET['s'];
if($s >10)$s=10;
if($s==0) $s=1;
echo $_GET['t'];
echo "&nbsp;";
echo "start:".date(DateTime::ISO8601);
echo "&nbsp;";
sleep($s);
echo "end:".date(DateTime::ISO8601);
echo "<br>\r\n";

コードはこんな感じで1,2,3秒スリープするPHPをincludeします。

通常のESI

シーケンシャルに処理しているため1+2+3で6秒かかっています。

パラレルESI

パラレルで処理しているため、ベースページ(esi1.html)が読み込まれた後に子ESIのfetchが一気に走っています。

ちなみにESIをネストしても結果は期待通りです。

ESIネスト

<html>
<body>
        <esi:include src="./time.php?t=C%3Cesi%3Ainclude+src%3D%22.%2Ftime.php%3Ft%3D1%26s%3D1%22%2F%3EC%3Cesi%3Ainclude+src%3D%22.%2Ftime.php%3Ft%3D1%26s%3D1%22%2F%3E&s=1"/>
        <esi:include src="./time.php?t=C%3Cesi%3Ainclude+src%3D%22.%2Ftime.php%3Ft%3D2%26s%3D2%22%2F%3EC%3Cesi%3Ainclude+src%3D%22.%2Ftime.php%3Ft%3D2%26s%3D2%22%2F%3E&s=2"/>
        <esi:include src="./time.php?t=C%3Cesi%3Ainclude+src%3D%22.%2Ftime.php%3Ft%3D3%26s%3D3%22%2F%3EC%3Cesi%3Ainclude+src%3D%22.%2Ftime.php%3Ft%3D3%26s%3D3%22%2F%3E&s=3"/>
</body>
</html>

urlエンコードしてる部分はこんな感じで。

C<esi:include src="./time.php?t=1&s=1"/>C<esi:include src="./time.php?t=1&s=1"/>
C<esi:include src="./time.php?t=2&s=2"/>C<esi:include src="./time.php?t=2&s=2"/>
C<esi:include src="./time.php?t=3&s=3"/>C<esi:include src="./time.php?t=3&s=3"/>

要は子ESIでさらに2つincludeしています。イメージは以下の通りです。

+ sleep=1
|+ sleep=1
|+ sleep=1
+ sleep=2
|+ sleep=2
|+ sleep=2
+ sleep=3
|+ sleep=3
|+ sleep=3

通常ESI

パラレルESI

子ESIにおいてもパラレルで動いていることがわかります。

使うときの注意点

きわめて魅力的なパラレルESIですが、注意点があります。

ESIをネストしていて、パラレルESIを有効にした場合は子要素もパラレルESIを有効(pesi.activate)する必要があります。

パラレルESIを有効にしたリクエストにおいて子要素で通常のESIを行うとpanicを起こしてVarnishがrestartします。

ここら辺のことはREADMEに書いてますのでしっかり読んでおいてしっかり検証したほうがいいでしょう。

あ、あとVarnish自体のアップデート記事はそろっと書きます(本書いててサボってたけどもう書き終わってしまったので)


9月 042019
 

Varnish6.0.4/6.2.1が公開されました。
今回の更新はセキュリティ上の問題の修正(VSV00003/CVE-2019-15892)です。
Varnishに存在するヘッダのパース処理の問題により細工されたHTTPのリクエスト(keep-alive)によりassertを引き起こさせ結果としてVarnishを再起動させることが可能です。

影響を受けるバージョンについてと修正

  • 6.1.0~6.1.1および6.2.0
    • 6.2.1で修正
  • 6.0.0~6.0.3
    • 6.0.4で修正

なお6.0.0未満のバージョン(以前のLTSだった4.1も含む)についてもパース処理に同様の問題はあるもののassertがなかったため影響を受けません。

VCLによる緩和策について

すぐにアップデートできない環境向けにVCLでの緩和が可能で公式で紹介されています。
実際のコードは公式を参照してください。

単純な緩和策

今回の攻撃の成立条件としてkeepaliveが必須なので、単純な方法としてはクライアントにレスポンスする際にConnection: Closeを返却する方法がありますが、当然パフォーマンスにインパクトがあります。

複雑な緩和策

なるだけパフォーマンスに影響を与えない形で緩和するためのVCLも提供されています。
ただしこれはインラインCを使っている関係でサポートしているバージョンが

  • 6.2.0
  • 6.1.1
  • 6.0.3

と限られています。

その他変更について(6.0.4)

6.2.1の変更は今回のセキュリティ修正のみですが、6.0.4についてはもともと積んでいて6.0ブランチにコミットされていたバグ修正や改善も同時に入っています。(かなり少ないですが)
その中で一部を紹介します

std.ipのデフォルトポートを指定可能に

std.ipは文字列をIPアドレスに変換するメソッドですが、もともとポート番号は80固定となっていましたが、pというパラメータが追加されて指定可能となりました。
デフォルト値は今まで通り80なのは変更ありませんので今まで使っていても新規に指定が必要というわけではありません。

そのほか

また、今回の更新については以前紹介したVML購入者向けのVIVU-listに事前に通知されました。そう高くもないですし使っている人は検討してもよいかと思います。
あともともとの次のリリース予定日が9/15だったんですが、どうなるんだろ・・


4月 092018
 

Varnish6.0.0がリリースされました。[changelog] [公式のリリース文] [パッケージDL] [ソースDL]
今回のリリースはメジャーバージョンアップということで機能追加や累積バグFixなど来ています。
目玉機能はUnixDomainSocket(UDS)対応で、個人的に注目しているのがshard directorにおけるlazyモード追加です。
また新しいVCLバージョン(4.1)も追加されていますが、既存が使えなくなったわけではないので影響は少ないでしょう。
なお、アップデートする場合はいくつか注意が必要な点があります。

現状で適用する場合は十分に注意検討する必要がある環境

http/2環境
h2利用時にスレッドがリークしているような動きをしています。(#2623)
最初は自分のテスト用環境だけかなと考えていたんですが、他に踏んでる人を確認したので現状のままでh2を有効にするのはかなり注意が必要です。
h2を使いたいのであれば、きちんと検証して自環境で起きないことを確認しつつ行うか、既存バージョンのままで使いつつFixを待つと良いと思います。
ただしh1のみの場合は現状問題ないため、h2を使わないのであればバージョンアップをしても良いかと思います。

公式パッケージの対象バージョンが少なくなった
公式で提供してるパッケージの対象OSは以下の通りです
– RHEL7(CentOS7)
– Debian9
– Ubuntu16.04
Ubuntu14.04やRHEL6は対象外となりましたので注意が必要です。
ただ、これはあくまで対象バージョンを絞ったというだけなので、自前でpkg-varnish-cacheを利用してパッケージを作って適用は可能です。

VMODを利用している場合
VMOD側で対応しないといけない変更が入っているため、いろんなVMODを利用している場合は対応状況を確認したほうが良いです。
必要な変更はたいしたことないので、メンテされてるものについては対応されるんじゃないかなとか思います。

なお、VSMに直接つなぐアプリについては5.2.0のときのVSM/VUTインタフェース変更に対応できていればほぼ動くと思います。
一部定数と戻り値の型が変わったものがあるためそこを使っていて運が悪いと踏むかもしれません。


VCL

vclを記述する際には、先頭に「vcl 4.0;」と記述していたかと思いますが、今回「vcl 4.1;」が追加されました。
既存の4.0が使えなくなったわけではないため、そのまま動かすことも可能ですし、大抵の場合は先頭行だけ4.1に書き換えても(大抵の場合は)動くでしょう。
あとで紹介するUDSは4.1のみでサポートしている記法が必要なので、もし使う場合は4.1に変更しましょう。
またincludeですが親のVCLバージョンより小さい場合は可能です。
つまり

parent vcl ver child vcl ver result
4.0 4.0 OK
4.0 4.1 NG
4.1 4.0 OK
4.1 4.1 OK

こんな感じです。
VCLの記載の仕方は環境それぞれだとおもうのでなんともいえないのですが、
default.vclを起点として、includeしていくやり方であればdefault.vclを一旦4.1にしておくと良いのかなと思います。
とはいえ、4.0から4.1へのアップデートはそんなに難しくないのでやってしまったほうがいいんじゃないかなとは思います。

VCL変更点

4.0での変更点と4.1での変更点があります。
まずは共通の変更点から

変更点(4.0/4.1共通)

return(fetch)がvcl_hitで使えなくなりました
個人的にはまだ残っていたのかといった感じですが(結構前からdeplicatedになってた)
return(miss)を使いましょう

req.storage / hash_ignore_busy / hash_always_missをclientスレッド側でアクセスできるようになりました。

obj.storageが追加され、vcl_hit/vcl_deliverでアクセスできるようになりました

restart時の動作が変わりました
req.restarts,xid以外のすべてのreq変数は維持されます。
restartを利用している場合は動作の確認をしたほうが良いでしょう。

vcl_recvでもreturn(restart)が呼べるようになりました

変更点(4.1のみ)

backendの定義に.pathが追加
あとで触れるUDSで使うパスです。
.hostと同時に定義することは出来ません。

local.socketの追加
接続に利用したソケットの名前を返します。
-afoo=:81と名前が指定されてる場合はfooを返しますが、もし定義されていない場合は自動的につけられた名前(a0,a1)が返却されます。

local.endpointの追加
接続に利用したソケットの定義を返します。
-afoo=:81の場合は:81が返却されます

sess.xidの追加
セッションのユニークなID(xid)を返します。
今まで使用していたreq.xid/bereq.xidはそのリクエスト、バックエンドリクエストを示すものでした。
しかしKeepaliveやHTTP/2では1セッション中に複数のリクエストが含まれます。


*   << Session  >> 433160262
-   Begin          sess 0 PROXY
-   SessOpen       ***
-   Proxy          ***
-   Link           req 423002616 rxreq
-   Link           req 423002617 rxreq
-   Link           req 423002618 rxreq
-   Link           req 423002619 rxreq
-   Link           req 423002620 rxreq
-   ReqAcct        72 22 94 1251 6 1257
-   End

これはとあるHTTP/2.0でのログの抜粋ですが、これが示しているのは
xid:433160262のセッション(sess.xid)にxid:423002616~423002620のリクエスト(req.xid)がぶら下がっているということです。
今回sess.xidがvcl中から簡単に取れることで、異なるリクエスト中でも同一セッションであることがわかるので、なにかに使えるかもしれないです(ぱっと思いつかないですが・・)

resp.do_esiの追加
beresp.do_esiの指定でフェッチ時にパースした場合でも無効にすることができます。
vcl_deliver/vcl_synthで呼び出し可能です。

(req|bereq|beresp).protoがreadonly
そんなに変更することもなかったと思いますが.protoがreadonlyになりました。

req.esiが削除された
まぁ使う人はそんなにいないかと・・・beresp.do_esiを使いましょう

beresp.storage_hintが削除されました
beresp.storageで代用できます。
例えば今まで


set beresp.storage_hint = "foo";

としていたのであれば


set beresp.storage = storage.foo;

と変更すればよいです。

beresp.backend.ipがが削除されました

IPアドレスが0.0.0.0:0のものはUDS経由のものになります。


Unix Domain Socket(UDS)対応

上下流どちらも対応しています。
これを利用することにより高CPS環境において、特に同一サーバに置いてることがほとんどだと思われるTLS終端を行うミドルウェアとの通信をより効率的に行えるようになります。(上流側)

なお、今回の例ではNginxを組み合わせてやってみます。

LISTEN側でUDSを使う場合
起動パラメータの-aで以下のような指定をします


-a /var/run/varnish-uds.sock,user=vcache,group=varnish,mode=666

今回はproxyプロトコルを指定していませんが指定できます。(PROXYを追加しましょう)
また今回はお手軽にNginxとつなぐために666にしていますが、適切な設定にするべきでしょう。

Nginx側


upstream backend {
    server unix:/var/run/varnish-uds.sock;
}
server {
        listen 80 default_server;
        listen [::]:80 default_server;
        location / {
            proxy_pass http://backend;
        }
...
}

これだけです。
なお、LISTEN側でUDSを使う場合はVCL4.1が必須です。
が、現状試してる限りでは起点となるvcl(default.vcl)が4.1であればinclude先は4.0でも平気な動きをしています。 
しかし、ドキュメント上は

When UDS listeners are in use, VCL >= 4.1 will be required for all VCL programs loaded by Varnish.

と記述されており、もしかしたら塞がれる可能性もありますので、UDSを使う場合はすべてのVCLを4.1にするのが無難でしょう。

なお、UDSは当然ながらIPアドレスはなく、UDS経由のアクセスは0.0.0.0:0として扱われます。
なので、client.identity(デフォルトではclient.ipが使われる)を使って振り分けを行う場合は常に同一backendが選択されるので注意が必要です。
適切に修正を入れるか、そもそも上流はPROXYプロトコルが使えるミドルウェアにするなどの対策が必要でしょう。

バックエンド側でUDSを使う場合
すごく単純です
Nginx側


server {
  listen unix:/var/run/nginx-uds.sock;
  access_log off;
  root /var/www/html;
  index index.html index.htm index.nginx-debian.html;
}

Varnish側


vcl 4.1;
backend default {
    .path = "/var/run/nginx-uds.sock";//絶対パス
}

これだけです。
特に難しくないと思いますが、1点注意なのがbackend内での.pathプロパティはvcl4.1のみなので4.0だと出来ません。
なおListenの時と違いバックエンドのみ使用する場合は4.0混在でも動きます。とはいえ4.1の書き換えは難しくないので書き換えたほうが無難でしょう。


その他新機能・変更

varnishncsa

制御文字・バイナリを含む可能性があるタグの-Fを拒否するようになりました
対象はH2RxHdr H2RxBody H2TxHdr H2TxBody Debug HttpGarbage Hashですが、たぶん使ってる人はいないかなと

起動パラメータの変更

esi_iovsの追加
ESI処理で使うiovec構造体の割り当て数ですが通常の場合はいじる必要がありません。
ESIを使っていてどうしてもtuneしたい場合はsystemcallでwritevの実使用数をみながら調整するとよいでしょう
またこの値を変更する場合はworkspace_threadを同時に調整する必要があるでしょう(割り当て元がworkspace_threadからなので)

h2関連パラメータの追加
h2_header_table_size
h2_max_concurrent_streams
h2_initial_window_size
h2_max_frame_size
h2_max_header_list_size
このあたりはh2のRFCを読んでもらえればわかるかなって感じです。
ちなみにh2_max_concurrent_streamsは100なんですが、chromeのストリームはデフォで1000なので、そのあたりで気になる人もいるかもしれないです。(とはいえそんなに弄る必要ないと思います)

feature_bitの追加(http_dete_postel)

日付の形式はrfc7231#section-7.1.1.2で定義されてるんですが、たまにこれに準拠しないやんちゃなDate,Last-Modified,Expiresを返してくるサーバがいます


Fri, 2 Mar 2018 14:26:02 GMT  (まちがい)
Fri, 02 Mar 2018 14:26:02 GMT (ただしい)

この間違いのパタンを許容するようになります
有効にしたい場合は-p feature=+http_dete_postelにします。
個人的にはオリジン側直したほうがよいかなーとは思いますが・・・

cli_bufferの削除
まぁ以前からアナウンスあったので・・・

カウンタの追加

cache_hit_grace
そのまんまですがgrace状態でレスポンスできた件数になります。
パフォーマンスを稼ぐ場合は一旦古い状態のキャッシュを返しつつ裏で更新リクエストを行うのを重視しますが(うまくいけばキャッシュ更新時に遅くならない)
このカウンタを見てチューニングを行うことが出来るかなとも思います。 

n_lru_limited
nuke_limitに達した件数を示します。
当然ですがストレージがいっぱいの状態で新しいオブジェクトをキャッシュを行おうとする場合は古いものを押し出す必要があります。(LRUです)
Varnishではこの処理をNukeと呼んでいますが、実はこの処理にはnuke_limitというパラメータがあり、一つのオブジェクトを確保するためにNukeして良いオブジェクトのLimitを定義しています。
オブジェクトサイズがだいたい似たようなところならデフォルト値(50)でも問題ないのですが、平均50KBのところに10MBのでかいオブジェクトがたまにやってくるとかだと足りないことがあります。
当然Limitを超えた場合はストレージを確保できないためエラー(503)となるのですが、問題はストリームをしている場合です。
ストリームでレスポンスをしている場合は、当然ながらオリジンから帰ってきたレスポンスコード(200)を返してしまっているので503を返すことができません。
そのため200なのにレスポンスボディが中途半端なサイズになるということがあるのですが(割とmlとか見てるとそれっぽい問い合わせ多い)
このカウンタを見ることでそのような状態に陥ってることがわかります。
とりあえず1でもあがるようであれば、nuke_limitを引き上げる、ストレージの見直しをする(サイズあげるとか、ある程度のサイズ毎に分けてしまうとか)とかすると良いと思います。

vmod_directors

shard directorにかなりの修正が入っています。
shard_param
新しくshard_paramというクラスが定義されています。
基本的に今までのshardの各種パラメータ(rampupとか)が定義されており、単純に外出しにされたものです
使い方は


sub vcl_init{
	new vd = directors.shard();
	vd.add_backend(hoge);
	new p = directors.shard_param();
	p.set(by=URL, warmup=0.7);
	vd.associate(p.use());
}


sub vcl_init{
	new vd = directors.shard();
	vd.add_backend(hoge);
	new p = directors.shard_param();
	p.set(by=URL, warmup=0.7);
}
sub vcl_backend_fetch{
	set bereq.backend=vd.backend(param=p);
}

こんな感じです。

ハッシュアルゴリズムについて
今までハッシュアルゴリズムを選択できたのですが(SHA256(default),CRC32,RS)
これがSHA256だけになりました。これに伴いalgを指定できていたメソッドにおいてalg引数が削除されました。
しかし、わざわざSHA256以外を使ってる人も少ないと思うのでそこまで影響ないと思います。
引数がなくなる影響を受けるのは.reconfigureと.keyになります。

resolve={now,lazy}の追加
個人的に注目している機能です。
lazyモードは実際にバックエンドへの接続を作るタイミングで解決されるものです。
これが何が嬉しいのかというと、Directorを入れ子にすることができます。
例えばfallbackと組み合わせてdirectorが全滅したらfallbackするみたいなのも書けます。


probe healthcheck {
    .url ="/check";
    .timeout           = 1s;
    .window            = 4;
    .threshold         = 2;
    .interval          = 1s;
}
backend ws01  {.port="80";.host = "XX";.probe=healthcheck;}
backend ws02  {.port="80";.host = "YY";.probe=healthcheck;}

sub vcl_init {
	new sdp = directors.shard_param();
	sdp.set(warmup=0.5, healthy=ALL);

	new sd1 = directors.shard();
	sd1.associate(sdp.use());
	sd1.add_backend(ws01);
	sd1.reconfigure();

	new sd2 = directors.shard();
	sd2.associate(sdp.use());
	sd2.add_backend(ws02);
	sd2.reconfigure();

	new fb = directors.fallback();
	fb.add_backend(sd1.backend());//lazy
	fb.add_backend(sd2.backend());//lazy

}
sub vcl_recv {
	set req.backend_hint = fb.backend();
	return(pass);
}

サンプルでshardに登録してるバックエンドはそれぞれ一つですが感覚は掴んでもらえるかと思います。(ついでにshard_paramもサンプル的に入れてみました)

vmod_std

std.log/syslogにおいてworkspace枯渇した場合でもfailしないようになりました

vmod_unix

今回追加されたvmodです
接続してきたプロセスのuser/group/uid/gidを取得できます。
厳密にガードをかけたいときに使うとよいかもしれないです。
man vmod_unixそのままですが


sub vcl_recv {
      # Return "403 Forbidden" if the connected peer is
      # not running as the user "trusteduser".
      if (unix.user() != "trusteduser") {
              return( synth(403) );
      }

      # Require the connected peer to run in the group
      # "trustedgroup".
      if (unix.group() != "trustedgroup") {
              return( synth(403) );
      }

      # Require the connected peer to run under a specific numeric
      # user id.
      if (unix.uid() != 4711) {
              return( synth(403) );
      }

      # Require the connected peer to run under a numeric group id.
      if (unix.gid() != 815) {
              return( synth(403) );
      }
}

こんな感じです

vmod_proxy

今回追加されたvmodです
Proxy Protocol v2にはType-Length-Value(TLV)というフィールドに例えばどんなCipherを使用しているかみたいな情報を埋め込むことが可能です。
残念ながらHitchはalpn(PP2_TYPE_ALPN)しか埋め込んで来ないようなので、フル機能を使いたい場合はHAProxyの比較的新しいバージョンを使う必要がありますが
何かしら情報を取得してログに出力したい場合は有用だと考えています。
また、残念ながら取れる値は固定になっているので、カスタムTLVの範囲へは現状アクセスできません。(NLBのVPCエンドポイントIDとかl)
とはいえコードを見る限りでは対応は容易なのでp-rしてみるのもおもしろいかもしれないです。
使い方は単純で


vcl 4.1;
import proxy;
import std;
sub vcl_recv{
	if(proxy.alpn() == "http/1.1"){
		//プロトコルはまぁreq.protoで取れるんで適切ではないかもですがサンプルとして
		...
	}
}

こんな感じです

VSL

ReqAcctのバイト数が正確になりました
プロトコルのオーバーヘッド分(chunkedのとか)は今まで入ってませんでしたが、OSから報告される値を使うように変更したので正確になりました。

バグ修正

多くのバグ修正がされているのですが、その中でも特筆したほうがよいものに触れます。
max_restartsの取扱い
return(restart)を行った際のチェックの不備で、max_restrtsで指定した回数より1回少ないrestartでlimitになっていましたが、修正されてmax_restartsの指定した数return(restart)ができるようになりました。


11月 222017
 

最近のVarnishのリリースサイクルは半年に一回で、その間はセキュリティ等のクリティカルな問題がない限りはリリースされません。
当然、半年の間に様々なFeatureやBugfixなどがコミットされており、場合によっては使いたいこともあるかと思います。
今までにそのように最新のVarnishを使うためには、以前紹介したpkg-varnishcacheを利用して自前でビルドする必要がありましたが
それなりに敷居が高く、そんなに使ってる人はいないんじゃないかと思います。
しかし、今月に入ってからweekly-buildがされるようになりました。(rpm/deb)

https://packagecloud.io/varnishcache/varnish-weekly

branchはtrunkになりますので、場合によってはすでに互換性のない変更が上がっている可能性がありますが
気になるfeatureやBugfixなどがあって更新したいときは使ってみるとよいかと思います。
なお、weeklyとありますが、基本的に毎週月曜に行っているbug-washでビルドするか決めているので
開発中のfeatureがあるなどの場合はskipされるみたいです。


11月 162017
 

Varnishの複数のバージョンがリリースされました。 [changelog] [パッケージDL] [ソースDL]

今回のリリースはセキュリティ対策によるもので、なるだけ迅速にアップデート、もしくは回避するために設定を見直す必要があります。
詳しくは公式情報(VSV00002)を参照してください。

脆弱性の内容

以下の条件を満たす際にsegfault/データリークが発生する可能性があります。

  • VCL_backend_errorで生成したsyntheticオブジェクト(beresp.body)を
  • file/persistentストレージに挿入する
原因

バックエンドへの接続が失敗したりなどで表示されるエラー画面はvcl_backend_errorで生成されています。
builtin.vclをみればわかるようにこのアクション内でエラー画面を生成しています。
で、この生成したものを後処理でストレージを確保してからberesp.bodyからストレージへmemcpy(3)するんですが、この際にfile/persistentストレージがターゲットに選ばれた際に問題が発生します。
file/persistentストレージの領域はpage-sizeでalignされており、確保サイズが割り切れない場合はちょっと余分な容量がついてきます。
で、この時にmemcpy(3)の第三引数に指定するサイズをalignされたサイズをそのまま渡してしまったために、コピー元から余分なサイズをreadしようとしてしまいます(なのでだいたいはsegfaultするかと)

では、vcl_backend_errorを通過すると必ず起きるかというとそうでもないです。
このようなエラー画面などは、TTLが短いためtransientというテンポラリストレージに保存するようになっています。
これは明示的に指定していない場合はmallocストレージエンジンが利用されるので今回のケースには当てはまりません。
しかし、transientに挿入する条件はエラー画面だからとかそういうのではなく、純粋にTTL(正確にはttl+grace+keepの合計値)がパラメータのshortlived未満かどうかです。
shortlivedを超える場合はhintで指定された(無指定はdefault)ストレージを使用します。
そして、shortlivedはデフォルトで10秒なので、VCL_backend_errorにおいてttlがそれ以上になるようにしていると引っかかります。(そのような人は珍しい気もしますが・・)
では、そのまま使っているケースであれば踏まないかというと、実は踏むケースが存在します。
ここのコードを見てみると同一リクエストがある状態(waitinglistに入ってる)の場合だとttl+grace+keepの合計が11secになります。
デフォルトのshortlivedが10secなのでここにタッチしますので、この状態でdefault storageがfile/persistentを使用している場合は踏んでしまいます。
なのでまとめるとこんな感じになります

Transientを明示的にfile/persistent指定している場合(ex -sTransient=file…)
vcl_backend_errorに突入した場合は引っかかります。
そもそもTransientをfile/persistentで使うメリットはそこまで無いので再検討するとよいかと思います。

デフォルトストレージにfile/persistent指定している場合
このケースでは2パタンあります。
vcl_backend_errorでTTL/grace/keepを明示的に指定していて、合計値がshortlived以上の場合はvcl_backend_errorに突入した場合は引っかかります。
次にTTLの変更もなく、shortlivedの変更もない(default 10sec)場合で同一リクエストがラッシュしてる状態だとTTL合計値がshortlivedを超えるためvcl_backend_errorに突入した場合は引っかかります。

なお、同様にsyntheticオブジェクトを生成するvcl_synthのほうは影響を受けません。
vcl_synthの場合はここでmemcpy(3)していますが、resp.bodyのlength(szl)を利用していますので問題ありません。

暫定の回避方法

それぞれの設定によって違いますが、説明したとおりとりあえずvcl_backend_errorでfile/persistentストレージが使われなければOKです。
なのでvcl_backend_errorもshortlivedもいじってない場合はshortlivedを12sに設定するかvcl_backend_errorでのTTL/grace/keep指定を明示的に0にすると良いでしょう。
なお、shortlivedを引き上げた場合はメモリの使用状況が変わるのでモニタリングする必要がありますし、TTLを明示的に0にした場合は若干ラッシュ時のオリジンの耐性が弱くなるでしょう。(気にするレベルではないと思います)
まぁどちらにせよ早めにアップデートするのが良いでしょう。

そのほかの変更について

今回はこの脆弱性以外にもいくつか修正があります。

4.1.9/5.2.1共通の変更
バグ修正

クライアントからのリクエストが空っぽの場合にidle_timeoutが正しく使用されなかったのを修正(#2492)

4.1.9のみの変更

基本的に5.2.0にあるもののbackportになります

新機能

bereq.is_bgfetchの追加
req.ttlの評価変更(#2422)

バグ修正

いくつかのカウンタの修正


9月 242017
 

VarnishCache5.2.0がリリースされました。 [changelog] [公式のリリース文] [パッケージDL] [ソースDL]
バグの修正(特にh2関連で多くの修正入っています)はもちろんですが、
新しいvmodが提供されたり、影響の大きい(破壊的)変更があります。
また、VMOD/TOOL開発者向けの変更も多く、新しいツールなどが期待できます。

影響の大きい変更

VSMの抜本的な変更

Varnishの内部でログや統計の保存先に使用しているVSMの仕組みが変わりました。
これによりVSMを利用してログ(VSL)や統計(VSC)にアクセスしているサードパーティのプログラムについては対応が必要になります。
なお、VSMのラッパーであるVUTについても変更があるのでこれ関係は全部修正が必要と考えても差し支えありません。
(Varnish UTilities:VUTはVSMを使う上でlog,ncsa等のコマンドで毎回同じコードを書くのが面倒でまとめてる高レベルAPIと思ってもらえれば間違いないです)

例えば影響を受けるところとしては
vago
python-varnishapi
varnishkafka
などがあります。
そのためバージョンアップを行う場合は、特にログ、監視周りについて正常に動作するかなどの確認を行う必要があります。

varnishstatの出力フォーマットの変更(-j/-x)

例えばmuninなどの監視ミドルウェアの一部はVarnish公式コマンド群のvarnishstatを叩いて統計を取得しているケースがあります。
今回varnishstatの-j(json)-x(xml)オプションの出力からtype, identフィールドが削除されました。


~5.2.0(-j)
  "MAIN.uptime": {
    "description": "Child process uptime",
    "type": "MAIN", "flag": "c", "format": "d",
    "value": 4043809
  },

5.2.0~(-j)
  "MGT.uptime": {
    "description": "Management process uptime",
    "flag": "c", "format": "d",
    "value": 168192
  },

そのためそのフィールドを利用している場合は動かなくなる可能性があります。
自分が確認した限りだとmuninのvarnish4プラグインについては影響を受け、修正が必要になります。

一部VSLタグのフォーマット変更

これも主にツールを作ってる人たち向けですが(もしくはvarnishlogで生ログみてる人)
一部VSLタグのフォーマット変更されており、そのためパースに失敗するなど起こる可能性があります。

Hit, HitMiss, HitPass
今まではVXIDのみでしたが、追加で残TTLを出力するようになりました。(Hitについてはgrace/keepTTLも出力)

SessOpen
第3フィールドには今までlistenソケット(-aオプションの値)がそのまま表示されていましたが、
このバージョンからは-aで指定された名前が出力されます。指定されていない場合はvarnish側で自動でつけられた名前(a0など)が入ります。(後述します)

VCL_trace
新規にVCLの名前とsource indexを出力するようになりました。
これにより、今まではVCLのどこを通過したかを判断するのが割とめんどくさかったのが簡単にできるようになります


10 VCL_trace      c boot 4 0.23.3
                    |    | | |  |
                    |    | | |  +- VCL program line position
                    |    | | +---- VCL program line number
                    |    | +------ VCL program source index
                    |    +-------- VCL trace point index
                    +------------- VCL configname

例えばこの出力の場合であれば


# varnishadm vcl.show -v boot

で出てきたVCLから


// VCL.SHOW 0 939 /etc/varnish/default2.vcl
#           ↑ここがsource indexになる
#
# This is an example VCL file for Varnish.
...

の記述を探して


 22 sub vcl_deliver{
 23   set resp.http.Hash = blob.encode(BASE64, blob=req.hash);
      ↑ここ

line:23 pos:3をさがします
これでどこを通過したかがわかります。

その他変更

varnishdの変更

-lでvsmサイズを指定する必要がなくなりました
先程も述べたとおりvsmの仕組みが変わった影響でサイズを指定する必要がなくなりました。
とりあえずは無視されるようになっています。

-aで名前が指定できるようになりました
listenソケットの名前を指定できるようになりました。(-a admin=127.0.0.1:88の場合だと名前がadminになる)
現時点では先述したSessOpenのフォーマットに影響する程度です。
次バージョンへの準備と思ってもらえれば良いかと。

varnishstatの変更

MAIN.s_reqは廃止されました
MAIN.client_reqと同じなのでこちらを見るようにしましょう

MAIN.req_droppedが追加されました
h2ストリームが拒否された回数を示します。

-Nオプションの廃止

varnishlog,stat,ncsa,histに存在した-Nオプションは廃止されました。

VCLの変更

変数の追加や一部変更はありますが、既存の修正が必要になるような変更はありません。基本的には5.1で動いていたVCLはそのまま動くと思います。

[動作変更]server.identity
以前は-nオプションの指定(def:インスタンス名)だったのですが、このバージョンより-iオプションの値になりました。
もし-iで指定していない場合はホスト名(gethostname(3)の結果)になります。

[追加](req|bereq).hash
現在のオブジェクトのキーハッシュをBLOB出力します。
後述するvmod_blobを利用することでHEXやBASE64形式に変換することが出来ます


sub vcl_deliver {
    # base64形式でハッシュをレスポンスヘッダにセットする
    # Hash: ANTj5yFpZGjJsL0IaxEDGiG29h0fSZRvjywdvDkKihc=
    # こんな感じで出力される
    set resp.http.Hash = blob.encode(BASE64, blob=req.hash);
}

[追加]bereq.is_bgfetch
Varnishのバックエンドへのフェッチは2つあり、キャッシュが無いなどでリクエストと同時に取得しにいくfetchとgraceの動作などでバックグラウンドでfetchを行うbgfetchが存在します。(ここのバックグラウンド更新について見てもらえればどんな動きするかわかると思います)
今まではbackend_fetchなどのバックエンドアクションにおいて、今処理しているfetchがバックグラウンド処理なのかを判定する方法はありませんでしたが
この変数を使うことによって可能になりました。
bgfetchを行っている場合はtrueが入ります。また読み取り専用なので書き込みは出来ません。

[その他]req.backend_hint
特に変更というわけではないと思うんですが
req.backend_hintのrestart時の挙動が明記されました。
デフォルトのバックエンドにリセットされます。

vmod_std

新しく指定ファイルの有無をtrue/falseで返すfile_existsが追加されました。
この関数はstd.filereadと違い結果はキャッシュされないので呼び出すごとにstatが呼び出されます。

vmod_blob

新しくvmod_blobが追加されました。
実はあまり使われていないのですがVCLにはBLOB型というのが存在しており、主にvmod間でのデータの受け渡しなどで使用されています。
このvmod_blobはencode/decodeなどを提供しています。
対応エンコードにはbase64/urlencodeも含んでおり便利かと思います。
とりあえず基本的な使い方について解説します。

例えばHEX表記されたfoo(666F6F00)を一旦blobに変換したあとにSTRINGに変換してヘッダに入れる場合はこうなります。


sub vcl_deliver{
  set resp.http.foo = blob.encode(IDENTITY, blob=blob.decode(HEX, encoded="666F6F00"));
}

ここでIDENTITYやHEXがフォーマットです
ちなみにblob.decode/encodeを使わずとも一気に変換するtranscodeというものもあります。
先程と同じ出力を得るには


sub vcl_deliver{
  set resp.http.foo = blob.transcode(encoding=IDENTITY, decoding=HEX, encoded="666F6F00");
}

でも出来ます。

とりあえず対応フォーマットです。

  • IDENTITY
  • BASE64
  • BASE64URL
  • BASE64URLNOPAD
  • HEX
  • URL

BASE64とかURL(URLエンコード)はみたまんまなので特に解説はいらないと思います。
IDENTITYフォーマットは特に何らかのエンコードがされているわけではなく入力された値そのままを使います。
なので先程の例で666F6F00を出力した場合はfooになったわけです。
ちなみにHEX形式の先頭には0xは不要ですので注意しましょう。

詳しい使い方についてはこちらを参照ください

vmod_purge

組み込みのpurgeをより柔軟に使うためのものです。
ちなみに既存のreturn(purge)がなくなったわけではありません。
なお以下の関数はすべてvcl_hit/vcl_missでのみ呼び出し可能です。

INT purge.hard()
return(purge)と同じ挙動をします。(今のハッシュと一致するオブジェクトを消す)
戻り値は消したオブジェクト数になります。

INT purge.soft(DURATION ttl=0, DURATION grace=-1, DURATION keep=-1)
デフォルトの場合はTTLを0にします。ただしgraceやkeepは変更しません(-1)
ちなみにhard()はsoft(0,0,0)と同じです。
まぁ、既にキャッシュされたオブジェクトのTTL,grace,keepを自由にいじるのに使えると思ってもらえれば良いかと思います。
なお戻り値は操作したオブジェクト数になります。

vmod_vtc

これは元々vmod作者がテストコードを記述する際に便利に使えるものです。
子プロセスをpanicさせたりする関数が含まれており、通常のユーザーが使うものではないと思います。

バグ修正

多くのバグが修正されているため、そこまで触れませんが1点だけ・・・
http/2においてメモリリークのバグが修正されています。
今のところ該当パッチが当たってから一月以上稼働させていますが、安定動作しております。
まだexperimentalなので人柱覚悟とはいえ普通に使えるんじゃないかと思います。

次のバージョンについて

次の大型リリースは2018年3月予定です。
実は元々今回はメジャーバージョンアップが予定されていたのですが、予定リリース日に間に合わないということで一部分を切り出して5.2でリリースされました。
なので既に次期バージョンの片鱗はprなどにあるのですが(例えばUDS対応)なかなかおもしろい機能が増えたバージョンになりそうで、個人的には楽しみにしてます。


8月 032017
 

Varnishの複数のバージョンがリリースされました。 [changelog] [パッケージDL] [ソースDL]

今回のリリースはセキュリティ対策によるもので、なるだけ迅速にアップデート、もしくは回避VCLを入れるべきです。
詳しくは公式情報(VSV00001)を参照してください。

脆弱性の内容

書くか少し迷ったんですが、テストコードがまんまコミットされてるので解説します・・
クライアントがTransfer-Encoding: chunkedかつchunk-sizeの指定が内部の変数でMSBが立つぐらい大きな指定でリクエストを行った場合、AssertでVarnishが再起動します。(DoS)


$ sudo varnishadm panic.show|head
Panic at: Wed, 02 Aug 2017 16:45:40 GMT
Assert error in v1f_pull_chunked(), http1/cache_http1_vfp.c line 172:
  Condition((vfe->priv2) == 0) not true.
version = varnish-5.1.2 revision 6ece695, vrt api = 6.
0
...

対象バージョン

Transfer-Encoding: chunkedに対応したのは4.0.1からなのでそれ以降のすべてのバージョンになります。

バージョン系 影響を受けるバージョン 修正バージョン パッケージDL先
4.0.x 4.0.1~4.0.4 4.0.5 link
4.1.x 4.1.0~4.1.7 4.1.8 link
5.0.x 5.0.0 5.1.3まで上げる必要があります link
5.1.x 5.1.0~5.1.2 5.1.3 link

4.0.0及び3.0.xは先に述べた通り、そもそも今回の機能がないので対象外です。

回避方法(VCL)

VCLでの暫定的な回避方法があります。
これらはchunkedでリクエストしてきたものを503で落とすことで回避します。
大抵のブラウザの場合はTransfer-Encodingを指定したリクエストは行わない事が多いのですが、念のため確認すると良いでしょう。
varnishlog -cq ReqHeader:Transfer-Encoding -i ReqMethod -i ReqURL
これで何かしら出てきて、正規のリクエストの場合はVCLでの回避は不可能ですのでバージョンを上げる必要があります。

なおここで紹介している回避コードは公式そのままなので公式も参考にしてください(VSV00001)
4.0.x向け
インラインCを利用します。
そのためvcc_allow_inline_cをtrueに設定する必要があります
起動パラメータの場合は
-pvcc_allow_inline_c=true
varnishadmで指定する場合は
varnishadm param.set vcc_allow_inline_c true
で可能です
VCLは以下の通りです。


sub exploit_workaround_4_0 {
        # This needs to come before your vcl_recv function
        # The following code is only valid for Varnish Cache and
        # Varnish Cache Plus versions 4.0.x
        if (req.http.transfer-encoding ~ "(?i)chunked") {
                C{
                struct dummy_req {
                        unsigned magic;
                        int restarts;
                        int esi_level;
                        int disable_esi;
                        char hash_ignore_busy;
                        char hash_always_miss;
                        void *sp;
                        void *wrk;
                        int req_step;
                        struct {
                                void *a;
                                void *b;
                        };
                        int req_body_status;
                };
                ((struct dummy_req *)ctx->req)->req_body_status = 6;
                }C

                return (synth(503, "Bad request"));
        }
}

sub vcl_recv {
        # Call this early in your vcl_recv function
        call exploit_workaround_4_0;
}

4.1.xと5.0.0向け
同じくインラインCを利用しますので4.0.xで有効にしたように指定が必要です。


sub exploit_workaround_4_1 {
        # This needs to come before your vcl_recv function
        # The following code is only valid for Varnish Cache and
        # Varnish Cache Plus versions 4.1.x and 5.0.0
        if (req.http.transfer-encoding ~ "(?i)chunked") {
                C{
                struct dummy_req {
                        unsigned magic;
                        int step;
                        int req_body_status;
                };
                ((struct dummy_req *)ctx->req)->req_body_status = 5;
                }C

                return (synth(503, "Bad request"));
        }
}

sub vcl_recv {
        # Call this early in your vcl_recv function
        call exploit_workaround_4_1;
}

5.1.x向け
インラインCは使用しません。


sub vcl_recv {
        if (req.http.transfer-encoding ~ "(?i)chunked") {
                return (fail);
        }
}

その他

実は今回の脆弱性について概要と公開予定時刻を事前に教えてもらっていました(攻撃には使用できないぐらいの荒い情報ですが)
お陰で公開後に即対処することが出来たのですが、VIVU – Very Important Varnish Usersで触れられているようにVMLを買うと教えてもらえるようなので(€1000/年)
企業でVarnishを使っているころは買ってみると良いのではないでしょうか?


8月 022017
 

4.1系の最新版の4.1.7がリリースされました。 [changelog] [公式のリリース文] [パッケージDL] [ソースDL]

今回の変更内容は主にstat周りのバグフィックスとちょっとした新機能です。

追加された機能

varnishncsaのLogFormatの%{X}xでrecord-prefixに対応しました(#2077)

もともとは%{VSL:tag[field]}xといった感じで指定できたのですが
Timestampのように同一タグでラベルで情報が違うものの場合、例えばProcessにかかった時間、Fetchにかかった時間を分けて出力することは不可能でした。(最初にマッチしたものだけなのでStartになる)
しかし今回record-prefixに対応したため、vsl-queryのような柔軟な指定が可能になりました。(levelには対応してないですが)


$ sudo varnishncsa -F "fetch:%{VSL:timestamp:fetch[2]}x process:%{VSL:timestamp:process[2]}x"
fetch:0.000377 process:0.000392

バグ修正

workspaceのリセット漏れでworkspaceが足りなくなって落ちるケースがあったのを修正(#2219)

varnishstatでのglobを修正(#2022,2118,2320)
あまり知られていないオプションだと思うのですが -fでstatのキー絞り込みをできるのですが
-1との組み合わせの時にうまく動かないバグがいくつかあったようです。

nuke_limitの効果がなくなっていたのを修正(#1764)

ヘルスチェックの応答でReason-Phraseがない場合に失敗していたのを修正(#2069)

(ちなみに今回記事が遅れたのは前の記事書いたあとあたりに書いたつもりになって忘れてました・・すいません)


4月 072017
 

5.1.2がリリースされました。
5.1.1の記事で4月中旬まで様子を見たほうがよいと言っていたのはこの件です。
(まだ確定じゃなかったので微妙にぼかした表現でしたが・・)
今回のリリースは1件の機能強化と主にHTTP/2関連のバグフィックスです。
[changelog] [リリース文] [パッケージDL] [ソースDL]
 

機能強化

std.collectでセパレータを指定できるようになりました
std.collectは同一名のヘッダが複数行送られてきた場合に1行にまとめる関数ですが今までは[, ]でまとめられていました。
通常の場合は特に問題ないのですが、Cookieの場合は[; ]でまとめる必要があり、追加で置換が必要など面倒でした。
デフォルト値には[, ]が指定されているので、既に使用している場合は特に追加指定は不要ですが
Cookieで使用する場合は以下のように指定するとよいです。


std.collect(req.http.cookie, "; ");

 

バグ修正(h2以外)

#2295 動的バックエンドを使用しているときにヘルスチェックが無限ループに陥ることが合ったのを修正
#2207 #2278 PROXYプロトコルを使用したバックエンドのヘルスチェックで競合状態がおきるのを修正
 

バグ修正(h2)

#2291 #2300 Cookieヘッダを1行にまとめるように修正
#2247 ボディを含むリクエストにContent-Lengthが存在した場合でもTransfer-Encoding: chunkedを追加していたのを修正
上記2つの修正により、前回紹介したVCLトリックは不要になりました。

#2238 ReqAcctの値がないためvarnishncsa等で入出力したバイト0になるのが修正されました
わかりやすいところだとvarnishncsaの%bなどが常に0になっていたのですが、この部分が実装されたためサイズがでるようになりました。

多数のassertを修正

Varnishでのhttp/2について

5.1.1では検証環境で使用できると書きましたが、今回は十分な検証を行った上であれば本番環境に入れることもできなくはないと考えています。
もちろんまだexperimentalで残バグもおそらく新規バグもあると思いますが
主にSimonや自分が相当数踏み抜き、修正されているので
以下の幾つかの注意点を守れば比較的安定的に動作すると思います。

#2268 PRIV_TASK/TOPを利用しているvmodを避ける
PRIV_TASK/TOPについてはこちらの記事を御覧ください。
このPRIVの確保・解放を行う際に競合状態が起きているようで、稀にassertが起こります。
現時点で回避するには使用しているvmodの関数を避けるしかありません(例:vmod_cookie)
使用しているかどうかの判定はvccファイルを見てPRIV_TASKかPRIV_TOPがあるかで判定できます。

スレッドのメモリ使用サイズがh/1の時と比べて大幅に増える可能性があるのでチューニングが必要
h2はh1と比べるとメモリをより多く使用するため一瞬なんか漏れてると思うぐらい増えます。
元々メモリに余裕がある環境であれば特に問題はないのですが、VPSなどあまりメモリがない環境の場合は問題といえます。
解決方法としては単純にスレッド数に余分があれば減らす感じです。
もし、減らしすぎてスレッドが足りなくなったとしても、そのリクエストはキューイングされて、すぐにスレッドを追加作成して処理されます。
関連するパラメータとカウンタはこれです。

パラメータ
thread_pools スレッドプール数
thread_pool_min スレッドプール毎の最低スレッド数
thread_pool_max スレッドプール毎の最大スレッド数
thread_queue_limit スレッド枯渇時のキュー長
カウンタ
MAIN.threads_limited ドロップされたセッション数
MAIN.sess_dropped ドロップされたセッション数
MAIN.sess_queued キューイングされたセッション数
MAIN.threads 現在のスレッド数

スレッド数はthread_pools * thread_pool_min以上thread_pools * thread_pool_max以下となります。
チューニングをする際はMAIN.sess_droppedが起きるのは論外で、MAIN.sess_queuedもそんなに起きないのが望ましいです。
このあたりのチューニングは今回の本筋ではないので詳しくは解説しませんが、要は過剰であればthread_pool_minを減らしてみて
MAIN.threadsが止まったあたりでdropped/queuedも起きないように調整するとよいのではとか思います。
本当にメモリが辛い場合は多少パフォーマンスは落ちますがqueuedを覚悟してthread_pool_minをガッツリ下げてthread_queue_limitを上げるのも手だと思います。(その前にスペックとか他のパラメータを再検討したほうが良いと思いますが)
結局のところ、ここでいいたいのはメモリの使用傾向が変わるとおもうのできちんとチューニングしようねって話です。
追記:上記で長めに運用しましたが、多少ましになる程度でした。(とはいえスレッド数の調整はしたほうがよいかなと思います。

とはいえ、HTTP/2を使う際はpkg-varnishcacheを使ってなるだけ最新を追っておくと良いかと思います。

hitch1.4.4を使う場合の注意事項
hitchは設定のreloadをサポートしているのですが(HUPを投げる)
ocsp staplingが有効だとreload時にゾンビプロセスが残る場合があります(#167)
以下のように設定して無効にすることで回避も可能です。


ocsp-dir = ""

あくまでoffにすることを推奨しているのではなく、ここは各自考えてといった感じでお願いします。

最後に
ぜひVarnishのhttp/2を試してみてほしいなと思います。
また、Varnishの開発を助けるためにVMLを購入するのをぜひ検討してみてください