ngx_mruby v2.7.4
ngx_mruby 2.7.4は、2.x系列のメンテナンスリリースです。
streamモジュールのセキュリティ問題を、アドバイザリGHSA-xjh6-6vgm-56wjとともに修正しました。深刻度はModerateです。「アップグレードの前に読む挙動の変更」の項目1と項目2です。
アップグレードの前に読む挙動の変更
Section titled “アップグレードの前に読む挙動の変更”1. Nginx::Stream::Connection.proxy_protocol_addrとproxy_protocol_ipは、PROXYの送信元のアドレスが無い接続でnilを返します
Section titled “1. Nginx::Stream::Connection.proxy_protocol_addrとproxy_protocol_ipは、PROXYの送信元のアドレスが無い接続でnilを返します”- 変更前:nginx 1.17.6以降では、
Nginx::Stream::Connection.proxy_protocol_addrとNginx::Stream::Connection.proxy_protocol_ipは、クラスメソッドでもインスタンスメソッドでも、PROXYプロトコルのヘッダーから得た送信元のアドレスが無い接続で値を返しませんでした。そのような接続は、proxy_protocolの無いlistenの接続と、バージョン1のヘッダーPROXY UNKNOWNか、コマンドがLOCALのバージョン2のヘッダーを受けた接続です。 - 変更後:これらのメソッドは、そのような接続で
nilを返します。アドレスを持つヘッダーの後では、これまでどおり送信元のアドレスを返します。 - 変更の理由:
- 不具合:「変更前」に書いたとおり、これらのメソッドは、そのような接続で値を返しませんでした。アドバイザリGHSA-xjh6-6vgm-56wjが、不具合、影響を受けるバージョン、条件を説明しています。
- 前の挙動を残せなかった理由:そのような接続には、返すアドレスがありません。
nilを返すと、アドレスのある接続と区別できます。HTTPのコードでは、Nginx::Var#proxy_protocol_addrが同じ場合にnilを返します。 - ドキュメントが示していた挙動:これらのメソッドには、ドキュメントがありませんでした。いまはdocs/class_
and_ が説明しています。method/README.md
- 影響の範囲:PROXYの送信元のアドレスが無い接続で、これらのメソッドを呼ぶ
mruby_streamとmruby_stream_codeのコードです。 - 必要な対応:結果をStringと比べるコードでは、対応は要りません。結果にStringのメソッドを呼ぶコードでは、
to_sなどでnilを扱ってください。
2. TCPのstreamのセッションのコードがsleepしている間、nginxはクライアントから何も読まず、worker_shutdown_timeoutでセッションが終わります
Section titled “2. TCPのstreamのセッションのコードがsleepしている間、nginxはクライアントから何も読まず、worker_shutdown_timeoutでセッションが終わります”- 変更前:TCPのセッションのコードが
Nginx::Stream::Async.sleepで待っている間、接続の読み込みのイベントは、コードが動く前と同じに扱われていました。proxy_protocolを付けたlistenでは、sleepの間にクライアントが送ったバイトでセッションが終わることがあり、アクセスログのステータスは400でした。sleepの間にworker_shutdown_timeoutが過ぎても、セッションはそのときに終わりませんでした。 - 変更後:コードがsleepしている間、nginxはクライアントから何も読みません。その間にクライアントが送ったバイトは、コードの後のフェーズのために残ります。
proxy_passは、そのバイトをupstreamへ送ります。sleepの間にクライアントが接続を閉じると、proxy_protocolの無いlistenと同じく、コードの後のフェーズがそれを検出します。sleepの間にworker_shutdown_timeoutが過ぎると、セッションは終わり、コードはもう一度動きません。 - 変更の理由:
- 不具合:「変更前」に書いたとおり、sleepの間の読み込みのイベントが、コードが動く前と同じに扱われていました。アドバイザリGHSA-xjh6-6vgm-56wjが、不具合、影響を受けるバージョン、条件を説明しています。
- 前の挙動を残せなかった理由:ヘッダーの後のバイトは、後のフェーズが運ぶプロトコルのものです。
worker_shutdown_timeoutで終わらないセッションは、ディレクティブが決めた時間を過ぎても古いワーカープロセスを残します。 - ドキュメントが示していた挙動:docs/class_
and_ は、sleepの間の読み込みについて何も書いていませんでした。nginxのmethod/README.md worker_shutdown_timeoutのドキュメントは、タイムアウトが過ぎるとnginxが開いている接続を閉じようとすると書いています。
- 影響の範囲:TCPのlistenで、
mruby_streamかmruby_stream_codeのコードがNginx::Stream::Async.sleepを呼ぶstreamのサーバーです。proxy_protocolを付けたlistenでsleepの間にバイトを送るか接続を閉じるクライアントと、コードがsleepしている間にworker_shutdown_timeoutを付けて終わるワーカーが影響を受けます。ワーカーが終わるのは、リロード、アップグレード、停止のときです。 - 必要な対応:ありません。コードがsleepしないセッションと同じく、コードの後のフェーズがクライアントのバイトを受け取ります。
- このリリースで変わらない挙動:UDPのセッションです。sleepの間にクライアントから届くデータグラムは、2.7.3と同じく、1つごとにコードをもう一度動かします。
worker_shutdown_timeoutは、コードがsleepしているUDPのセッションを終わらせません。
ありません。
不具合の修正
Section titled “不具合の修正”ありません。
ビルドとテストの変更
Section titled “ビルドとテストの変更”- テスト:
test/t/cases/stream_proxy_protocol_wait.rbとtest/conf/conf.d/stream/48-proxy-protocol-wait.confを加えました。ポートは12384です。test/t/cases/_second_instance.rbには、マスタープロセスと1つのワーカープロセスを持つ2つ目のnginxを起動するモードstream_masterを加えました。ポートは12385から12388です。test/t/cases/_tcp_client.rbは、最初のペイロードの0.3秒後に2つ目のペイロードを書けます。 - CI:コードかテストを変えるreadyのプルリクエストは、
test.ymlのbuildのマトリクスのうち、4つのセルを流し、全部のセルは流しません。全部のセルは、nginxのバージョン、リンクの形、--with-openssl-srcの有無の16の組み合わせと、sanitizerのセルです。4つのセルは、--with-openssl-srcを付けたnginx 1.31.8の静的モジュール、nginx 1.30.6の動的モジュール、sanitizerのセル、valgrindの下のセルです。sanitizerのセルとvalgrindの下のセルは、nginx 1.31.8の静的モジュールをビルドします。sanitizerのセルは、AddressSanitizerとUndefinedBehaviorSanitizerを付けてビルドします。全部のセルを流すのは、valgrindの下の夜間の実行、タグのpush、新しいラベルfull-matrixを付けたプルリクエスト、入力baseを付けないか入力valgrindを付けた「Run workflow」です。タグの前の点検のプルリクエストは、このラベルを付けます。v*.xのブランチへのpushは、masterとnextへのpushと同じく、ビルドを流しません。test/ci/changes.shは、nextと再び同じスクリプトになり、実行の種類をオプション(--label、--draft、--push、--tag、--dispatch、--valgrind)で受け取り、buildをstandard、full、noneのどれかで出します。説明は、docs/test/README.mdの「Which jobs a run starts」にあります(#704)。
アップグレード
Section titled “アップグレード”- 追加、改名、削除したディレクティブはありません。このリリースで、モジュールをビルドし直してください。2.7.3をビルドしたときと同じ方法でビルドし、いつもどおりnginxのバイナリか動的モジュールを置き換えてください。
アップグレードの前に、次の点を確かめてください。各行は、上の各項目に書いた「必要な対応」です。
Nginx::Stream::Connection.proxy_protocol_addrとproxy_protocol_ip:結果をStringと比べるコードでは、対応は要りません。結果にStringのメソッドを呼ぶコードでは、to_sなどでnilを扱ってください(項目1)。Nginx::Stream::Async.sleepの間のTCPのstreamのセッションの読み込み:対応は要りません(項目2)。