Skip to content

ngx_mruby v2.7.4

ngx_mruby 2.7.4 is a maintenance release of the 2.x line.

It fixes a security issue of the stream module, with its advisory (GHSA-xjh6-6vgm-56wj, Moderate): entries 1 and 2 under “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

Section titled “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 and Nginx::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 without proxy_protocol, and after the header PROXY UNKNOWN (version 1) or a version 2 header with the command LOCAL.
  • Now: the methods return nil for 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, and nil tells it apart from a connection with one. In HTTP code, Nginx::Var#proxy_protocol_addr returns nil in the same case.
    • What the documentation promised: the methods were not documented. docs/class_and_method/README.md describes them now.
  • Affected: mruby_stream and mruby_stream_code code 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 handles nil, 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

Section titled “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 with proxy_protocol, bytes that the client sent during the sleep could end the session (status 400 in the access log). When worker_shutdown_timeout expired 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. When worker_shutdown_timeout expires 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 at worker_shutdown_timeout keeps 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.
  • Affected: stream servers whose mruby_stream or mruby_stream_code code calls Nginx::Stream::Async.sleep on 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) with worker_shutdown_timeout while 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_timeout does not end a UDP session whose code sleeps.

None.

None.

  • Tests: test/t/cases/stream_proxy_protocol_wait.rb with test/conf/conf.d/stream/48-proxy-protocol-wait.conf (port 12384), and the mode stream_master of test/t/cases/_second_instance.rb, a second nginx with a master process and one worker on ports 12385 to 12388. test/t/cases/_tcp_client.rb can write a second payload 0.3 seconds after the first.
  • CI: a ready pull request that changes the code or the tests runs four cells of the build matrix of test.yml, not every cell (the 16 combinations of nginx version, link mode and --with-openssl-src, and the sanitizer cell). The four cells are nginx 1.31.8 with the static module and --with-openssl-src, nginx 1.30.6 with the dynamic module, the sanitizer cell (AddressSanitizer and UndefinedBehaviorSanitizer) and the cell under valgrind; the last two build nginx 1.31.8 with the static module. Every cell runs in the nightly run under valgrind, on the push of a tag, on a pull request with the new label full-matrix (the check pull request before a tag), and on “Run workflow” without the input base or with the input valgrind. A push to a v*.x branch runs no build, as a push to master or next does not. test/ci/changes.sh, the same script as on next again, takes the run as options (--label, --draft, --push, --tag, --dispatch, --valgrind) and gives build standard, full or none (“Which jobs a run starts” in docs/test/README.md) (#704)
  • No directive was added, renamed or removed. Rebuild the module with this release the way you built 2.7.3, and replace the binary or the dynamic module as usual.

Check these before upgrading. Each line is the “What to do” of its entry above.

  • Nginx::Stream::Connection.proxy_protocol_addr and proxy_protocol_ip: nothing for code that compares the result with a String. Code that calls a String method on the result handles nil, for example with to_s (entry 1).
  • The reads of a TCP stream session during Nginx::Stream::Async.sleep: nothing (entry 2).