This is an automated email from the ASF dual-hosted git repository.
exceptionfactory pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/nifi.git
The following commit(s) were added to refs/heads/main by this push:
new af8ec30c7b6 NIFI-15890 Added Token Request Endpoint property to
AwsRdsIamDatabasePasswordProvider (#11189)
af8ec30c7b6 is described below
commit af8ec30c7b6e6c2f962f891218ec920023177475
Author: Hiroyuki Yagihashi <[email protected]>
AuthorDate: Thu May 14 03:46:56 2026 +0900
NIFI-15890 Added Token Request Endpoint property to
AwsRdsIamDatabasePasswordProvider (#11189)
Signed-off-by: David Handermann <[email protected]>
---
.../aws/rds/AwsRdsIamDatabasePasswordProvider.java | 44 ++++++++++++++++++++--
.../additionalDetails.md | 28 ++++++++++++++
.../rds/AwsRdsIamDatabasePasswordProviderTest.java | 10 ++++-
3 files changed, 76 insertions(+), 6 deletions(-)
diff --git
a/nifi-extension-bundles/nifi-aws-bundle/nifi-aws-processors/src/main/java/org/apache/nifi/processors/aws/rds/AwsRdsIamDatabasePasswordProvider.java
b/nifi-extension-bundles/nifi-aws-bundle/nifi-aws-processors/src/main/java/org/apache/nifi/processors/aws/rds/AwsRdsIamDatabasePasswordProvider.java
index 675c8488dd8..1df43828f2f 100644
---
a/nifi-extension-bundles/nifi-aws-bundle/nifi-aws-processors/src/main/java/org/apache/nifi/processors/aws/rds/AwsRdsIamDatabasePasswordProvider.java
+++
b/nifi-extension-bundles/nifi-aws-bundle/nifi-aws-processors/src/main/java/org/apache/nifi/processors/aws/rds/AwsRdsIamDatabasePasswordProvider.java
@@ -29,6 +29,7 @@ import org.apache.nifi.controller.ConfigurationContext;
import org.apache.nifi.dbcp.api.DatabasePasswordProvider;
import org.apache.nifi.dbcp.api.DatabasePasswordRequestContext;
import org.apache.nifi.processor.exception.ProcessException;
+import org.apache.nifi.processor.util.StandardValidators;
import
org.apache.nifi.processors.aws.credentials.provider.AwsCredentialsProviderService;
import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider;
import software.amazon.awssdk.regions.Region;
@@ -53,6 +54,8 @@ import static
org.apache.nifi.processors.aws.region.RegionUtil.isDynamicRegion;
""")
public class AwsRdsIamDatabasePasswordProvider extends
AbstractControllerService implements DatabasePasswordProvider {
+ private static final String URI_ENDPOINT_FORMAT = "tcp://%s";
+
static final PropertyDescriptor AWS_CREDENTIALS_PROVIDER_SERVICE = new
PropertyDescriptor.Builder()
.name("AWS Credentials Provider Service")
.description("Controller Service that provides the AWS credentials
used to sign IAM authentication requests.")
@@ -60,15 +63,24 @@ public class AwsRdsIamDatabasePasswordProvider extends
AbstractControllerService
.required(true)
.build();
+ static final PropertyDescriptor TOKEN_REQUEST_ENDPOINT = new
PropertyDescriptor.Builder()
+ .name("Token Request Endpoint")
+ .description("Token Request Endpoint in hostname:port format used
for IAM token signing, overriding the hostname and port from the JDBC URL.")
+ .required(false)
+ .addValidator(StandardValidators.HOSTNAME_PORT_LIST_VALIDATOR)
+ .build();
+
private static final List<PropertyDescriptor> PROPERTY_DESCRIPTORS =
List.of(
AWS_CREDENTIALS_PROVIDER_SERVICE,
REGION,
- CUSTOM_REGION
+ CUSTOM_REGION,
+ TOKEN_REQUEST_ENDPOINT
);
private volatile AwsCredentialsProvider awsCredentialsProvider;
private volatile RdsUtilities rdsUtilities;
private volatile Region awsRegion;
+ private volatile ParsedEndpoint tokenRequestEndpoint;
@Override
protected List<PropertyDescriptor> getSupportedPropertyDescriptors() {
@@ -95,6 +107,7 @@ public class AwsRdsIamDatabasePasswordProvider extends
AbstractControllerService
awsCredentialsProvider =
credentialsService.getAwsCredentialsProvider();
awsRegion = getRegion(context);
rdsUtilities = createRdsUtilities(awsRegion, awsCredentialsProvider);
+ tokenRequestEndpoint = getTokenRequestEndpoint(context);
}
@OnDisabled
@@ -102,15 +115,23 @@ public class AwsRdsIamDatabasePasswordProvider extends
AbstractControllerService
awsCredentialsProvider = null;
rdsUtilities = null;
awsRegion = null;
+ tokenRequestEndpoint = null;
}
@Override
public char[] getPassword(final DatabasePasswordRequestContext
requestContext) {
Objects.requireNonNull(requestContext, "Database Password Request
Context required");
- final ParsedEndpoint parsedEndpoint =
parseEndpoint(requestContext.getJdbcUrl());
- final String hostname = resolveHostname(parsedEndpoint,
requestContext.getJdbcUrl());
- final int port = resolvePort(parsedEndpoint);
+ final String hostname;
+ final int port;
+ if (tokenRequestEndpoint == null) {
+ final ParsedEndpoint jdbcEndpoint =
parseEndpoint(requestContext.getJdbcUrl());
+ hostname = resolveHostname(jdbcEndpoint,
requestContext.getJdbcUrl());
+ port = resolvePort(jdbcEndpoint);
+ } else {
+ hostname = tokenRequestEndpoint.hostname();
+ port = tokenRequestEndpoint.port();
+ }
final String username =
resolveUsername(requestContext.getDatabaseUser());
final GenerateAuthenticationTokenRequest tokenRequest =
GenerateAuthenticationTokenRequest.builder()
@@ -160,6 +181,21 @@ public class AwsRdsIamDatabasePasswordProvider extends
AbstractControllerService
return username;
}
+ private ParsedEndpoint getTokenRequestEndpoint(final ConfigurationContext
context) {
+ return context.getProperty(TOKEN_REQUEST_ENDPOINT).isSet()
+ ?
parseTokenRequestEndpoint(context.getProperty(TOKEN_REQUEST_ENDPOINT).getValue())
+ : null;
+ }
+
+ private ParsedEndpoint parseTokenRequestEndpoint(final String endpoint) {
+ try {
+ final URI uri =
URI.create(URI_ENDPOINT_FORMAT.formatted(endpoint));
+ return new ParsedEndpoint(uri.getHost(), uri.getPort());
+ } catch (final IllegalArgumentException e) {
+ throw new ProcessException("Failed to parse Token Request Endpoint
[%s] as hostname:port".formatted(endpoint), e);
+ }
+ }
+
private ParsedEndpoint parseEndpoint(final String jdbcUrl) {
if (StringUtils.isBlank(jdbcUrl)) {
return ParsedEndpoint.EMPTY;
diff --git
a/nifi-extension-bundles/nifi-aws-bundle/nifi-aws-processors/src/main/resources/docs/org.apache.nifi.processors.aws.rds.AwsRdsIamDatabasePasswordProvider/additionalDetails.md
b/nifi-extension-bundles/nifi-aws-bundle/nifi-aws-processors/src/main/resources/docs/org.apache.nifi.processors.aws.rds.AwsRdsIamDatabasePasswordProvider/additionalDetails.md
index 1b0b7b6d72b..65394cb3368 100644
---
a/nifi-extension-bundles/nifi-aws-bundle/nifi-aws-processors/src/main/resources/docs/org.apache.nifi.processors.aws.rds.AwsRdsIamDatabasePasswordProvider/additionalDetails.md
+++
b/nifi-extension-bundles/nifi-aws-bundle/nifi-aws-processors/src/main/resources/docs/org.apache.nifi.processors.aws.rds.AwsRdsIamDatabasePasswordProvider/additionalDetails.md
@@ -90,3 +90,31 @@ When that works, configure NiFi’s DBCP service with:
- `Database Password Provider`: `AwsRdsIamDatabasePasswordProvider`
NiFi will then mint IAM tokens automatically for each new JDBC connection.
+
+## Connecting Through a Network Load Balancer in Front of an RDS Proxy
+
+When connecting through a Network Load Balancer (NLB) placed in front of an
RDS Proxy, the JDBC URL must point to the NLB endpoint, but the IAM token must
be signed for the RDS Proxy endpoint. By default, the token is generated using
the hostname extracted from the JDBC URL, which causes authentication to fail.
+
+Use the optional **Token Request Endpoint** property to specify the RDS Proxy
endpoint (`hostname:port`) for token signing while keeping the NLB endpoint in
the JDBC URL for the actual TCP connection.
+
+### CLI verification
+
+```bash
+NLB_ENDPOINT="nlb-example.elb.us-east-1.amazonaws.com"
+RDS_PROXY_ENDPOINT="my-proxy.proxy-example.us-east-1.rds.amazonaws.com"
+
+TOKEN=$(aws rds generate-db-auth-token \
+ --hostname "$RDS_PROXY_ENDPOINT" \
+ --port 3306 \
+ --region us-east-1 \
+ --username nifi_app)
+
+mysql -h "$NLB_ENDPOINT" -P 3306 -u nifi_app --password="$TOKEN"
+```
+
+When that works, configure NiFi's DBCP service with:
+
+- `Database Connection URL`:
`jdbc:mysql://nlb-example.elb.us-east-1.amazonaws.com:3306/mydb`
+- `Database User`: `nifi_app`
+- `Database Password Provider`: `AwsRdsIamDatabasePasswordProvider`
+- `Token Request Endpoint`:
`my-proxy.proxy-example.us-east-1.rds.amazonaws.com:3306`
diff --git
a/nifi-extension-bundles/nifi-aws-bundle/nifi-aws-processors/src/test/java/org/apache/nifi/processors/aws/rds/AwsRdsIamDatabasePasswordProviderTest.java
b/nifi-extension-bundles/nifi-aws-bundle/nifi-aws-processors/src/test/java/org/apache/nifi/processors/aws/rds/AwsRdsIamDatabasePasswordProviderTest.java
index 9df6a940d9c..502da342ad1 100644
---
a/nifi-extension-bundles/nifi-aws-bundle/nifi-aws-processors/src/test/java/org/apache/nifi/processors/aws/rds/AwsRdsIamDatabasePasswordProviderTest.java
+++
b/nifi-extension-bundles/nifi-aws-bundle/nifi-aws-processors/src/test/java/org/apache/nifi/processors/aws/rds/AwsRdsIamDatabasePasswordProviderTest.java
@@ -30,6 +30,7 @@ import software.amazon.awssdk.regions.Region;
import static
org.apache.nifi.processors.aws.credentials.provider.service.AWSCredentialsProviderControllerService.ACCESS_KEY_ID;
import static
org.apache.nifi.processors.aws.credentials.provider.service.AWSCredentialsProviderControllerService.SECRET_KEY;
import static
org.apache.nifi.processors.aws.rds.AwsRdsIamDatabasePasswordProvider.AWS_CREDENTIALS_PROVIDER_SERVICE;
+import static
org.apache.nifi.processors.aws.rds.AwsRdsIamDatabasePasswordProvider.TOKEN_REQUEST_ENDPOINT;
import static org.apache.nifi.processors.aws.region.RegionUtil.REGION;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
@@ -43,6 +44,7 @@ class AwsRdsIamDatabasePasswordProviderTest {
private static final String POSTGRES_DRIVER_CLASS =
"org.postgresql.Driver";
private static final String DB_USER = "dbuser";
private static final String HOSTNAME =
"example.us-west-2.rds.amazonaws.com";
+ private static final String RDS_PROXY_HOSTNAME =
"my-proxy.proxy-example.us-west-2.rds.amazonaws.com";
private static final String JDBC_PREFIX = "jdbc:postgresql://";
private static final String DATABASE = "dev";
private static final int PORT = 5432;
@@ -79,7 +81,11 @@ class AwsRdsIamDatabasePasswordProviderTest {
}
@Test
- void testGeneratesTokenWithDefaultPort() {
+ void testGeneratesTokenUsingTokenRequestEndpointOverride() {
+ runner.disableControllerService(passwordProvider);
+ runner.setProperty(passwordProvider, TOKEN_REQUEST_ENDPOINT,
"%s:%d".formatted(RDS_PROXY_HOSTNAME, PORT));
+ runner.enableControllerService(passwordProvider);
+
final DatabasePasswordProvider service = getService();
final DatabasePasswordRequestContext context =
DatabasePasswordRequestContext.builder()
.jdbcUrl("%s%s:%d/%s".formatted(JDBC_PREFIX, HOSTNAME, PORT,
DATABASE))
@@ -88,7 +94,7 @@ class AwsRdsIamDatabasePasswordProviderTest {
.build();
final String token = new String(service.getPassword(context));
- assertTrue(token.startsWith("%s:%d/".formatted(HOSTNAME, PORT)));
+ assertTrue(token.startsWith("%s:%d/".formatted(RDS_PROXY_HOSTNAME,
PORT)));
assertTrue(token.contains("DBUser=" + DB_USER));
}