NIFI-2799 AWS Assume Role Credentials With Proxy

This closes #1112.

Signed-off-by: James Wing <jvwing@gmail.com>
This commit is contained in:
Keren Tseytlin 2016-10-06 17:04:12 -04:00 committed by James Wing
parent 979b4d8ab9
commit 6dc2f14198
5 changed files with 123 additions and 11 deletions

View File

@ -155,4 +155,27 @@ public class CredentialPropertyDescriptors {
.description("External ID for cross-account access. This is used in conjunction with role arn, " + .description("External ID for cross-account access. This is used in conjunction with role arn, " +
"role name, and optional session time out") "role name, and optional session time out")
.build(); .build();
/**
* Assume Role Proxy variables for configuring proxy to retrieve keys
*/
public static final PropertyDescriptor ASSUME_ROLE_PROXY_HOST = new PropertyDescriptor.Builder()
.name("assume-role-proxy-host")
.displayName("Assume Role Proxy Host")
.expressionLanguageSupported(false)
.required(false)
.addValidator(StandardValidators.NON_EMPTY_VALIDATOR)
.sensitive(false)
.description("Proxy host for cross-account access, if needed within your environment. This will configure a proxy to request for temporary access keys into another AWS account")
.build();
public static final PropertyDescriptor ASSUME_ROLE_PROXY_PORT = new PropertyDescriptor.Builder()
.name("assume-role-proxy-port")
.displayName("Assume Role Proxy Port")
.expressionLanguageSupported(false)
.required(false)
.addValidator(StandardValidators.POSITIVE_INTEGER_VALIDATOR)
.sensitive(false)
.description("Proxy pot for cross-account access, if needed within your environment. This will configure a proxy to request for temporary access keys into another AWS account")
.build();
} }

View File

@ -24,13 +24,18 @@ import org.apache.nifi.components.PropertyDescriptor;
import org.apache.nifi.components.ValidationContext; import org.apache.nifi.components.ValidationContext;
import org.apache.nifi.components.ValidationResult; import org.apache.nifi.components.ValidationResult;
import static org.apache.nifi.processors.aws.credentials.provider.factory.CredentialPropertyDescriptors.ASSUME_ROLE_ARN; import static org.apache.nifi.processors.aws.credentials.provider.factory.CredentialPropertyDescriptors.ASSUME_ROLE_ARN;
import static org.apache.nifi.processors.aws.credentials.provider.factory.CredentialPropertyDescriptors.ASSUME_ROLE_NAME;
import static org.apache.nifi.processors.aws.credentials.provider.factory.CredentialPropertyDescriptors.MAX_SESSION_TIME;
import static org.apache.nifi.processors.aws.credentials.provider.factory.CredentialPropertyDescriptors.ASSUME_ROLE_EXTERNAL_ID; import static org.apache.nifi.processors.aws.credentials.provider.factory.CredentialPropertyDescriptors.ASSUME_ROLE_EXTERNAL_ID;
import static org.apache.nifi.processors.aws.credentials.provider.factory.CredentialPropertyDescriptors.MAX_SESSION_TIME;
import static org.apache.nifi.processors.aws.credentials.provider.factory.CredentialPropertyDescriptors.ASSUME_ROLE_NAME;
import static org.apache.nifi.processors.aws.credentials.provider.factory.CredentialPropertyDescriptors.ASSUME_ROLE_PROXY_PORT;
import static org.apache.nifi.processors.aws.credentials.provider.factory.CredentialPropertyDescriptors.ASSUME_ROLE_PROXY_HOST;
import org.apache.nifi.processors.aws.credentials.provider.factory.CredentialsStrategy; import org.apache.nifi.processors.aws.credentials.provider.factory.CredentialsStrategy;
import com.amazonaws.ClientConfiguration;
import com.amazonaws.auth.AWSCredentialsProvider; import com.amazonaws.auth.AWSCredentialsProvider;
import com.amazonaws.auth.STSAssumeRoleSessionCredentialsProvider; import com.amazonaws.auth.STSAssumeRoleSessionCredentialsProvider;
import com.amazonaws.services.securitytoken.AWSSecurityTokenService;
import com.amazonaws.services.securitytoken.AWSSecurityTokenServiceClient;
/** /**
@ -66,6 +71,16 @@ public class AssumeRoleCredentialsStrategy extends AbstractCredentialsStrategy {
return false; return false;
} }
public boolean proxyVariablesValidForAssumeRole(Map<PropertyDescriptor, String> properties){
final String assumeRoleProxyHost = properties.get(ASSUME_ROLE_PROXY_HOST);
final String assumeRoleProxyPort = properties.get(ASSUME_ROLE_PROXY_PORT);
if (assumeRoleProxyHost != null && !assumeRoleProxyHost.isEmpty()
&& assumeRoleProxyPort != null && !assumeRoleProxyPort.isEmpty()) {
return true;
}
return false;
}
@Override @Override
public Collection<ValidationResult> validate(final ValidationContext validationContext, public Collection<ValidationResult> validate(final ValidationContext validationContext,
final CredentialsStrategy primaryStrategy) { final CredentialsStrategy primaryStrategy) {
@ -73,6 +88,8 @@ public class AssumeRoleCredentialsStrategy extends AbstractCredentialsStrategy {
final boolean assumeRoleNameIsSet = validationContext.getProperty(ASSUME_ROLE_NAME).isSet(); final boolean assumeRoleNameIsSet = validationContext.getProperty(ASSUME_ROLE_NAME).isSet();
final Integer maxSessionTime = validationContext.getProperty(MAX_SESSION_TIME).asInteger(); final Integer maxSessionTime = validationContext.getProperty(MAX_SESSION_TIME).asInteger();
final boolean assumeRoleExternalIdIsSet = validationContext.getProperty(ASSUME_ROLE_EXTERNAL_ID).isSet(); final boolean assumeRoleExternalIdIsSet = validationContext.getProperty(ASSUME_ROLE_EXTERNAL_ID).isSet();
final boolean assumeRoleProxyHostIsSet = validationContext.getProperty(ASSUME_ROLE_PROXY_HOST).isSet();
final boolean assumeRoleProxyPortIsSet = validationContext.getProperty(ASSUME_ROLE_PROXY_PORT).isSet();
final Collection<ValidationResult> validationFailureResults = new ArrayList<ValidationResult>(); final Collection<ValidationResult> validationFailureResults = new ArrayList<ValidationResult>();
@ -96,6 +113,14 @@ public class AssumeRoleCredentialsStrategy extends AbstractCredentialsStrategy {
.build()); .build());
} }
// Both proxy host and proxy port are required if present
if (assumeRoleProxyHostIsSet ^ assumeRoleProxyPortIsSet){
validationFailureResults.add(new ValidationResult.Builder().input("Assume Role Proxy Host and Port")
.valid(false)
.explanation("Assume role with proxy requires both host and port for the proxy to be set")
.build());
}
return validationFailureResults; return validationFailureResults;
} }
@ -113,14 +138,27 @@ public class AssumeRoleCredentialsStrategy extends AbstractCredentialsStrategy {
rawMaxSessionTime = (rawMaxSessionTime != null) ? rawMaxSessionTime : MAX_SESSION_TIME.getDefaultValue(); rawMaxSessionTime = (rawMaxSessionTime != null) ? rawMaxSessionTime : MAX_SESSION_TIME.getDefaultValue();
final Integer maxSessionTime = Integer.parseInt(rawMaxSessionTime.trim()); final Integer maxSessionTime = Integer.parseInt(rawMaxSessionTime.trim());
final String assumeRoleExternalId = properties.get(ASSUME_ROLE_EXTERNAL_ID); final String assumeRoleExternalId = properties.get(ASSUME_ROLE_EXTERNAL_ID);
STSAssumeRoleSessionCredentialsProvider.Builder builder;
ClientConfiguration config = new ClientConfiguration();
STSAssumeRoleSessionCredentialsProvider.Builder builder = new STSAssumeRoleSessionCredentialsProvider // If proxy variables are set, then create Client Configuration with those values
if (proxyVariablesValidForAssumeRole(properties)) {
final String assumeRoleProxyHost = properties.get(ASSUME_ROLE_PROXY_HOST);
final Integer assumeRoleProxyPort = Integer.parseInt(properties.get(ASSUME_ROLE_PROXY_PORT));
config.withProxyHost(assumeRoleProxyHost);
config.withProxyPort(assumeRoleProxyPort);
}
AWSSecurityTokenService securityTokenService = new AWSSecurityTokenServiceClient(primaryCredentialsProvider, config);
builder = new STSAssumeRoleSessionCredentialsProvider
.Builder(assumeRoleArn, assumeRoleName) .Builder(assumeRoleArn, assumeRoleName)
.withLongLivedCredentialsProvider(primaryCredentialsProvider) .withStsClient(securityTokenService)
.withRoleSessionDurationSeconds(maxSessionTime); .withRoleSessionDurationSeconds(maxSessionTime);
if (assumeRoleExternalId != null && !assumeRoleExternalId.isEmpty()) { if (assumeRoleExternalId != null && !assumeRoleExternalId.isEmpty()) {
builder = builder.withExternalId(assumeRoleExternalId); builder = builder.withExternalId(assumeRoleExternalId);
} }
final AWSCredentialsProvider credsProvider = builder.build(); final AWSCredentialsProvider credsProvider = builder.build();
return credsProvider; return credsProvider;

View File

@ -35,15 +35,17 @@ import org.apache.nifi.processors.aws.credentials.provider.factory.CredentialPro
import org.apache.nifi.processors.aws.credentials.provider.factory.CredentialsProviderFactory; import org.apache.nifi.processors.aws.credentials.provider.factory.CredentialsProviderFactory;
import org.apache.nifi.reporting.InitializationException; import org.apache.nifi.reporting.InitializationException;
import static org.apache.nifi.processors.aws.credentials.provider.factory.CredentialPropertyDescriptors.USE_DEFAULT_CREDENTIALS; import com.amazonaws.auth.AWSCredentialsProvider;
import static org.apache.nifi.processors.aws.credentials.provider.factory.CredentialPropertyDescriptors.ACCESS_KEY; import static org.apache.nifi.processors.aws.credentials.provider.factory.CredentialPropertyDescriptors.ACCESS_KEY;
import static org.apache.nifi.processors.aws.credentials.provider.factory.CredentialPropertyDescriptors.SECRET_KEY; import static org.apache.nifi.processors.aws.credentials.provider.factory.CredentialPropertyDescriptors.ASSUME_ROLE_EXTERNAL_ID;
import static org.apache.nifi.processors.aws.credentials.provider.factory.CredentialPropertyDescriptors.ASSUME_ROLE_PROXY_PORT;
import static org.apache.nifi.processors.aws.credentials.provider.factory.CredentialPropertyDescriptors.ASSUME_ROLE_PROXY_HOST;
import static org.apache.nifi.processors.aws.credentials.provider.factory.CredentialPropertyDescriptors.CREDENTIALS_FILE; import static org.apache.nifi.processors.aws.credentials.provider.factory.CredentialPropertyDescriptors.CREDENTIALS_FILE;
import static org.apache.nifi.processors.aws.credentials.provider.factory.CredentialPropertyDescriptors.PROFILE_NAME; import static org.apache.nifi.processors.aws.credentials.provider.factory.CredentialPropertyDescriptors.PROFILE_NAME;
import static org.apache.nifi.processors.aws.credentials.provider.factory.CredentialPropertyDescriptors.SECRET_KEY;
import static org.apache.nifi.processors.aws.credentials.provider.factory.CredentialPropertyDescriptors.USE_ANONYMOUS_CREDENTIALS; import static org.apache.nifi.processors.aws.credentials.provider.factory.CredentialPropertyDescriptors.USE_ANONYMOUS_CREDENTIALS;
import static org.apache.nifi.processors.aws.credentials.provider.factory.CredentialPropertyDescriptors.ASSUME_ROLE_EXTERNAL_ID; import static org.apache.nifi.processors.aws.credentials.provider.factory.CredentialPropertyDescriptors.USE_DEFAULT_CREDENTIALS;
import com.amazonaws.auth.AWSCredentialsProvider;
/** /**
* Implementation of AWSCredentialsProviderService interface * Implementation of AWSCredentialsProviderService interface
@ -75,6 +77,8 @@ public class AWSCredentialsProviderControllerService extends AbstractControllerS
props.add(ASSUME_ROLE_NAME); props.add(ASSUME_ROLE_NAME);
props.add(MAX_SESSION_TIME); props.add(MAX_SESSION_TIME);
props.add(ASSUME_ROLE_EXTERNAL_ID); props.add(ASSUME_ROLE_EXTERNAL_ID);
props.add(ASSUME_ROLE_PROXY_HOST);
props.add(ASSUME_ROLE_PROXY_PORT);
properties = Collections.unmodifiableList(props); properties = Collections.unmodifiableList(props);
} }
@ -109,4 +113,4 @@ public class AWSCredentialsProviderControllerService extends AbstractControllerS
public String toString() { public String toString() {
return "AWSCredentialsProviderService[id=" + getIdentifier() + "]"; return "AWSCredentialsProviderService[id=" + getIdentifier() + "]";
} }
} }

View File

@ -34,6 +34,8 @@ import static org.apache.nifi.processors.aws.credentials.provider.factory.Creden
import static org.apache.nifi.processors.aws.credentials.provider.factory.CredentialPropertyDescriptors.PROFILE_NAME; import static org.apache.nifi.processors.aws.credentials.provider.factory.CredentialPropertyDescriptors.PROFILE_NAME;
import static org.apache.nifi.processors.aws.credentials.provider.factory.CredentialPropertyDescriptors.USE_ANONYMOUS_CREDENTIALS; import static org.apache.nifi.processors.aws.credentials.provider.factory.CredentialPropertyDescriptors.USE_ANONYMOUS_CREDENTIALS;
import static org.apache.nifi.processors.aws.credentials.provider.factory.CredentialPropertyDescriptors.ASSUME_ROLE_ARN; import static org.apache.nifi.processors.aws.credentials.provider.factory.CredentialPropertyDescriptors.ASSUME_ROLE_ARN;
import static org.apache.nifi.processors.aws.credentials.provider.factory.CredentialPropertyDescriptors.ASSUME_ROLE_PROXY_PORT;
import static org.apache.nifi.processors.aws.credentials.provider.factory.CredentialPropertyDescriptors.ASSUME_ROLE_PROXY_HOST;
import static org.apache.nifi.processors.aws.credentials.provider.factory.CredentialPropertyDescriptors.ASSUME_ROLE_NAME; import static org.apache.nifi.processors.aws.credentials.provider.factory.CredentialPropertyDescriptors.ASSUME_ROLE_NAME;
import static org.apache.nifi.processors.aws.credentials.provider.factory.CredentialPropertyDescriptors.MAX_SESSION_TIME; import static org.apache.nifi.processors.aws.credentials.provider.factory.CredentialPropertyDescriptors.MAX_SESSION_TIME;
import static org.apache.nifi.processors.aws.credentials.provider.factory.CredentialPropertyDescriptors.ASSUME_ROLE_EXTERNAL_ID; import static org.apache.nifi.processors.aws.credentials.provider.factory.CredentialPropertyDescriptors.ASSUME_ROLE_EXTERNAL_ID;
@ -57,7 +59,9 @@ public class MockAWSProcessor extends AbstractAWSCredentialsProviderProcessor<Am
ASSUME_ROLE_ARN, ASSUME_ROLE_ARN,
ASSUME_ROLE_NAME, ASSUME_ROLE_NAME,
MAX_SESSION_TIME, MAX_SESSION_TIME,
ASSUME_ROLE_EXTERNAL_ID ASSUME_ROLE_EXTERNAL_ID,
ASSUME_ROLE_PROXY_HOST,
ASSUME_ROLE_PROXY_PORT
); );
@Override @Override

View File

@ -198,4 +198,47 @@ public class TestCredentialsProviderFactory {
assertEquals("credentials provider should be equal", ProfileCredentialsProvider.class, assertEquals("credentials provider should be equal", ProfileCredentialsProvider.class,
credentialsProvider.getClass()); credentialsProvider.getClass());
} }
@Test
public void testAssumeRoleCredentialsWithProxy() throws Throwable {
final TestRunner runner = TestRunners.newTestRunner(MockAWSProcessor.class);
runner.setProperty(CredentialPropertyDescriptors.CREDENTIALS_FILE, "src/test/resources/mock-aws-credentials.properties");
runner.setProperty(CredentialPropertyDescriptors.ASSUME_ROLE_ARN, "BogusArn");
runner.setProperty(CredentialPropertyDescriptors.ASSUME_ROLE_NAME, "BogusSession");
runner.setProperty(CredentialPropertyDescriptors.ASSUME_ROLE_PROXY_HOST, "proxy.company.com");
runner.setProperty(CredentialPropertyDescriptors.ASSUME_ROLE_PROXY_PORT, "8080");
runner.assertValid();
Map<PropertyDescriptor, String> properties = runner.getProcessContext().getProperties();
final CredentialsProviderFactory factory = new CredentialsProviderFactory();
final AWSCredentialsProvider credentialsProvider = factory.getCredentialsProvider(properties);
Assert.assertNotNull(credentialsProvider);
assertEquals("credentials provider should be equal", STSAssumeRoleSessionCredentialsProvider.class,
credentialsProvider.getClass());
}
@Test
public void testAssumeRoleMissingProxyHost() throws Throwable {
final TestRunner runner = TestRunners.newTestRunner(MockAWSProcessor.class);
runner.setProperty(CredentialPropertyDescriptors.CREDENTIALS_FILE, "src/test/resources/mock-aws-credentials.properties");
runner.setProperty(CredentialPropertyDescriptors.ASSUME_ROLE_PROXY_PORT, "8080");
runner.assertNotValid();
}
@Test
public void testAssumeRoleMissingProxyPort() throws Throwable {
final TestRunner runner = TestRunners.newTestRunner(MockAWSProcessor.class);
runner.setProperty(CredentialPropertyDescriptors.CREDENTIALS_FILE, "src/test/resources/mock-aws-credentials.properties");
runner.setProperty(CredentialPropertyDescriptors.ASSUME_ROLE_PROXY_HOST, "proxy.company.com");
runner.assertNotValid();
}
@Test
public void testAssumeRoleInvalidProxyPort() throws Throwable {
final TestRunner runner = TestRunners.newTestRunner(MockAWSProcessor.class);
runner.setProperty(CredentialPropertyDescriptors.CREDENTIALS_FILE, "src/test/resources/mock-aws-credentials.properties");
runner.setProperty(CredentialPropertyDescriptors.ASSUME_ROLE_PROXY_HOST, "proxy.company.com");
runner.setProperty(CredentialPropertyDescriptors.ASSUME_ROLE_PROXY_PORT, "notIntPort");
runner.assertNotValid();
}
} }