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だったんですが、どうなるんだろ・・


5月 092019
 

Vanrish6.2.0がリリースされました
マイナーバージョンアップにとどまることからわかる通り大きな機能の追加はなく、公式のリリース2019年4月3日メールでも触れられていますが、HTTP/3が見えてきているのでその前段階の改善といったところでしょうか(最近のbugwashでもh3の話題を見かけます)
割と重要なバグ修正や機能改善が含まれています。

開発者向けの仕様変更

インストールやビルドに必要なPythonが2.7から3.4以上に変更されました

今回の公式パッケージの提供が遅れた原因です。
6.2.0のリリース自体は3/15にあったのですが、公式パッケージは3/29まで提供されていませんでした。(で、記事が今頃になったのは書きかけで放置してた感じです・・
なおPython2.7.xは来年EOLになるみたいです。

vmodの仕様変更

対応していないvmodはほぼ間違いなく動作しません。
変更ポイントは多いのですが、多くのvmodで引っかかるのはvccで定義した各種func/eventは必ず先頭にvmod_がつくようになりました。
今までは

$Event event_function

という定義であれば

int v_matchproto_(vmod_event_f)
event_function(VRT_CTX, struct vmod_priv *priv, enum vcl_event_e e)

でよかったのですがこれが

int v_matchproto_(vmod_event_f)
vmod_event_function(VRT_CTX, struct vmod_priv *priv, enum vcl_event_e e)

とする必要があります。
また次に引っかかるのが多そうなのがVCL_BLOBで使う構造体が変わったことです。
今まではvmod_privだったのですが専用のvrt_blob構造体が用意されました。
ほぼrenameで対応できるのでこれも容易だと思います(あとは->privを->blobに変えれば大体動くはず)
VRT_APIが9に変更されてるので

int v_matchproto_(vmod_event_f)
#if VRT_MAJOR_VERSION > 8U
  vmod_event_function(VRT_CTX, struct vmod_priv *priv, enum vcl_event_e e)
#else
  event_function(VRT_CTX, struct vmod_priv *priv, enum vcl_event_e e)
#endif

こんな感じで両対応も可能です。

VUTの仕様変更

通常の利用者は問題ないと思うのですが、自前で作ったスクリプトとかでVUTをたたいている人はおそらく動かなくなります。
VUT構造体が変更されていますので追随する必要があります。
また、signalのハンドリングも変わっているので気を付ける必要があります。

仕様変更

タイマー系のログ出力はミリ秒精度に統一されました。

ExpKillとExpRearmは元々ナノ秒だったんですがこれがミリ秒に変わりましたが
これを見てる人ほぼいないと思うので影響する人はいないでしょう。

非推奨のパラメータが削除されました。

shm_reclen/vcl_dir/vmod_dirが削除されています。
それぞれvcl_reclen/vcl_path/vmod_pathを利用してください。

パラメータの追加/変更

thread_pool_stackのデフォルトサイズが大きくなりました。

VCLの変更

req.is_hitmiss/req.is_hitpassが追加されました

vcl_hit内でreturn(miss)が削除されました。
そもそも期待通りうごかなかったみたいです

機能追加/改善

起動パラメータのdebugにvcl_keepが追加されました。

VCLをCコードに変換した結果とsoを保存するようになっています
出力は/var/lib/varnish/[instance-name]/[vcl-name]/以下にvgc.cで出ます。
varnishtestを行う際に便利かもしれません。

ban時にttlなどの有効期限での比較が使えるようになりました。

obj.age/ttl/grace/keepは時間の経過とともに変わる値ですがこの値を使ってbanができるようになりました
なので例えば5時間経過しているオブジェクトを消したいといったときは

ban obj.age > 5h

みたいな指定が可能になりました。

パラメータをデフォルトに戻るparam.resetが追加されました(varnishadm/varnish-cli)

varnishadmでのjson出力サポート

6.0.2と6.1.0からサポートするようになったjson出力ですが
6.1.1の記事でも触れた通り実は6.0.2のほうがjson出力に対応しているコマンドが多かったのですが、6.2.0では違いがなくなっています。
また同時にいくつかのコマンドの列幅がターミナルのサイズに合わせて可変長となったので
もしツールなどでcliの結果を処理しているのであればjsonを使うことを検討すべきでしょう。

statの強化

これも6.0.2のみで対応していたstatが6.2.0でも出るようになりました。

lookup処理の改善(#2854)

lookup時のmutexを減らしています。
高RPSな環境では割と効果があると思います。

resp.filtersの追加

6.1.0の際に増えたberesp.filtersのresp版です。
最終的なレスポンスを行う際に使用できるVDP filterの並び替えができるようになりました。
ただ、バグがあるのですぐに使う人もいないと思いますが一旦次バージョンを待ったほうがいいです(masterは修正済み

vmod_stdの型変換関数の改善

型変換は今までreal->integerの時はreal2integer、time->integerの時はtime2integerと分かれていてめんどくさく、fallbackの指定も必要でしたがこれがすっきりしました。
例えばほしいものがintegerであればこんな形です。

std.integer(real=1.23)

とりあえずduration/bytes/integer/real/timeが用意されており、
以前のreal2integer/real2time/time2integer/time2realは非推奨となり、将来削除されます。

vmod_directorsにlookupが追加されました。

vcl_init/finiで呼び出し可能で、個人的にはいまいち使い道が思いつかないのですが

new l2_cache = directors.hash();
l2_cache.add_backend(cache01, 1.0);

みたいな指定を

new l2_cache = directors.hash();
l2_cache.add_backend(directors.lookup("cache01"), 1.0);

といった感じで書けます。

varnishncsa/varnishlogにratelimitオプション(R)を追加しました。

-R 10/2m

こんな指定が可能です。

varnishstatのリフレッシュレートが変更できるようになりました。
表示中に+/-を入れると0.1秒単位で増減します。

バグ修正

http/2のactive streamのカウント周りのバグが修正されました(#2916,2923)

一つはコントロール用のstream=0をactiveなストリームとしてカウントしてたのでmax_concurrent_streamsの判定が1ずれてたものと
もう一つはストリームのカウントの増減をスレッドの割り当て時・解放時に行っていたので、解放時に後処理してたら次のstream openの要求が来てrefuseするといった感じです。
とりあえずこれでストリームを多く使うようなサイトでも問題なく使えるようになったと思います。


10月 282018
 

6.1.0が出たときにすぐ記事を書かなかったのは、リリース後に一部デグレ(varnishhist表示)がIRCで話題になって6.1.1をリリースする?
みたいな話がでていたので実際に6.1.0を動かしながらはまりポイントがないかを探しながら6.1.1のリリースをまってました。
思ったより期間があいたので、先に書いとけばよかったなと反省しています。すいません。
また、すでにPRが用意されていることからさほど時期を開けずにLTS(多分)である6.0.2もリリースされると思われます。

6.0.0/6.0.1から6.1.1の変更は新機能もあるのですが、それ以上にバグ修正が多いので基本的にバージョンアップをおすすめします(6.0.2でもいいですが)

特に以下の機能を使っている場合は関連するバグ修正がいくつかあるのでバージョンアップを強くお勧めします。
・HTTP/2 (6.1.1/6.0.2のみの重要な修正があるので6.1.0/6.0.1を利用している場合もバージョンアップ必要)
・proxy protocol
・UDS
・pipe

ダウンロードはこちらから

6.1.xと6.0.xの違い

6.1.xはいくつかの新機能やコア部分の変更はありますが、バージョンアップに苦労するような変更はほぼありません。
例えばVCLについては削除された変数などがないため、6.0で動いていたものをそのまま動かすことができます。
ただしvmodについてはdirector APIの変更があったため、一部については再コンパイルだけでは動作させることができません。(要対応)

では、今回はどのような大きな変更があったかというと
backendをフェッチしてstorageに格納する際に呼ばれるVFPと呼ばれるスタックをVCLから入れ替えなどの操作ができることになったことです。
今までもVFPスタックに処理を追加することはできたのですが(Varnishでテストコードを書こう!~実践編~+Bodyを読もう!)、デフォルトの順番を動かすことや差し替えを行う事が難しかったのです。
例えば今回の変更で、デフォルトのgzip処理をZopfliに変更するようなこともできるといえば可能です。(他にもESI処理の置き換えも)
ちなみにVarnishにはクライアントにレスポンスする直前に呼ばれるVDPスタックもあるのですが、ここの同様な操作は6.1.xでは実装されていません。
しかしtrunkではVDP関連のコミットがあるのでおそらく次期バージョン(2019/03)では来ると思います。

VarnishのLTS

Varnishは古いバージョンのサポートを早々に打ち切ります。
しかし一部のバージョンについては長くサポートされています。
例えば4.1.xは5.xがサポートが打ち切られているのにも関わらずサポートされ続けています。
Releases & Downloads

特に明言されているわけではないのですが(MLとかIRCでなんどかそのような発言は見かけましたが)
VarnishSoftware(VS)がリリースしているVarnishCachePlusのベースとしているバージョンはLTSとしてサポートされるようです。
(用はスポンサーがついてる Please backport #2077 to 5.1.x
現在のところPlusは6.0.1r2と4.1.10r5がリリースされていますので6.0.xと4.1.xはLTSです。
とはいえ6.0ベースのPlusが出たので、さすがに4.1.xは収束していくのではと考えています。
もし、現在4.1.xを使っている方は6.0.xまで上げるのも検討してもよいかと思います。

6.1.1と6.0.2の位置づけについて

今回のリリースで6.0.2だけに追加されている機能があります。(もちろんVFPスタック操作については6.1限定です)
理由を聞いたところ6.1.1はbugfixリリースで新機能を入れることで新しいバグが入り込むのを避けたいということでした。

動作変更について

HostヘッダなしのHTTP/1.1のリクエストを400で返すように変更(#2631)
そもそもHTTP/1.1はHostヘッダが必須なので特に気にすることはないでしょう。
builtin.vclでの実装なので、どうしても困ることがあるのであれば処理を上書きすればよいかと思います。(望ましくないですが)
For HTTP/1.1 requests, Host is mandatory

パラメータの追加

max_vcl/max_vcl_handling
varnishの設定を適用する際にreloadをしていると以前のvclが残ります。
当然これらはディスク容量を圧迫しますし、また一部の操作(vcl.listなど)は当然遅くなります。
max_vclはvcl数の閾値でデフォルトは100です。で、閾値を超えた場合の動作を定義するのがmax_vcl_handlingです。
0は警告も何もしない、1でwarning表示(デフォルト)、2でVCLの追加を拒否します。
デフォルトの状態で使い続けても特にVCLの追加ができなくなることもないので(warnはでる)
warnが出たらvcl.discardしていくのもよいかと思います。

backend_local_error_holddown/backend_remote_error_holddown
バックエンドに接続を行う際に、自身もしくは接続先のリソース不足などで接続失敗することがあります。
この場合で即時再試行しても結局リソース不足で再度失敗することが多いので、少し待ってみるのも効果的です。
このパラメータはその秒数を指定するものです。
backend_local_error_holddownはEADDRNOTAVAIL・EACCESS・EPERMが起きた際に待機する秒数で
backend_remote_error_holddownはECONNREFUSED・ENETUNREACHが起きた際に待機する秒数です。
どちらもデフォルトでよいかなと思います。
また、これらのエラーが起きた際は後述するカウンタが上がりますので、その際はチューニングや構成見直しを行うと良いでしょう。

thread_pool_watchdog
h2の場合依存関係や制御情報をやり取りするだけのストリームがあるのですが、Varnishの場合はそれもスレッドを割り当てます。
ですので、スレッド不足で実際のリクエストに対してスレッド割り当てができない場合、そこでリクエストの処理を待ってしまいデッドロックする可能性があります。
これらのデッドロックを検知した場合に子プロセスを再起動するのですが、その設定時間がこれです。
基本的にデフォルト値で問題ないでしょう。
もし、これ起因でrestartが走るようであれば、スレッド関連の設定を見直すのがよいでしょう(thread_pool_maxなど)
また、h2起因で追加されたパラメータですが、別にh2以外でもデッドロックが起きた場合は発動します。

VCL変更

削除されたいくつかの変数が復活
req.ttl・req.ttl・req.graceが復活しています。

すでにimportしたvmodを再度importしようとしてもエラーにならないように変更
地味にうれしい変更です。
例えばvmod_stdはよく使われているvmodなのですが、複数のvclをincludeしている際にimport std;を複数のvclに記載してエラーになることがあります。(きちんとdefine.vclみたいなものにまとめておけばいいのですが)
この変更でエラーにならずに2回目以降のimportは無視されるようになります。

vcl_init中のreturn failでメッセージを入れられるようになりました
vcl_initで何かしらのエラーが起きて終了させたいときに使えるreturn(fail)ですが
なんのエラーで落ちたかとメッセージで出す方法がありませんでした。
今回return(fail(“foo”))といった指定が可能になったことで失敗した理由を通知することが可能です。


//vcl
sub vcl_init{
  return(fail("foo"));
}

//output
VCL "boot" Failed initialization
Message:
        foo

このような形で出力されます。

beresp.filtersの追加
後述します

新機能について

vmod_stdにfnmatchを追加


BOOL fnmatch(STRING pattern, STRING subject, BOOL pathname=1, BOOL noescape=0, BOOL period=0)

shellでパスを指定するような(ex:/var/log/*)形で指定することが可能です。
pathname/noescape/periodはそれぞれfnmatchのflagsの該当するものと同じ意味です。
FNMATCH

サンプルにもありますが、


if (std.fnmatch("/foo/*", req.url)) { ... }

といった感じでurlマッチに使えると思います。
これの便利なところとしては、VCL中で使う正規表現と違いpre-compileが必要無いのでマッチパタンを変数にするといったことも可能です。

VFPスタックの呼び出し順をVCLから変更可能に(6.1.x)

先ほども述べた通り現状ですぐに役に立つものではありません。
VFPを提供するVMODが増えてくれば有用でしょう。
とりあえず現状どのようなものが適用されているかを確認するのであれば


varnishlog -graw -i filters

で見ることができます。

また、レスポンスヘッダにどのようなフィルタが使われているかをだすには


sub vcl_backend_response {
  set beresp.http.x-filters = beresp.filters;
}

このようにすればヘッダに出力されます。

また設定するには


sub vcl_backend_response {
  set beresp.filters = "esi" // gzipじゃない時のberesp.do_esi = true;と同等;
}

とすればよいでしょう。
とはいえ、理解せずに弄るといろいろ崩れたりしますので注意しましょう。(多分VMODで指定されると思うのでその通り使えばOKかと)
かなり上級者向けの設定です。

varnishadm(varnish-cli)でのjson出力(6.0.2/6.1.0)

json出力がサポートするようになりました。


# sudo varnishadm backend.list -j

[ 2, ["backend.list", "-j"], 1540196843.165,
  {
    "reload_20181022_074741_10060.***": {
      "type": "backend",
      "admin_health": "probe",
      "probe_message": "5/5 good",
      "last_change": 1540194463.057
    },

...

    "reload_20181022_074741_10060.***": {
      "type": "hash",
      "admin_health": "probe",
      "probe_message": "healthy",
      "last_change": 1540194463.054
    }
  }
]

6.1.1では一部、6.0.2は大部分のコマンドで出力をサポートしています。
varnishadmの結果をスクリプトで処理する際には非常に便利だと思います。

6.1.0/6.0.2の両方で対応しているもの
– ping
– backend.list
– help

6.0.2のみで対応しているもの
– status
– vcl.list
– param.show
– ban.list
– storage.list
– panic.show

statの強化
varnishstatで表示されるstatがかなり追加されています。
こちらも一部は6.0.2のみ対応の項目があります

6.1.0/6.0.2の両方で対応しているもの
– MAIN.sess_fail_econnaborted
– MAIN.sess_fail_eintr
– MAIN.sess_fail_emfile
– MAIN.sess_fail_ebadf
– MAIN.sess_fail_enomem
– MAIN.sess_fail_other
– [backend名].unhealthy
– [backend名].busy
– [backend名].fail
– [backend名].fail_eacces
– [backend名].fail_eaddrnotavail
– [backend名].fail_econnrefused
– [backend名].fail_enetunreach
– [backend名].fail_etimedout
– [backend名].fail_other
– [backend名].helddown

以下6.0.2のみ対応
– wrk.client_resp_500
– wrk.ws_backend_overflow
– wrk.ws_client_overflow
– wrk.ws_thread_overflow
– wrk.ws_session_overflow

例えばfail_eaddrnotavail(EADDRNOTAVAIL)であればエフェメラルポートの確保に失敗してるんだろうということで設定の見直しなどができると思います。
また、overflowのカウンタも便利です。
これらはそれぞれ起動パラメータのworkspace_clientなどと一致するので、カウンタが上がっていたらチューニングを行うと良いでしょう。

その他変更について

director周りにAPIが変更されているため一部vmodは対応が必要
通常のVMODであれば再コンパイルだけで動くと思いますが、director周りを触るvmodの場合は対応が必須です。
アップデートする前に対応しているか確認すると良いでしょう。

バグ修正について

6.0.2/6.1.1は大量のバグ修正が含まれます。
また、毎回言ってるのですがHTTP/2で重要なバグフィックスが2件あります(他にもあるんですが特に重要・・・)

HTTP/2利用時にスレッドリークが起こるの修正(#2623)

閉じられたストリーム宛てにPRIORITYフレームが届くとプロトコルエラーとして扱っていたのを修正(#2775)

http/2のexperimentalはまだ外れてないのですが、さすがにもう強烈なバグは出尽くしたんじゃないかなとか・・・考えてます。

そのほか

公式パッケージでUbuntu18.04(bionic)向けのパッケージが追加
varnishtestでhaproxyを扱うことが可能に
おそらくvarnishtestは将来的にプロジェクトが分離され汎用的になるでしょう(Divorcing varnishtest from varnish-cache)
既にレポジトリもあります
VTest


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月 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)

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


5月 052017
 

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

今回の変更内容は基本的にバグフィックスと5.1.1/5.1.2で追加された一部機能の取り込みになります。

追加された機能

すべて5.1.1/5.1.2で追加された機能です。
vmod_stdにgetenv(STRING)の追加
varnishtestにsetenvの追加
vsl-queryでvxid指定が可能に

バグ修正

Ageレスポンスヘッダの数値が実経過時間より大きな数値になるケースがあったのを修正しました(#2216)
Ageレスポンスヘッダの小数点の丸めをsprintfの%.0fで行っていたため、実際の経過時間より大きな数値になることがあったんですが
切り捨て(floor)を行うようにした感じです。

std.querysortでワークスペースが足りない際にpanicしてたのを修正しました(#2233)

稀にVSLを利用するクライアントが初期化に失敗するケースがあるのを修正しました(#2241)
あくまでに初期化時(VSM_Init)のみなので稼働中のクライアントの場合には問題ありません。
自分もVSLを利用するクライアントを作ったり使ってますが遭遇したことがないのでかなり稀だと思います。

クライアントワークスペースの空き容量が少ない場合にチャンク転送を行った場合にpanicしていたのを修正しました(#2275)

動的バックエンドを利用している場合で稀に子プロセスが再起動するのを修正しました(#2295)


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を購入するのをぜひ検討してみてください