Repository navigation
ngx_mruby v2.7.4
ngx_mruby 2.7.4 is a maintenance release of the 2.x line. It changes two
behaviors of the stream module that an existing configuration or Ruby script
can observe; read "Behavior changes: read before upgrading" in the notes
before you upgrade. No directive was added, renamed or removed: rebuild the
module the way you built 2.7.3 and replace the binary or the dynamic module.
Security
This release fixes a security issue of the stream module, with its advisory:
- Moderate — On a stream
listen ... proxy_protocol, a session whose
code waits inNginx::Stream::Async.sleepnow reads nothing from the
client until the code resumes, andNginx::Stream::Connection.proxy_protocol_addr
returnsnilfor a connection without a PROXY source address (CWE-476, CWE-345).
(GHSA-xjh6-6vgm-56wj)
See entries 1 and 2 of the notes for what changes and what to do (nothing,
for most configurations).
Behavior changes: read before upgrading
1. Nginx::Stream::Connection.proxy_protocol_addr and proxy_protocol_ip return nil for a connection without a PROXY source address
- Before: with nginx 1.17.6 and later,
Nginx::Stream::Connection.proxy_protocol_addr
andNginx::Stream::Connection.proxy_protocol_ip, as class and as instance
methods, did not return a value for a connection that had no source address
from a PROXY protocol header: on a listen withoutproxy_protocol, and
after the headerPROXY UNKNOWN(version 1) or a version 2 header with the
commandLOCAL. - Now: the methods return
nilfor such a connection. After a header that
carries addresses, they return its source address as before. - Why it changed:
- The defect: the methods returned no value for such a connection, as
"Before" describes. The advisory
GHSA-xjh6-6vgm-56wj
describes the defect, the affected versions and the conditions. - Why the old behavior could not be kept: such a connection has no address
to return, andniltells it apart from a connection with one. In HTTP
code,Nginx::Var#proxy_protocol_addrreturnsnilin the same case. - What the documentation promised: the methods were not documented.
docs/class_and_method/README.md
describes them now.
- The defect: the methods returned no value for such a connection, as
- Affected:
mruby_streamandmruby_stream_codecode that calls these
methods on a connection without a PROXY source address. - What to do: nothing for code that compares the result with a String. Code
that calls a String method on the result handlesnil, for example with
to_s.
2. While the code of a TCP stream session sleeps, nginx reads nothing from the client, and worker_shutdown_timeout ends the session
- Before: while the code of a TCP session waited in
Nginx::Stream::Async.sleep, the read events of the connection were
handled as before the code ran. On a listen withproxy_protocol, bytes
that the client sent during the sleep could end the session (status 400 in
the access log). Whenworker_shutdown_timeoutexpired during the sleep,
the session did not end then. - Now: while the code sleeps, nginx reads nothing from the client. The bytes
that the client sends then stay for the phase after the code:proxy_pass
sends them to the upstream. A client that closes the connection during the
sleep is seen by the phase after the code, as on a listen without
proxy_protocol. Whenworker_shutdown_timeoutexpires during the sleep,
the session ends, and the code does not run again. - Why it changed:
- The defect: the read events during the sleep were handled as before the
code ran, as "Before" describes. The advisory
GHSA-xjh6-6vgm-56wj
describes the defect, the affected versions and the conditions. - Why the old behavior could not be kept: the bytes after the header
belong to the protocol that the later phases carry. A session that does
not end atworker_shutdown_timeoutkeeps the old worker process after
the time that the directive sets. - What the documentation promised:
docs/class_and_method/README.md
said nothing about reads during the sleep. nginx's documentation of
worker_shutdown_timeout
says that nginx tries to close the open connections when the timeout
expires.
- The defect: the read events during the sleep were handled as before the
- Affected: stream servers whose
mruby_streamormruby_stream_codecode
callsNginx::Stream::Async.sleepon a TCP listen: the clients that send
bytes or close the connection during the sleep on a listen with
proxy_protocol, and a worker that shuts down (a reload, an upgrade, a
stop) withworker_shutdown_timeoutwhile the code sleeps. - What to do: nothing. The phase after the code gets the bytes of the
client, as on a session whose code does not sleep. - Not changed by this release: a UDP session. Each datagram of its client
during the sleep runs the code again, as in 2.7.3, and
worker_shutdown_timeoutdoes not end a UDP session whose code sleeps.
ngx_mruby 2.7.4は、2.x系列のメンテナンスリリースです。既存の設定やRubyのスクリプトから分かる、streamモジュールの2つの挙動を変えます。アップグレードの前に、ノートの「アップグレードの前に読む挙動の変更」を読んでください。追加、改名、削除したディレクティブはありません。2.7.3をビルドしたときと同じ方法でモジュールをビルドし、バイナリか動的モジュールを置き換えてください。
セキュリティ
このリリースは、streamモジュールのセキュリティ問題を、アドバイザリとともに修正します。
- Moderate — streamの
listen ... proxy_protocolで、コードがNginx::Stream::Async.sleepで待っているセッションは、コードが再開するまでクライアントから何も読まなくなりました。また、Nginx::Stream::Connection.proxy_protocol_addrは、PROXYの送信元のアドレスが無い接続でnilを返します(CWE-476、CWE-345)。(GHSA-xjh6-6vgm-56wj)
何が変わり何をすべきかは、ノートの項目1と項目2にあります。ほとんどの設定では、何もする必要はありません。
アップグレードの前に読む挙動の変更
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でセッションが終わります
- 変更前: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_method/README.mdは、sleepの間の読み込みについて何も書いていませんでした。nginxの
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のセッションを終わらせません。
Fixes
None apart from the two entries above.
Build and test changes
- CI: a ready pull request that changes the code or the tests runs four
cells of the build matrix; every cell runs in the nightly run under
valgrind, on the push of a tag and on a pull request with the label
full-matrix(#704). - Tests:
test/t/cases/stream_proxy_protocol_wait.rb(port 12384) and the
modestream_masteroftest/t/cases/_second_instance.rb(ports 12385 to
12388).
Performance
The callgrind comparison (test/perf/compare.sh) builds nginx without
--with-stream, and the changes since 2.7.3 that reach the built module are
in src/stream/ only, so 2.7.4 and 2.7.3 give the same measured binary. Per
stream session, the fix adds a comparison and two stores when the code of a
TCP session starts to wait, a comparison and a store when it resumes, and a
NULL check in proxy_protocol_addr.
Upgrading
Supported nginx versions: the nginx mainline and stable lines; CI builds nginx
1.31.8, 1.30.6, 1.28.3 and 1.26.3. See "Upgrading" in the notes for the
per-entry "What to do".
Full release notes
The complete, per-entry notes are in the repository:
docs/releases/v2.7.4.md
(English) and
docs/releases/v2.7.4.ja.md
(Japanese).