This is an automated email from the ASF dual-hosted git repository. cmcfarlen pushed a commit to branch 10.0.x in repository https://gitbox.apache.org/repos/asf/trafficserver.git
commit 8dbafecbe0ae217ea31962e841564fc5b69e2635 Author: Masakazu Kitajo <[email protected]> AuthorDate: Wed Apr 3 09:40:14 2024 -0600 proxy.config.http2.max_continuation_frames_per_minute (#11205) This adds the ability to rate limite HTTP/2 CONTINUATION frames per stream per minute. Co-authored-by: Brian Neradt <[email protected]> (cherry picked from commit e0620eb941eab2603b2c230366e0fae5eeb6b57d) --- doc/admin-guide/files/records.yaml.en.rst | 11 +- doc/admin-guide/files/sni.yaml.en.rst | 382 +++++++++++---------- .../statistics/core/http-connection.en.rst | 11 +- include/iocore/net/TLSSNISupport.h | 1 + include/iocore/net/YamlSNIConfig.h | 2 + include/proxy/http2/HTTP2.h | 2 + include/proxy/http2/Http2ConnectionState.h | 12 +- src/iocore/net/SNIActionPerformer.cc | 9 + src/iocore/net/SNIActionPerformer.h | 12 + src/iocore/net/YamlSNIConfig.cc | 7 + src/proxy/http2/HTTP2.cc | 36 +- src/proxy/http2/Http2ConnectionState.cc | 36 +- src/records/RecordsConfig.cc | 2 + 13 files changed, 306 insertions(+), 217 deletions(-) diff --git a/doc/admin-guide/files/records.yaml.en.rst b/doc/admin-guide/files/records.yaml.en.rst index fda3d3686e..6c698dd17e 100644 --- a/doc/admin-guide/files/records.yaml.en.rst +++ b/doc/admin-guide/files/records.yaml.en.rst @@ -4553,8 +4553,15 @@ HTTP/2 Configuration .. ts:cv:: CONFIG proxy.config.http2.max_rst_stream_frames_per_minute INT 200 :reloadable: - Specifies how many RST_STREAM frames |TS| receives for a minute at maximum. - Clients exceeded this limit will be immediately disconnected with an error + Specifies how many RST_STREAM frames |TS| receives per minute at maximum. + Clients exceeding this limit will be immediately disconnected with an error + code of ENHANCE_YOUR_CALM. + +.. ts:cv:: CONFIG proxy.config.http2.max_continuation_frames_per_minute INT 120 + :reloadable: + + Specifies how many CONTINUATION frames |TS| receives per minute at maximum. + Clients exceeding this limit will be immediately disconnected with an error code of ENHANCE_YOUR_CALM. .. ts:cv:: CONFIG proxy.config.http2.min_avg_window_update FLOAT 2560.0 diff --git a/doc/admin-guide/files/sni.yaml.en.rst b/doc/admin-guide/files/sni.yaml.en.rst index b514752063..7be40de811 100644 --- a/doc/admin-guide/files/sni.yaml.en.rst +++ b/doc/admin-guide/files/sni.yaml.en.rst @@ -83,203 +83,207 @@ inbound_port_ranges Inbound The port ranges for the inbound connection i The following fields are the directives that determine the behavior of connections matching the key. -====================================== ========= ======================================================================================== -Key Direction Meaning -====================================== ========= ======================================================================================== -ip_allow Inbound Specify a list of client IP address, subnets, or ranges what are allowed to complete - the connection. This list is comma separated. IPv4 and IPv6 addresses can be specified. - Here is an example list :: - - 192.168.1.0/24,192.168.10.1-192.168.10.4 - - This would allow connections - from clients in the 19.168.1.0 network or in the range from 192.168.10.1 to 192.168.1.4. - - Alternatively, the path to a file containing - the list of IP addresses can be specified in - the form of ``"@path_to_file"``. The IP - addresses in the file can be either - comma-separated or line-separated. If a - given file path does not begin with ``/``, - it must be relative to the Traffic Server - configuration directory. Here is an example - showing this form of the configuration: - - ``ip_allow: "@ip_dir/example.com.ip.txt"`` - -verify_server_policy Outbound One of the values :code:`DISABLED`, :code:`PERMISSIVE`, or :code:`ENFORCED`. - - By default this is :ts:cv:`proxy.config.ssl.client.verify.server.policy`. - This controls how |TS| evaluated the origin certificate. - -verify_server_properties Outbound One of the values :code:`NONE`, :code:`SIGNATURE`, :code:`NAME`, and :code:`ALL` - - By default this is :ts:cv:`proxy.config.ssl.client.verify.server.properties`. - This controls what |TS| checks when evaluating the origin certificate. - -verify_client Outbound One of the values :code:`NONE`, :code:`MODERATE`, or :code:`STRICT`. - If ``NONE`` is specified, |TS| requests no certificate. If ``MODERATE`` is specified - |TS| will verify a certificate that is presented by the client, but it will not - fail the TLS handshake if no certificate is presented. If ``STRICT`` is specified - the client must present a certificate during the TLS handshake. - - By default this is :ts:cv:`proxy.config.ssl.client.certification_level`. - -verify_client_ca_certs Both Specifies an alternate set of certificate authority certs to use to verify the - client cert. The value must be either a file path, or a nested set of key / - value pairs. If the value is a file path, it must specify a file containing the - CA certs. Otherwise, there should be up to two nested pairs. The possible keys - are ``file`` and ``dir``. The value for ``file`` must be a file path for a file - containing CA certs. The value for ``dir`` must be a file path for an OpenSSL - X509 hashed directory containing CA certs. If a given file path does not begin - with ``/`` , it must be relative to the |TS| configuration directory. - ``verify_client_ca_certs`` can only be used with capabilities provided by - OpenSSL 1.0.2 or later. - -host_sni_policy Inbound One of the values :code:`DISABLED`, :code:`PERMISSIVE`, or :code:`ENFORCED`. - - If not specified, the value of :ts:cv:`proxy.config.http.host_sni_policy` is used. - This controls how policy impacting mismatches between host header and SNI values are - dealt with. For details about how this configuration behaves, see the corresponding - :ts:cv:`proxy.config.http.host_sni_policy` :file:`records.yaml` documentation. - - Note that this particular configuration will be inspected at the time the HTTP Host - header field is processed. Further, this policy check will be keyed off of the Host header - field value rather than the SNI in this :file:`sni.yaml` file. This is done because - the Host header field is ultimately the resource that will be retrieved from the - origin and the administrator will intend to guard this resource rather than the SNI, - which a malicious user may alter to some other server value whose policies are more - lenient than the host he is trying to access. - -valid_tls_version_min_in Inbound This specifies the minimum TLS version that will be offered to user agents during - the TLS negotiation. This replaces the global settings in - :ts:cv:`proxy.config.ssl.server.version.min`, - :ts:cv:`proxy.config.ssl.TLSv1`, :ts:cv:`proxy.config.ssl.TLSv1_1`, - :ts:cv:`proxy.config.ssl.TLSv1_2`, and :ts:cv:`proxy.config.ssl.TLSv1_3`. The potential - values are TLSv1, TLSv1_1, TLSv1_2, and TLSv1_3. This key is only valid for OpenSSL - 1.1.0 and later and BoringSSL. Older versions of OpenSSL do not provide a hook early enough to update - the SSL object. It is a syntax error for |TS| built against earlier versions. - -valid_tls_version_max_in Inbound This specifies the minimum TLS version that will be offered to user agents during - the TLS negotiation. This replaces the global settings in - :ts:cv:`proxy.config.ssl.server.version.max`, - :ts:cv:`proxy.config.ssl.TLSv1`, :ts:cv:`proxy.config.ssl.TLSv1_1`, - :ts:cv:`proxy.config.ssl.TLSv1_2`, and :ts:cv:`proxy.config.ssl.TLSv1_3`. The potential - values are TLSv1, TLSv1_1, TLSv1_2, and TLSv1_3. This key is only valid for OpenSSL - 1.1.0 and later and BoringSSL. Older versions of OpenSSL do not provide a hook early enough to update - the SSL object. It is a syntax error for |TS| built against earlier versions. - -valid_tls_versions_in Inbound Deprecated. This specifies the list of TLS protocols that will be offered to user agents during - the TLS negotiation. This replaces the global settings in - :ts:cv:`proxy.config.ssl.TLSv1`, :ts:cv:`proxy.config.ssl.TLSv1_1`, - :ts:cv:`proxy.config.ssl.TLSv1_2`, and :ts:cv:`proxy.config.ssl.TLSv1_3`. The potential - values are TLSv1, TLSv1_1, TLSv1_2, and TLSv1_3. You must list all protocols that |TS| - should offer to the client when using this key. This key is only valid for OpenSSL - 1.1.0 and later and BoringSSL. Older versions of OpenSSL do not provide a hook early enough to update - the SSL object. It is a syntax error for |TS| built against earlier versions. - -client_cert Outbound The file containing the client certificate to use for the outbound connection. - - If this is relative, it is relative to the path in - :ts:cv:`proxy.config.ssl.client.cert.path`. If not set - :ts:cv:`proxy.config.ssl.client.cert.filename` is used. - -client_key Outbound The file containing the client private key that corresponds to the certificate - for the outbound connection. - - If this is relative, it is relative to the path in - :ts:cv:`proxy.config.ssl.client.private_key.path`. If not set, - |TS| tries to use a private key in client_cert. Otherwise, - :ts:cv:`proxy.config.ssl.client.private_key.filename` is used. - -client_sni_policy Outbound Policy of SNI on outbound connection. - - If not specified, the value of :ts:cv:`proxy.config.ssl.client.sni_policy` is used. - -http2 Inbound Indicates whether the H2 protocol should be added to or removed from the - protocol negotiation list. The valid values are :code:`on` or :code:`off`. - -http2_buffer_water_mark Inbound Specifies the high water mark for all HTTP/2 frames on an outgoing connection. - By default this is :ts:cv:`proxy.config.http2.default_buffer_water_mark`. - NOTE: Connection coalescing may prevent this from taking effect. - -http2_initial_window_size_in Inbound Specifies the initial HTTP/2 stream window size for inbound connections that - |TS| as a receiver advertises to the peer. - By default this is :ts:cv:`proxy.config.http2.initial_window_size_in`. - NOTE: Connection coalescing may prevent this from taking effect. - -http2_max_settings_frames_per_minute Inbound Specifies how many SETTINGS frames |TS| receives per minute at maximum. - By default this is :ts:cv:`proxy.config.http2.max_settings_frames_per_minute`. - NOTE: Connection coalescing may prevent this from taking effect. - -http2_max_ping_frames_per_minute Inbound Specifies how many PING frames |TS| receives per minute at maximum. - By default this is :ts:cv:`proxy.config.http2.max_ping_frames_per_minute`. - NOTE: Connection coalescing may prevent this from taking effect. - -http2_max_priority_frames_per_minute Inbound Specifies how many PRIORITY frames |TS| receives per minute at maximum. - By default this is :ts:cv:`proxy.config.http2.max_priority_frames_per_minute`. - NOTE: Connection coalescing may prevent this from taking effect. - -http2_max_rst_stream_frames_per_minute Inbound Specifies how many RST_STREAM frames |TS| receives per minute at maximum. - By default this is :ts:cv:`proxy.config.http2.max_rst_stream_frames_per_minute`. - NOTE: Connection coalescing may prevent this from taking effect. - -quic Inbound Indicates whether QUIC connections should be accepted. The valid values are :code:`on` or - :code:`off`. Note that this is a more specific setting to configure QUIC availability per server - name. More broadly, you will also need to configure :ts:cv:`proxy.config.http.server_ports` to - open ports for QUIC. - -tunnel_route Inbound Destination as an FQDN and port, separated by a colon ``:``. - Match group number can be specified by ``$N`` where N should refer to a specified group - in the FQDN, ``tunnel_route: $1.domain``. - - This will forward all traffic to the specified destination without first terminating - the incoming TLS connection. - - The destination port can be designated with the literal string - ``{inbound_local_port}`` to specify that |TS| should connect to the tunnel route's - port on the same destination port that the incoming connection had. For - example, if a client connected to |TS| on port ``4443`` and the associated - ``tunnel_route`` had ``{inbound_local_port}`` for the port designation, then |TS| - will connect to the specified host using port ``4443``. - - The destination port can also be designated with the literal string - ``{proxy_protocol_port}``, in which case |TS| will connect to the specified host on - the port that was specified by the incoming Proxy Protocol payload. See :ref:`Proxy - Protocol <proxy-protocol>` for more information on Proxy Protocol and how it is - configured for |TS|. - - Note that only one of the ``{inbound_local_port}`` and ``{proxy_protocol_port}`` literal - strings can be specified. The match group number can be used in combination with either - one of those. - - For each of these tunnel targets, unless the port is explicitly specified in the target - (e.g., if the port is derived from the Proxy Protocol header), the port must be - specified in the :ts:cv:`proxy.config.http.connect_ports` configuration in order for - the tunnel to succeed. +======================================== ========= ======================================================================================== +Key Direction Meaning +======================================== ========= ======================================================================================== +ip_allow Inbound Specify a list of client IP address, subnets, or ranges what are allowed to complete + the connection. This list is comma separated. IPv4 and IPv6 addresses can be specified. + Here is an example list :: + + 192.168.1.0/24,192.168.10.1-192.168.10.4 + + This would allow connections + from clients in the 19.168.1.0 network or in the range from 192.168.10.1 to 192.168.1.4. + + Alternatively, the path to a file containing + the list of IP addresses can be specified in + the form of ``"@path_to_file"``. The IP + addresses in the file can be either + comma-separated or line-separated. If a + given file path does not begin with ``/``, + it must be relative to the Traffic Server + configuration directory. Here is an example + showing this form of the configuration: + + ``ip_allow: "@ip_dir/example.com.ip.txt"`` + +verify_server_policy Outbound One of the values :code:`DISABLED`, :code:`PERMISSIVE`, or :code:`ENFORCED`. + + By default this is :ts:cv:`proxy.config.ssl.client.verify.server.policy`. + This controls how |TS| evaluated the origin certificate. + +verify_server_properties Outbound One of the values :code:`NONE`, :code:`SIGNATURE`, :code:`NAME`, and :code:`ALL` + + By default this is :ts:cv:`proxy.config.ssl.client.verify.server.properties`. + This controls what |TS| checks when evaluating the origin certificate. + +verify_client Outbound One of the values :code:`NONE`, :code:`MODERATE`, or :code:`STRICT`. + If ``NONE`` is specified, |TS| requests no certificate. If ``MODERATE`` is specified + |TS| will verify a certificate that is presented by the client, but it will not + fail the TLS handshake if no certificate is presented. If ``STRICT`` is specified + the client must present a certificate during the TLS handshake. + + By default this is :ts:cv:`proxy.config.ssl.client.certification_level`. + +verify_client_ca_certs Both Specifies an alternate set of certificate authority certs to use to verify the + client cert. The value must be either a file path, or a nested set of key / + value pairs. If the value is a file path, it must specify a file containing the + CA certs. Otherwise, there should be up to two nested pairs. The possible keys + are ``file`` and ``dir``. The value for ``file`` must be a file path for a file + containing CA certs. The value for ``dir`` must be a file path for an OpenSSL + X509 hashed directory containing CA certs. If a given file path does not begin + with ``/`` , it must be relative to the |TS| configuration directory. + ``verify_client_ca_certs`` can only be used with capabilities provided by + OpenSSL 1.0.2 or later. + +host_sni_policy Inbound One of the values :code:`DISABLED`, :code:`PERMISSIVE`, or :code:`ENFORCED`. + + If not specified, the value of :ts:cv:`proxy.config.http.host_sni_policy` is used. + This controls how policy impacting mismatches between host header and SNI values are + dealt with. For details about how this configuration behaves, see the corresponding + :ts:cv:`proxy.config.http.host_sni_policy` :file:`records.yaml` documentation. + + Note that this particular configuration will be inspected at the time the HTTP Host + header field is processed. Further, this policy check will be keyed off of the Host header + field value rather than the SNI in this :file:`sni.yaml` file. This is done because + the Host header field is ultimately the resource that will be retrieved from the + origin and the administrator will intend to guard this resource rather than the SNI, + which a malicious user may alter to some other server value whose policies are more + lenient than the host he is trying to access. + +valid_tls_version_min_in Inbound This specifies the minimum TLS version that will be offered to user agents during + the TLS negotiation. This replaces the global settings in + :ts:cv:`proxy.config.ssl.server.version.min`, + :ts:cv:`proxy.config.ssl.TLSv1`, :ts:cv:`proxy.config.ssl.TLSv1_1`, + :ts:cv:`proxy.config.ssl.TLSv1_2`, and :ts:cv:`proxy.config.ssl.TLSv1_3`. The potential + values are TLSv1, TLSv1_1, TLSv1_2, and TLSv1_3. This key is only valid for OpenSSL + 1.1.0 and later and BoringSSL. Older versions of OpenSSL do not provide a hook early enough to update + the SSL object. It is a syntax error for |TS| built against earlier versions. + +valid_tls_version_max_in Inbound This specifies the minimum TLS version that will be offered to user agents during + the TLS negotiation. This replaces the global settings in + :ts:cv:`proxy.config.ssl.server.version.max`, + :ts:cv:`proxy.config.ssl.TLSv1`, :ts:cv:`proxy.config.ssl.TLSv1_1`, + :ts:cv:`proxy.config.ssl.TLSv1_2`, and :ts:cv:`proxy.config.ssl.TLSv1_3`. The potential + values are TLSv1, TLSv1_1, TLSv1_2, and TLSv1_3. This key is only valid for OpenSSL + 1.1.0 and later and BoringSSL. Older versions of OpenSSL do not provide a hook early enough to update + the SSL object. It is a syntax error for |TS| built against earlier versions. + +valid_tls_versions_in Inbound Deprecated. This specifies the list of TLS protocols that will be offered to user agents during + the TLS negotiation. This replaces the global settings in + :ts:cv:`proxy.config.ssl.TLSv1`, :ts:cv:`proxy.config.ssl.TLSv1_1`, + :ts:cv:`proxy.config.ssl.TLSv1_2`, and :ts:cv:`proxy.config.ssl.TLSv1_3`. The potential + values are TLSv1, TLSv1_1, TLSv1_2, and TLSv1_3. You must list all protocols that |TS| + should offer to the client when using this key. This key is only valid for OpenSSL + 1.1.0 and later and BoringSSL. Older versions of OpenSSL do not provide a hook early enough to update + the SSL object. It is a syntax error for |TS| built against earlier versions. + +client_cert Outbound The file containing the client certificate to use for the outbound connection. + + If this is relative, it is relative to the path in + :ts:cv:`proxy.config.ssl.client.cert.path`. If not set + :ts:cv:`proxy.config.ssl.client.cert.filename` is used. + +client_key Outbound The file containing the client private key that corresponds to the certificate + for the outbound connection. + + If this is relative, it is relative to the path in + :ts:cv:`proxy.config.ssl.client.private_key.path`. If not set, + |TS| tries to use a private key in client_cert. Otherwise, + :ts:cv:`proxy.config.ssl.client.private_key.filename` is used. + +client_sni_policy Outbound Policy of SNI on outbound connection. + + If not specified, the value of :ts:cv:`proxy.config.ssl.client.sni_policy` is used. + +http2 Inbound Indicates whether the H2 protocol should be added to or removed from the + protocol negotiation list. The valid values are :code:`on` or :code:`off`. + +http2_buffer_water_mark Inbound Specifies the high water mark for all HTTP/2 frames on an outgoing connection. + By default this is :ts:cv:`proxy.config.http2.default_buffer_water_mark`. + NOTE: Connection coalescing may prevent this from taking effect. + +http2_initial_window_size_in Inbound Specifies the initial HTTP/2 stream window size for inbound connections that + |TS| as a receiver advertises to the peer. + By default this is :ts:cv:`proxy.config.http2.initial_window_size_in`. + NOTE: Connection coalescing may prevent this from taking effect. + +http2_max_settings_frames_per_minute Inbound Specifies how many SETTINGS frames |TS| receives per minute at maximum. + By default this is :ts:cv:`proxy.config.http2.max_settings_frames_per_minute`. + NOTE: Connection coalescing may prevent this from taking effect. + +http2_max_ping_frames_per_minute Inbound Specifies how many PING frames |TS| receives per minute at maximum. + By default this is :ts:cv:`proxy.config.http2.max_ping_frames_per_minute`. + NOTE: Connection coalescing may prevent this from taking effect. + +http2_max_priority_frames_per_minute Inbound Specifies how many PRIORITY frames |TS| receives per minute at maximum. + By default this is :ts:cv:`proxy.config.http2.max_priority_frames_per_minute`. + NOTE: Connection coalescing may prevent this from taking effect. + +http2_max_rst_stream_frames_per_minute Inbound Specifies how many RST_STREAM frames |TS| receives per minute at maximum. + By default this is :ts:cv:`proxy.config.http2.max_rst_stream_frames_per_minute`. + NOTE: Connection coalescing may prevent this from taking effect. + +http2_max_continuation_frames_per_minute Inbound Specifies how many CONTINUATION frames |TS| receives per minute at maximum. + By default this is :ts:cv:`proxy.config.http2.max_continuation_frames_per_minute`. + NOTE: Connection coalescing may prevent this from taking effect. + +quic Inbound Indicates whether QUIC connections should be accepted. The valid values are :code:`on` or + :code:`off`. Note that this is a more specific setting to configure QUIC availability per server + name. More broadly, you will also need to configure :ts:cv:`proxy.config.http.server_ports` to + open ports for QUIC. + +tunnel_route Inbound Destination as an FQDN and port, separated by a colon ``:``. + Match group number can be specified by ``$N`` where N should refer to a specified group + in the FQDN, ``tunnel_route: $1.domain``. + + This will forward all traffic to the specified destination without first terminating + the incoming TLS connection. + + The destination port can be designated with the literal string + ``{inbound_local_port}`` to specify that |TS| should connect to the tunnel route's + port on the same destination port that the incoming connection had. For + example, if a client connected to |TS| on port ``4443`` and the associated + ``tunnel_route`` had ``{inbound_local_port}`` for the port designation, then |TS| + will connect to the specified host using port ``4443``. + + The destination port can also be designated with the literal string + ``{proxy_protocol_port}``, in which case |TS| will connect to the specified host on + the port that was specified by the incoming Proxy Protocol payload. See :ref:`Proxy + Protocol <proxy-protocol>` for more information on Proxy Protocol and how it is + configured for |TS|. + + Note that only one of the ``{inbound_local_port}`` and ``{proxy_protocol_port}`` literal + strings can be specified. The match group number can be used in combination with either + one of those. + + For each of these tunnel targets, unless the port is explicitly specified in the target + (e.g., if the port is derived from the Proxy Protocol header), the port must be + specified in the :ts:cv:`proxy.config.http.connect_ports` configuration in order for + the tunnel to succeed. -forward_route Inbound Destination as an FQDN and port, separated by a colon ``:``. +forward_route Inbound Destination as an FQDN and port, separated by a colon ``:``. - This is similar to tunnel_route, but it terminates the TLS connection and forwards the - decrypted traffic. |TS| will not interpret the decrypted data, so the contents do not - need to be HTTP. + This is similar to tunnel_route, but it terminates the TLS connection and forwards the + decrypted traffic. |TS| will not interpret the decrypted data, so the contents do not + need to be HTTP. -partial_blind_route Inbound Destination as an FQDN and port, separated by a colon ``:``. +partial_blind_route Inbound Destination as an FQDN and port, separated by a colon ``:``. - This is similar to forward_route in that |TS| terminates the incoming TLS connection. - In addition partial_blind_route creates a new TLS connection to the specified origin. - It does not interpret the decrypted data before passing it to the origin TLS - connection, so the contents do not need to be HTTP. + This is similar to forward_route in that |TS| terminates the incoming TLS connection. + In addition partial_blind_route creates a new TLS connection to the specified origin. + It does not interpret the decrypted data before passing it to the origin TLS + connection, so the contents do not need to be HTTP. -tunnel_alpn Inbound List of ALPN Protocol Ids for Partial Blind Tunnel. +tunnel_alpn Inbound List of ALPN Protocol Ids for Partial Blind Tunnel. - ATS negotiates application protocol with the client on behalf of the origin server. - This only works with ``partial_blind_route``. + ATS negotiates application protocol with the client on behalf of the origin server. + This only works with ``partial_blind_route``. -server_max_early_data Inbound Specifies the maximum amount of early data in bytes that is permitted to be sent on a single connection. +server_max_early_data Inbound Specifies the maximum amount of early data in bytes that is permitted to be sent on a single connection. - If not specified, the value of :ts:cv:`proxy.config.ssl.server.max_early_data` is used. -====================================== ========= ======================================================================================== + If not specified, the value of :ts:cv:`proxy.config.ssl.server.max_early_data` is used. +======================================== ========= ======================================================================================== Pre-warming TLS Tunnel ---------------------- diff --git a/doc/admin-guide/monitoring/statistics/core/http-connection.en.rst b/doc/admin-guide/monitoring/statistics/core/http-connection.en.rst index cd9a554e0b..80df47cf5b 100644 --- a/doc/admin-guide/monitoring/statistics/core/http-connection.en.rst +++ b/doc/admin-guide/monitoring/statistics/core/http-connection.en.rst @@ -306,10 +306,17 @@ HTTP/2 .. ts:stat:: global proxy.process.http2.max_rst_stream_frames_per_minute_exceeded integer :type: counter - Represents the total number of closed HTTP/2 connections for exceeding the - maximum allowed number of rst_stream frames per minute limit which is configured by + Represents the total number of HTTP/2 connections closed for exceeding the + maximum allowed number of ``RST_STREAM`` frames per minute limit which is configured by :ts:cv:`proxy.config.http2.max_rst_stream_frames_per_minute`. +.. ts:stat:: global proxy.process.http2.max_continuation_frames_per_minute_exceeded integer + :type: counter + + Represents the total number of HTTP/2 connections closed for exceeding the + maximum allowed number of ``CONTINUATION`` frames per minute limit which is + configured by :ts:cv:`proxy.config.http2.max_continuation_frames_per_minute`. + .. ts:stat:: global proxy.process.http2.insufficient_avg_window_update integer :type: counter diff --git a/include/iocore/net/TLSSNISupport.h b/include/iocore/net/TLSSNISupport.h index a4a3de350c..8fcff07fa2 100644 --- a/include/iocore/net/TLSSNISupport.h +++ b/include/iocore/net/TLSSNISupport.h @@ -65,6 +65,7 @@ public: std::optional<uint32_t> http2_max_ping_frames_per_minute; std::optional<uint32_t> http2_max_priority_frames_per_minute; std::optional<uint32_t> http2_max_rst_stream_frames_per_minute; + std::optional<uint32_t> http2_max_continuation_frames_per_minute; std::optional<std::string_view> outbound_sni_policy; } hints_from_sni; diff --git a/include/iocore/net/YamlSNIConfig.h b/include/iocore/net/YamlSNIConfig.h index aa5ec2008d..40978f9a82 100644 --- a/include/iocore/net/YamlSNIConfig.h +++ b/include/iocore/net/YamlSNIConfig.h @@ -66,6 +66,7 @@ TSDECL(http2_max_settings_frames_per_minute); TSDECL(http2_max_ping_frames_per_minute); TSDECL(http2_max_priority_frames_per_minute); TSDECL(http2_max_rst_stream_frames_per_minute); +TSDECL(http2_max_continuation_frames_per_minute); TSDECL(quic); TSDECL(host_sni_policy); TSDECL(http2_initial_window_size_in); @@ -111,6 +112,7 @@ struct YamlSNIConfig { std::optional<int> http2_max_ping_frames_per_minute; std::optional<int> http2_max_priority_frames_per_minute; std::optional<int> http2_max_rst_stream_frames_per_minute; + std::optional<int> http2_max_continuation_frames_per_minute; uint32_t server_max_early_data = 0; std::optional<int> http2_initial_window_size_in; diff --git a/include/proxy/http2/HTTP2.h b/include/proxy/http2/HTTP2.h index fbaa579e7c..8001c9787d 100644 --- a/include/proxy/http2/HTTP2.h +++ b/include/proxy/http2/HTTP2.h @@ -103,6 +103,7 @@ struct Http2StatsBlock { Metrics::Counter::AtomicType *max_ping_frames_per_minute_exceeded; Metrics::Counter::AtomicType *max_priority_frames_per_minute_exceeded; Metrics::Counter::AtomicType *max_rst_stream_frames_per_minute_exceeded; + Metrics::Counter::AtomicType *max_continuation_frames_per_minute_exceeded; Metrics::Counter::AtomicType *insufficient_avg_window_update; Metrics::Counter::AtomicType *max_concurrent_streams_exceeded_in; Metrics::Counter::AtomicType *max_concurrent_streams_exceeded_out; @@ -427,6 +428,7 @@ public: static uint32_t max_ping_frames_per_minute; static uint32_t max_priority_frames_per_minute; static uint32_t max_rst_stream_frames_per_minute; + static uint32_t max_continuation_frames_per_minute; static float min_avg_window_update; static uint32_t con_slow_log_threshold; static uint32_t stream_slow_log_threshold; diff --git a/include/proxy/http2/Http2ConnectionState.h b/include/proxy/http2/Http2ConnectionState.h index 4952f979fc..4f992de488 100644 --- a/include/proxy/http2/Http2ConnectionState.h +++ b/include/proxy/http2/Http2ConnectionState.h @@ -199,6 +199,8 @@ public: uint32_t get_received_priority_frame_count(); void increment_received_rst_stream_frame_count(); uint32_t get_received_rst_stream_frame_count(); + void increment_received_continuation_frame_count(); + uint32_t get_received_continuation_frame_count(); ssize_t get_peer_rwnd() const; Http2ErrorCode increment_peer_rwnd(size_t amount); @@ -333,6 +335,7 @@ private: FrequencyCounter _received_ping_frame_counter; FrequencyCounter _received_priority_frame_counter; FrequencyCounter _received_rst_stream_frame_counter; + FrequencyCounter _received_continuation_frame_counter; /** Records the various settings for each SETTINGS frame that we've sent. * @@ -402,10 +405,11 @@ private: Event *zombie_event = nullptr; Event *retransmit_event = nullptr; - uint32_t configured_max_settings_frames_per_minute = 0; - uint32_t configured_max_ping_frames_per_minute = 0; - uint32_t configured_max_priority_frames_per_minute = 0; - uint32_t configured_max_rst_stream_frames_per_minute = 0; + uint32_t configured_max_settings_frames_per_minute = 0; + uint32_t configured_max_ping_frames_per_minute = 0; + uint32_t configured_max_priority_frames_per_minute = 0; + uint32_t configured_max_rst_stream_frames_per_minute = 0; + uint32_t configured_max_continuation_frames_per_minute = 0; }; /////////////////////////////////////////////// diff --git a/src/iocore/net/SNIActionPerformer.cc b/src/iocore/net/SNIActionPerformer.cc index 34f35e533b..a9130b8673 100644 --- a/src/iocore/net/SNIActionPerformer.cc +++ b/src/iocore/net/SNIActionPerformer.cc @@ -134,6 +134,15 @@ HTTP2MaxRstStreamFramesPerMinute::SNIAction(SSL &ssl, const Context &ctx) const return SSL_TLSEXT_ERR_OK; } +int +HTTP2MaxContinuationFramesPerMinute::SNIAction(SSL &ssl, const Context &ctx) const +{ + if (auto snis = TLSSNISupport::getInstance(&ssl)) { + snis->hints_from_sni.http2_max_continuation_frames_per_minute = value; + } + return SSL_TLSEXT_ERR_OK; +} + TunnelDestination::TunnelDestination(const std::string_view &dest, SNIRoutingType type, YamlSNIConfig::TunnelPreWarm prewarm, const std::vector<int> &alpn) : destination(dest), type(type), tunnel_prewarm(prewarm), alpn_ids(alpn) diff --git a/src/iocore/net/SNIActionPerformer.h b/src/iocore/net/SNIActionPerformer.h index a6654f3117..5b7604318a 100644 --- a/src/iocore/net/SNIActionPerformer.h +++ b/src/iocore/net/SNIActionPerformer.h @@ -139,6 +139,18 @@ private: int value = -1; }; +class HTTP2MaxContinuationFramesPerMinute : public ActionItem +{ +public: + HTTP2MaxContinuationFramesPerMinute(int value) : value(value) {} + ~HTTP2MaxContinuationFramesPerMinute() override {} + + int SNIAction(SSL &ssl, const Context &ctx) const override; + +private: + int value = -1; +}; + class TunnelDestination : public ActionItem { // ID of the configured variable. This will be used to know which function diff --git a/src/iocore/net/YamlSNIConfig.cc b/src/iocore/net/YamlSNIConfig.cc index 26d12b6e3a..a742abdde2 100644 --- a/src/iocore/net/YamlSNIConfig.cc +++ b/src/iocore/net/YamlSNIConfig.cc @@ -175,6 +175,9 @@ YamlSNIConfig::Item::populate_sni_actions(action_vector_t &actions) if (http2_max_rst_stream_frames_per_minute.has_value()) { actions.push_back(std::make_unique<HTTP2MaxRstStreamFramesPerMinute>(http2_max_rst_stream_frames_per_minute.value())); } + if (http2_max_continuation_frames_per_minute.has_value()) { + actions.push_back(std::make_unique<HTTP2MaxContinuationFramesPerMinute>(http2_max_continuation_frames_per_minute.value())); + } actions.push_back(std::make_unique<ServerMaxEarlyData>(server_max_early_data)); actions.push_back(std::make_unique<SNI_IpAllow>(ip_allow, fqdn)); @@ -222,6 +225,7 @@ std::set<std::string> valid_sni_config_keys = {TS_fqdn, TS_http2_max_ping_frames_per_minute, TS_http2_max_priority_frames_per_minute, TS_http2_max_rst_stream_frames_per_minute, + TS_http2_max_continuation_frames_per_minute, TS_quic, TS_ip_allow, #if TS_USE_HELLO_CB || defined(OPENSSL_IS_BORINGSSL) @@ -277,6 +281,9 @@ template <> struct convert<YamlSNIConfig::Item> { if (node[TS_http2_max_rst_stream_frames_per_minute]) { item.http2_max_rst_stream_frames_per_minute = node[TS_http2_max_rst_stream_frames_per_minute].as<int>(); } + if (node[TS_http2_max_continuation_frames_per_minute]) { + item.http2_max_continuation_frames_per_minute = node[TS_http2_max_continuation_frames_per_minute].as<int>(); + } if (node[TS_quic]) { item.offer_quic = node[TS_quic].as<bool>(); } diff --git a/src/proxy/http2/HTTP2.cc b/src/proxy/http2/HTTP2.cc index 3bd504ee62..abfd85f345 100644 --- a/src/proxy/http2/HTTP2.cc +++ b/src/proxy/http2/HTTP2.cc @@ -482,22 +482,23 @@ uint32_t Http2::initial_window_size_out = 65535; Http2FlowControlPolicy Http2::flow_control_policy_out = Http2FlowControlPolicy::STATIC_SESSION_AND_STATIC_STREAM; uint32_t Http2::no_activity_timeout_out = 120; -float Http2::stream_error_rate_threshold = 0.1; -uint32_t Http2::stream_error_sampling_threshold = 10; -uint32_t Http2::max_settings_per_frame = 7; -uint32_t Http2::max_settings_per_minute = 14; -uint32_t Http2::max_settings_frames_per_minute = 14; -uint32_t Http2::max_ping_frames_per_minute = 60; -uint32_t Http2::max_priority_frames_per_minute = 120; -uint32_t Http2::max_rst_stream_frames_per_minute = 200; -float Http2::min_avg_window_update = 2560.0; -uint32_t Http2::con_slow_log_threshold = 0; -uint32_t Http2::stream_slow_log_threshold = 0; -uint32_t Http2::header_table_size_limit = 65536; -uint32_t Http2::write_buffer_block_size = 262144; -float Http2::write_size_threshold = 0.5; -uint32_t Http2::write_time_threshold = 100; -uint32_t Http2::buffer_water_mark = 0; +float Http2::stream_error_rate_threshold = 0.1; +uint32_t Http2::stream_error_sampling_threshold = 10; +uint32_t Http2::max_settings_per_frame = 7; +uint32_t Http2::max_settings_per_minute = 14; +uint32_t Http2::max_settings_frames_per_minute = 14; +uint32_t Http2::max_ping_frames_per_minute = 60; +uint32_t Http2::max_priority_frames_per_minute = 120; +uint32_t Http2::max_rst_stream_frames_per_minute = 200; +uint32_t Http2::max_continuation_frames_per_minute = 120; +float Http2::min_avg_window_update = 2560.0; +uint32_t Http2::con_slow_log_threshold = 0; +uint32_t Http2::stream_slow_log_threshold = 0; +uint32_t Http2::header_table_size_limit = 65536; +uint32_t Http2::write_buffer_block_size = 262144; +float Http2::write_size_threshold = 0.5; +uint32_t Http2::write_time_threshold = 100; +uint32_t Http2::buffer_water_mark = 0; void Http2::init() @@ -545,6 +546,7 @@ Http2::init() REC_EstablishStaticConfigInt32U(max_ping_frames_per_minute, "proxy.config.http2.max_ping_frames_per_minute"); REC_EstablishStaticConfigInt32U(max_priority_frames_per_minute, "proxy.config.http2.max_priority_frames_per_minute"); REC_EstablishStaticConfigInt32U(max_rst_stream_frames_per_minute, "proxy.config.http2.max_rst_stream_frames_per_minute"); + REC_EstablishStaticConfigInt32U(max_continuation_frames_per_minute, "proxy.config.http2.max_continuation_frames_per_minute"); REC_EstablishStaticConfigFloat(min_avg_window_update, "proxy.config.http2.min_avg_window_update"); REC_EstablishStaticConfigInt32U(con_slow_log_threshold, "proxy.config.http2.connection.slow.log.threshold"); REC_EstablishStaticConfigInt32U(stream_slow_log_threshold, "proxy.config.http2.stream.slow.log.threshold"); @@ -597,6 +599,8 @@ Http2::init() Metrics::Counter::createPtr("proxy.process.http2.max_priority_frames_per_minute_exceeded"); http2_rsb.max_rst_stream_frames_per_minute_exceeded = Metrics::Counter::createPtr("proxy.process.http2.max_rst_stream_frames_per_minute_exceeded"); + http2_rsb.max_continuation_frames_per_minute_exceeded = + Metrics::Counter::createPtr("proxy.process.http2.max_continuation_frames_per_minute_exceeded"); http2_rsb.insufficient_avg_window_update = Metrics::Counter::createPtr("proxy.process.http2.insufficient_avg_window_update"); http2_rsb.max_concurrent_streams_exceeded_in = Metrics::Counter::createPtr("proxy.process.http2.max_concurrent_streams_exceeded_in"); diff --git a/src/proxy/http2/Http2ConnectionState.cc b/src/proxy/http2/Http2ConnectionState.cc index b4fcd28b68..1a0306ef5c 100644 --- a/src/proxy/http2/Http2ConnectionState.cc +++ b/src/proxy/http2/Http2ConnectionState.cc @@ -1013,6 +1013,18 @@ Http2ConnectionState::rcv_continuation_frame(const Http2Frame &frame) } } + // Update CONTINUATION frame count per minute. + this->increment_received_continuation_frame_count(); + // Close this connection if its CONTINUATION frame count exceeds a limit. + if (configured_max_continuation_frames_per_minute != 0 && + this->get_received_continuation_frame_count() > configured_max_continuation_frames_per_minute) { + Metrics::Counter::increment(http2_rsb.max_continuation_frames_per_minute_exceeded); + Http2StreamDebug(this->session, stream_id, "Observed too frequent CONTINUATION frames: %u frames within a last minute", + this->get_received_continuation_frame_count()); + return Http2Error(Http2ErrorClass::HTTP2_ERROR_CLASS_CONNECTION, Http2ErrorCode::HTTP2_ERROR_ENHANCE_YOUR_CALM, + "reset too frequent CONTINUATION frames"); + } + uint32_t header_blocks_offset = stream->header_blocks_length; stream->header_blocks_length += payload_length; @@ -1254,10 +1266,11 @@ Http2ConnectionState::init(Http2CommonSession *ssn) acknowledged_local_settings.set(HTTP2_SETTINGS_MAX_HEADER_LIST_SIZE, configured_settings.get(HTTP2_SETTINGS_MAX_HEADER_LIST_SIZE)); - configured_max_settings_frames_per_minute = Http2::max_settings_frames_per_minute; - configured_max_ping_frames_per_minute = Http2::max_ping_frames_per_minute; - configured_max_priority_frames_per_minute = Http2::max_priority_frames_per_minute; - configured_max_rst_stream_frames_per_minute = Http2::max_rst_stream_frames_per_minute; + configured_max_settings_frames_per_minute = Http2::max_settings_frames_per_minute; + configured_max_ping_frames_per_minute = Http2::max_ping_frames_per_minute; + configured_max_priority_frames_per_minute = Http2::max_priority_frames_per_minute; + configured_max_rst_stream_frames_per_minute = Http2::max_rst_stream_frames_per_minute; + configured_max_continuation_frames_per_minute = Http2::max_continuation_frames_per_minute; if (auto snis = session->get_netvc()->get_service<TLSSNISupport>(); snis) { if (snis->hints_from_sni.http2_max_settings_frames_per_minute.has_value()) { configured_max_settings_frames_per_minute = snis->hints_from_sni.http2_max_settings_frames_per_minute.value(); @@ -1271,6 +1284,9 @@ Http2ConnectionState::init(Http2CommonSession *ssn) if (snis->hints_from_sni.http2_max_rst_stream_frames_per_minute.has_value()) { configured_max_rst_stream_frames_per_minute = snis->hints_from_sni.http2_max_rst_stream_frames_per_minute.value(); } + if (snis->hints_from_sni.http2_max_continuation_frames_per_minute.has_value()) { + configured_max_continuation_frames_per_minute = snis->hints_from_sni.http2_max_continuation_frames_per_minute.value(); + } } _cop = ActivityCop<Http2Stream>(this->mutex, &stream_list, 1); @@ -2709,6 +2725,18 @@ Http2ConnectionState::get_received_rst_stream_frame_count() return this->_received_rst_stream_frame_counter.get_count(); } +void +Http2ConnectionState::increment_received_continuation_frame_count() +{ + this->_received_continuation_frame_counter.increment(); +} + +uint32_t +Http2ConnectionState::get_received_continuation_frame_count() +{ + return this->_received_continuation_frame_counter.get_count(); +} + // Return min_concurrent_streams_in when current client streams number is larger than max_active_streams_in. // Main purpose of this is preventing DDoS Attacks. unsigned diff --git a/src/records/RecordsConfig.cc b/src/records/RecordsConfig.cc index ee7bb70e0d..6a6d9c41f5 100644 --- a/src/records/RecordsConfig.cc +++ b/src/records/RecordsConfig.cc @@ -1310,6 +1310,8 @@ static const RecordElement RecordsConfig[] = , {RECT_CONFIG, "proxy.config.http2.max_rst_stream_frames_per_minute", RECD_INT, "200", RECU_DYNAMIC, RR_NULL, RECC_STR, "^[0-9]+$", RECA_NULL} , + {RECT_CONFIG, "proxy.config.http2.max_continuation_frames_per_minute", RECD_INT, "120", RECU_DYNAMIC, RR_NULL, RECC_STR, "^[0-9]+$", RECA_NULL} + , {RECT_CONFIG, "proxy.config.http2.min_avg_window_update", RECD_FLOAT, "2560.0", RECU_DYNAMIC, RR_NULL, RECC_NULL, nullptr, RECA_NULL} , {RECT_CONFIG, "proxy.config.http2.header_table_size_limit", RECD_INT, "65536", RECU_DYNAMIC, RR_NULL, RECC_STR, "^[0-9]+$", RECA_NULL}
