NIFI-6019 Removes "trusted hostname" property.

NIFI-6019 Adds support for excluded HTTP headers.

This closes #3452.

Signed-off-by: Andy LoPresto <alopresto@apache.org>
This commit is contained in:
Troy Melhase 2019-04-23 13:36:48 -08:00 committed by Andy LoPresto
parent 9194726b35
commit 1cadc72222
No known key found for this signature in database
GPG Key ID: 6EC293152D90B61D
2 changed files with 52 additions and 17 deletions

View File

@ -30,7 +30,6 @@ import okhttp3.Request;
import okhttp3.RequestBody; import okhttp3.RequestBody;
import okhttp3.Response; import okhttp3.Response;
import okhttp3.ResponseBody; import okhttp3.ResponseBody;
import okhttp3.internal.tls.OkHostnameVerifier;
import okio.BufferedSink; import okio.BufferedSink;
import org.apache.commons.io.input.TeeInputStream; import org.apache.commons.io.input.TeeInputStream;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
@ -54,6 +53,7 @@ import org.apache.nifi.logging.ComponentLog;
import org.apache.nifi.processor.AbstractProcessor; import org.apache.nifi.processor.AbstractProcessor;
import org.apache.nifi.processor.DataUnit; import org.apache.nifi.processor.DataUnit;
import org.apache.nifi.processor.ProcessContext; import org.apache.nifi.processor.ProcessContext;
import org.apache.nifi.processor.ProcessorInitializationContext;
import org.apache.nifi.processor.ProcessSession; import org.apache.nifi.processor.ProcessSession;
import org.apache.nifi.processor.Relationship; import org.apache.nifi.processor.Relationship;
import org.apache.nifi.processor.exception.ProcessException; import org.apache.nifi.processor.exception.ProcessException;
@ -155,6 +155,9 @@ public final class InvokeHTTP extends AbstractProcessor {
EXCEPTION_CLASS, EXCEPTION_MESSAGE, EXCEPTION_CLASS, EXCEPTION_MESSAGE,
"uuid", "filename", "path"))); "uuid", "filename", "path")));
// Set of HTTP header names explicitly excluded from requests.
private static final Map<String, String> excludedHeaders = new HashMap<String, String>();
public static final String HTTP = "http"; public static final String HTTP = "http";
public static final String HTTPS = "https"; public static final String HTTPS = "https";
@ -360,15 +363,6 @@ public final class InvokeHTTP extends AbstractProcessor {
.allowableValues("true", "false") .allowableValues("true", "false")
.build(); .build();
public static final PropertyDescriptor PROP_TRUSTED_HOSTNAME = new PropertyDescriptor.Builder()
.name("Trusted Hostname")
.description("Bypass the normal truststore hostname verifier to allow the specified remote hostname as trusted. "
+ "Enabling this property has MITM security implications, use wisely. Will still accept other connections based "
+ "on the normal truststore hostname verifier. Only valid with SSL (HTTPS) connections.")
.addValidator(StandardValidators.NON_EMPTY_VALIDATOR)
.required(false)
.build();
public static final PropertyDescriptor PROP_ADD_HEADERS_TO_REQUEST = new PropertyDescriptor.Builder() public static final PropertyDescriptor PROP_ADD_HEADERS_TO_REQUEST = new PropertyDescriptor.Builder()
.name("Add Response Headers to Request") .name("Add Response Headers to Request")
.description("Enabling this property saves all the response headers to the original request. This may be when the response headers are needed " .description("Enabling this property saves all the response headers to the original request. This may be when the response headers are needed "
@ -438,7 +432,6 @@ public final class InvokeHTTP extends AbstractProcessor {
PROP_PUT_ATTRIBUTE_MAX_LENGTH, PROP_PUT_ATTRIBUTE_MAX_LENGTH,
PROP_DIGEST_AUTH, PROP_DIGEST_AUTH,
PROP_OUTPUT_RESPONSE_REGARDLESS, PROP_OUTPUT_RESPONSE_REGARDLESS,
PROP_TRUSTED_HOSTNAME,
PROP_ADD_HEADERS_TO_REQUEST, PROP_ADD_HEADERS_TO_REQUEST,
PROP_CONTENT_TYPE, PROP_CONTENT_TYPE,
PROP_SEND_BODY, PROP_SEND_BODY,
@ -492,6 +485,13 @@ public final class InvokeHTTP extends AbstractProcessor {
private final AtomicReference<OkHttpClient> okHttpClientAtomicReference = new AtomicReference<>(); private final AtomicReference<OkHttpClient> okHttpClientAtomicReference = new AtomicReference<>();
protected void init(ProcessorInitializationContext context) {
excludedHeaders.put("Trusted Hostname", "HTTP request header '{}' excluded. " +
"Update processor to use the SSLContextService instead. " +
"See the Access Policies section in the System Administrator's Guide.");
}
@Override @Override
protected List<PropertyDescriptor> getSupportedPropertyDescriptors() { protected List<PropertyDescriptor> getSupportedPropertyDescriptors() {
return PROPERTIES; return PROPERTIES;
@ -573,6 +573,14 @@ public final class InvokeHTTP extends AbstractProcessor {
ProxyConfiguration.validateProxySpec(validationContext, results, PROXY_SPECS); ProxyConfiguration.validateProxySpec(validationContext, results, PROXY_SPECS);
for (String headerKey : validationContext.getProperties().values()) {
if (excludedHeaders.containsKey(headerKey)) {
// We're not using the header message format string here, just this
// static validation message string:
results.add(new ValidationResult.Builder().subject(headerKey).valid(false).explanation("Matches excluded HTTP header name").build());
}
}
return results; return results;
} }
@ -631,12 +639,6 @@ public final class InvokeHTTP extends AbstractProcessor {
setSslSocketFactory(okHttpClientBuilder, sslService, sslContext, isHttpsProxy); setSslSocketFactory(okHttpClientBuilder, sslService, sslContext, isHttpsProxy);
} }
// check the trusted hostname property and override the HostnameVerifier
String trustedHostname = trimToEmpty(context.getProperty(PROP_TRUSTED_HOSTNAME).getValue());
if (!trustedHostname.isEmpty()) {
okHttpClientBuilder.hostnameVerifier(new OverrideHostnameVerifier(trustedHostname, OkHostnameVerifier.INSTANCE));
}
setAuthenticator(okHttpClientBuilder, context); setAuthenticator(okHttpClientBuilder, context);
useChunked = context.getProperty(PROP_USE_CHUNKED_ENCODING).asBoolean(); useChunked = context.getProperty(PROP_USE_CHUNKED_ENCODING).asBoolean();
@ -1021,8 +1023,15 @@ public final class InvokeHTTP extends AbstractProcessor {
requestBuilder = requestBuilder.addHeader("Date", DATE_FORMAT.print(System.currentTimeMillis())); requestBuilder = requestBuilder.addHeader("Date", DATE_FORMAT.print(System.currentTimeMillis()));
} }
final ComponentLog logger = getLogger();
for (String headerKey : dynamicPropertyNames) { for (String headerKey : dynamicPropertyNames) {
String headerValue = context.getProperty(headerKey).evaluateAttributeExpressions(requestFlowFile).getValue(); String headerValue = context.getProperty(headerKey).evaluateAttributeExpressions(requestFlowFile).getValue();
// don't include any of the excluded headers, log instead
if (excludedHeaders.containsKey(headerKey)) {
logger.warn(excludedHeaders.get(headerKey), new Object[]{headerKey});
continue;
}
requestBuilder = requestBuilder.addHeader(headerKey, headerValue); requestBuilder = requestBuilder.addHeader(headerKey, headerValue);
} }

View File

@ -1448,6 +1448,32 @@ public abstract class TestInvokeHttpCommon {
Assert.assertEquals("chunked",header); Assert.assertEquals("chunked",header);
} }
@Test
public void testTrustedHostname() throws Exception {
addHandler(new GetOrHeadHandler());
runner.setProperty(InvokeHTTP.PROP_URL, url + "/status/200");
runner.setProperty("Trusted Hostname", "https://example.com/");
runner.assertValid();
runner.setProperty(InvokeHTTP.PROP_METHOD, "GET");
runner.setProperty(InvokeHTTP.PROP_OUTPUT_RESPONSE_REGARDLESS,"true");
runner.setProperty(InvokeHTTP.PROP_PUT_OUTPUT_IN_ATTRIBUTE,"outputBody");
runner.assertValid();
createFlowFiles(runner);
runner.run();
runner.assertValid();
runner.assertTransferCount(InvokeHTTP.REL_SUCCESS_REQ, 1);
runner.assertTransferCount(InvokeHTTP.REL_RESPONSE, 1);
runner.assertTransferCount(InvokeHTTP.REL_RETRY, 0);
runner.assertTransferCount(InvokeHTTP.REL_NO_RETRY,0);
runner.assertTransferCount(InvokeHTTP.REL_FAILURE, 0);
runner.assertPenalizeCount(0);
}
public static void createFlowFiles(final TestRunner testRunner) throws UnsupportedEncodingException { public static void createFlowFiles(final TestRunner testRunner) throws UnsupportedEncodingException {
final Map<String, String> attributes = new HashMap<>(); final Map<String, String> attributes = new HashMap<>();