mirror of
https://github.com/apache/nifi.git
synced 2025-03-03 07:59:15 +00:00
NIFI-7912 - Added properties to configure DoSFilter timeout and whitelisted addresses
- Added nifi.web.request.ip.whitelist property to set DoSFilter.ipWhitelist - Added nifi.web.request.timeout property to set DoSFilter.maxRequestMs with default of 60 seconds This closes #4972 Signed-off-by: David Handermann <exceptionfactory@apache.org>
This commit is contained in:
parent
b79987918a
commit
9da3b1ec01
@ -215,6 +215,8 @@ public abstract class NiFiProperties {
|
||||
public static final String WEB_PROXY_HOST = "nifi.web.proxy.host";
|
||||
public static final String WEB_MAX_CONTENT_SIZE = "nifi.web.max.content.size";
|
||||
public static final String WEB_MAX_REQUESTS_PER_SECOND = "nifi.web.max.requests.per.second";
|
||||
public static final String WEB_REQUEST_TIMEOUT = "nifi.web.request.timeout";
|
||||
public static final String WEB_REQUEST_IP_WHITELIST = "nifi.web.request.ip.whitelist";
|
||||
public static final String WEB_SHOULD_SEND_SERVER_VERSION = "nifi.web.should.send.server.version";
|
||||
|
||||
// ui properties
|
||||
@ -303,6 +305,7 @@ public abstract class NiFiProperties {
|
||||
public static final String DEFAULT_WEB_WORKING_DIR = "./work/jetty";
|
||||
public static final String DEFAULT_WEB_MAX_CONTENT_SIZE = "20 MB";
|
||||
public static final String DEFAULT_WEB_MAX_REQUESTS_PER_SECOND = "30000";
|
||||
public static final String DEFAULT_WEB_REQUEST_TIMEOUT = "60 secs";
|
||||
public static final String DEFAULT_NAR_WORKING_DIR = "./work/nar";
|
||||
public static final String DEFAULT_COMPONENT_DOCS_DIRECTORY = "./work/docs/components";
|
||||
public static final String DEFAULT_NAR_LIBRARY_DIR = "./lib";
|
||||
@ -672,6 +675,14 @@ public abstract class NiFiProperties {
|
||||
return getProperty(WEB_MAX_REQUESTS_PER_SECOND, DEFAULT_WEB_MAX_REQUESTS_PER_SECOND);
|
||||
}
|
||||
|
||||
public String getWebRequestTimeout() {
|
||||
return getProperty(WEB_REQUEST_TIMEOUT, DEFAULT_WEB_REQUEST_TIMEOUT);
|
||||
}
|
||||
|
||||
public String getWebRequestIpWhitelist() {
|
||||
return getProperty(WEB_REQUEST_IP_WHITELIST);
|
||||
}
|
||||
|
||||
public int getWebThreads() {
|
||||
return getIntegerProperty(WEB_THREADS, DEFAULT_WEB_THREADS);
|
||||
}
|
||||
|
@ -3481,6 +3481,8 @@ host[:port] that NiFi is bound to.
|
||||
blank meaning all requests containing a proxy context path are rejected. Configuring this property would allow requests where the proxy path is contained in this listing.
|
||||
|`nifi.web.max.content.size`|The maximum size (HTTP `Content-Length`) for PUT and POST requests. No default value is set for backward compatibility. Providing a value for this property enables the `Content-Length` filter on all incoming API requests (except Site-to-Site and cluster communications). A suggested value is `20 MB`.
|
||||
|`nifi.web.max.requests.per.second`|The maximum number of requests from a connection per second. Requests in excess of this are first delayed, then throttled.
|
||||
|`nifi.web.request.ip.whitelist`|A comma separated list of IP addresses. Used to specify the IP addresses of clients which can exceed the maximum requests per second (`nifi.web.max.requests.per.second`). Does not apply to web request timeout.
|
||||
|`nifi.web.request.timeout`|The request timeout for web requests. Requests running longer than this time will be forced to end with a HTTP 503 Service Unavailable response. Default value is `60 secs`.
|
||||
|====
|
||||
|
||||
[[security_properties]]
|
||||
|
@ -144,6 +144,8 @@
|
||||
<nifi.web.proxy.host />
|
||||
<nifi.web.max.content.size />
|
||||
<nifi.web.max.requests.per.second>30000</nifi.web.max.requests.per.second>
|
||||
<nifi.web.request.timeout>60 secs</nifi.web.request.timeout>
|
||||
<nifi.web.request.ip.whitelist />
|
||||
<nifi.web.should.send.server.version>true</nifi.web.should.send.server.version>
|
||||
<!-- nifi.properties: security properties -->
|
||||
<nifi.security.keystore />
|
||||
|
@ -163,6 +163,8 @@ nifi.web.proxy.context.path=${nifi.web.proxy.context.path}
|
||||
nifi.web.proxy.host=${nifi.web.proxy.host}
|
||||
nifi.web.max.content.size=${nifi.web.max.content.size}
|
||||
nifi.web.max.requests.per.second=${nifi.web.max.requests.per.second}
|
||||
nifi.web.request.timeout=${nifi.web.request.timeout}
|
||||
nifi.web.request.ip.whitelist=${nifi.web.request.ip.whitelist}
|
||||
nifi.web.should.send.server.version=${nifi.web.should.send.server.version}
|
||||
|
||||
# security properties #
|
||||
|
@ -584,6 +584,10 @@ public class StandardPublicPort extends AbstractPort implements PublicPort {
|
||||
} else {
|
||||
throw new ProcessException(e);
|
||||
}
|
||||
} catch (final InterruptedException e) {
|
||||
logger.error("The NiFi DoS filter has interrupted a long running session. If this is undesired, configure a longer web request timeout value in nifi.properties using '{}'",
|
||||
NiFiProperties.WEB_REQUEST_TIMEOUT);
|
||||
throw new ProcessException(e);
|
||||
} catch (final Exception e) {
|
||||
throw new ProcessException(e);
|
||||
}
|
||||
|
@ -691,8 +691,10 @@ public class JettyServer implements NiFiServer, ExtensionUiLoader {
|
||||
*/
|
||||
private static void addDenialOfServiceFilters(String path, WebAppContext webAppContext, NiFiProperties props) {
|
||||
// Add the requests rate limiting filter to all requests
|
||||
int maxWebRequestsPerSecond = determineMaxWebRequestsPerSecond(props);
|
||||
addWebRequestRateLimitingFilter(path, webAppContext, maxWebRequestsPerSecond);
|
||||
final int maxWebRequestsPerSecond = determineMaxWebRequestsPerSecond(props);
|
||||
final long requestTimeoutInMilliseconds = determineRequestTimeoutInMilliseconds(props);
|
||||
final String ipWhitelist = props.getWebRequestIpWhitelist();
|
||||
addWebRequestLimitingFilter(path, webAppContext, maxWebRequestsPerSecond, ipWhitelist, requestTimeoutInMilliseconds);
|
||||
|
||||
// Only add the ContentLengthFilter if the property is explicitly set (empty by default)
|
||||
int maxRequestSize = determineMaxRequestSize(props);
|
||||
@ -703,32 +705,52 @@ public class JettyServer implements NiFiServer, ExtensionUiLoader {
|
||||
}
|
||||
}
|
||||
|
||||
private static int determineMaxWebRequestsPerSecond(NiFiProperties props) {
|
||||
private static int determineMaxWebRequestsPerSecond(final NiFiProperties props) {
|
||||
int defaultMaxRequestsPerSecond = Integer.parseInt(NiFiProperties.DEFAULT_WEB_MAX_REQUESTS_PER_SECOND);
|
||||
int configuredMaxRequestsPerSecond = 0;
|
||||
try {
|
||||
configuredMaxRequestsPerSecond = Integer.parseInt(props.getMaxWebRequestsPerSecond());
|
||||
} catch (final NumberFormatException e) {
|
||||
logger.warn("Exception parsing property " + NiFiProperties.WEB_MAX_REQUESTS_PER_SECOND + "; using default value: " + defaultMaxRequestsPerSecond);
|
||||
logger.warn("Exception parsing property [{}]; using default value: [{}]", NiFiProperties.WEB_MAX_REQUESTS_PER_SECOND, defaultMaxRequestsPerSecond);
|
||||
}
|
||||
|
||||
return configuredMaxRequestsPerSecond > 0 ? configuredMaxRequestsPerSecond : defaultMaxRequestsPerSecond;
|
||||
}
|
||||
|
||||
private static long determineRequestTimeoutInMilliseconds(final NiFiProperties props) {
|
||||
long defaultRequestTimeout = Math.round(FormatUtils.getPreciseTimeDuration(NiFiProperties.DEFAULT_WEB_REQUEST_TIMEOUT, TimeUnit.MILLISECONDS));
|
||||
long configuredRequestTimeout = 0L;
|
||||
try {
|
||||
configuredRequestTimeout = Math.round(FormatUtils.getPreciseTimeDuration(props.getWebRequestTimeout(), TimeUnit.MILLISECONDS));
|
||||
} catch (final NumberFormatException e) {
|
||||
logger.warn("Exception parsing property [{}]; using default value: [{}]", NiFiProperties.WEB_REQUEST_TIMEOUT, defaultRequestTimeout);
|
||||
}
|
||||
|
||||
return configuredRequestTimeout > 0 ? configuredRequestTimeout : defaultRequestTimeout;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the {@link org.eclipse.jetty.servlets.DoSFilter} to the specified context and path. Limits incoming web requests to {@code maxWebRequestsPerSecond} per second.
|
||||
* In order to allow clients to make more requests than the maximum rate, clients can be added to the {@code ipWhitelist}.
|
||||
* The {@code requestTimeoutInMilliseconds} value limits requests to the given request timeout amount, and will close connections that run longer than this time.
|
||||
*
|
||||
* @param path the path to apply this filter
|
||||
* @param webAppContext the context to apply this filter
|
||||
* @param maxWebRequestsPerSecond the maximum number of allowed requests per second
|
||||
* @param ipWhitelist a whitelist of IP addresses that should not be rate limited. Does not apply to request timeout
|
||||
* @param requestTimeoutInMilliseconds the amount of time before a connection will be automatically closed
|
||||
*/
|
||||
private static void addWebRequestRateLimitingFilter(String path, WebAppContext webAppContext, int maxWebRequestsPerSecond) {
|
||||
private static void addWebRequestLimitingFilter(String path, WebAppContext webAppContext, int maxWebRequestsPerSecond, final String ipWhitelist, long requestTimeoutInMilliseconds) {
|
||||
FilterHolder holder = new FilterHolder(DoSFilter.class);
|
||||
holder.setInitParameters(new HashMap<String, String>() {{
|
||||
put("maxRequestsPerSec", String.valueOf(maxWebRequestsPerSecond));
|
||||
put("maxRequestMs", String.valueOf(requestTimeoutInMilliseconds));
|
||||
put("ipWhitelist", String.valueOf(ipWhitelist));
|
||||
}});
|
||||
holder.setName(DoSFilter.class.getSimpleName());
|
||||
logger.debug("Adding DoSFilter to context at path: " + path + " with max req/sec: " + maxWebRequestsPerSecond);
|
||||
|
||||
logger.debug("Adding DoSFilter to context at path: [{}] with max req/sec: [{}], request timeout: [{}] ms. Whitelisted IPs not subject to filter: [{}]",
|
||||
path, maxWebRequestsPerSecond, requestTimeoutInMilliseconds, StringUtils.defaultIfEmpty(ipWhitelist, "none"));
|
||||
webAppContext.addFilter(holder, path, EnumSet.allOf(DispatcherType.class));
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user