Skip to content

Commit 4dab51d

Browse files
committed
Add SSL option support across all drivers.
cr: https://code.amazon.com/reviews/CR-161521251
1 parent 4aa9883 commit 4dab51d

19 files changed

+476
-3
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,3 +23,5 @@ jacoco.exec
2323

2424
# VS Code
2525
.vscode/
26+
27+
.java-version

src/main/java/com/amazonaws/secretsmanager/sql/AWSSecretsManagerDb2Driver.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
import com.amazonaws.secretsmanager.caching.SecretCache;
1616
import com.amazonaws.secretsmanager.caching.SecretCacheConfiguration;
1717
import com.amazonaws.secretsmanager.util.SQLExceptionUtils;
18+
import com.amazonaws.secretsmanager.util.URLBuilder;
1819
import software.amazon.awssdk.services.secretsmanager.SecretsManagerClient;
1920
import software.amazon.awssdk.services.secretsmanager.SecretsManagerClientBuilder;
2021
import software.amazon.awssdk.utils.StringUtils;
@@ -113,6 +114,17 @@ public String constructUrlFromEndpointPortDatabase(String endpoint, String port,
113114
if (StringUtils.isNotBlank(dbname)) {
114115
url += "/" + dbname;
115116
}
117+
else {
118+
url += "/";
119+
}
120+
return url;
121+
}
122+
123+
@Override
124+
public String enforceSSL(String url, String sslMode) {
125+
if("true".equalsIgnoreCase(sslMode)) {
126+
return new URLBuilder(url).appendProperty("sslConnection", "true").build();
127+
}
116128
return url;
117129
}
118130

src/main/java/com/amazonaws/secretsmanager/sql/AWSSecretsManagerDriver.java

Lines changed: 47 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,11 @@ public abstract class AWSSecretsManagerDriver implements Driver {
108108
*/
109109
public static final String INVALID_SECRET_STRING_JSON = "Could not parse SecretString JSON";
110110

111+
/**
112+
* Logger for the AWSSecretsManagerDriver class.
113+
*/
114+
private static final Logger logger = Logger.getLogger(AWSSecretsManagerDriver.class.getName());
115+
111116
private SecretCache secretCache;
112117

113118
private String realDriverClass;
@@ -319,6 +324,32 @@ public boolean acceptsURL(String url) throws SQLException {
319324
*/
320325
public abstract String getDefaultDriverClass();
321326

327+
/**
328+
* Enforce SSL on the given database URL based on the specified SSL mode.
329+
* This method is called when the <code>connect</code> method is called with a secret ID instead of a URL.
330+
*
331+
* @param url The database URL to enforce SSL on.
332+
* @param sslMode The SSL mode to enforce.
333+
*
334+
* @return String The database URL with SSL enforced.
335+
*/
336+
public abstract String enforceSSL(String url, String sslMode);
337+
338+
private String getSSLConfig(JsonNode jsonObject) {
339+
JsonNode sslNode = jsonObject.get("ssl");
340+
341+
if(sslNode == null) {
342+
return "true";
343+
}
344+
345+
if (sslNode.isBoolean()) {
346+
return sslNode.asBoolean() ? "true" : "false";
347+
} else if (sslNode.isTextual()) {
348+
return sslNode.asText();
349+
}
350+
return "true";
351+
}
352+
322353
/**
323354
* Calls the real driver's <code>connect</code> method using credentials from a secret stored in AWS Secrets
324355
* Manager.
@@ -329,30 +360,42 @@ public boolean acceptsURL(String url) throws SQLException {
329360
* credentials retrieved from Secrets Manager.
330361
* @param credentialsSecretId The friendly name or ARN of the secret that stores the
331362
* login credentials.
363+
* @param isSecretId A flag indicating if the connection uses a secret ID.
332364
*
333365
* @return Connection A database connection.
334366
*
335367
* @throws SQLException If there is an error from the driver or underlying
336368
* database.
337369
* @throws InterruptedException If there was an interruption during secret refresh.
338370
*/
339-
private Connection connectWithSecret(String unwrappedUrl, Properties info, String credentialsSecretId)
371+
private Connection connectWithSecret(String unwrappedUrl, Properties info, String credentialsSecretId, boolean isSecretId)
340372
throws SQLException, InterruptedException {
341373
int retryCount = 0;
374+
String sslMode = null;
342375
while (retryCount++ <= MAX_RETRY) {
343376
String secretString = secretCache.getSecretString(credentialsSecretId);
344377
Properties updatedInfo = new Properties(info);
345378
try {
346379
JsonNode jsonObject = mapper.readTree(secretString);
347380
updatedInfo.setProperty("user", jsonObject.get("username").asText());
348381
updatedInfo.setProperty("password", jsonObject.get("password").asText());
382+
sslMode = isSecretId ? getSSLConfig(jsonObject) : null;
349383
} catch (IOException e) {
350384
// Most likely to occur in the event that the data is not JSON.
351385
// Or the secret's username and/or password fields have been
352386
// removed entirely. Either scenario is most often a user error.
353387
throw new RuntimeException(INVALID_SECRET_STRING_JSON);
354388
}
355389

390+
if (sslMode != null && !"false".equalsIgnoreCase(sslMode)) {
391+
try {
392+
return getWrappedDriver().connect(enforceSSL(unwrappedUrl, sslMode), updatedInfo);
393+
} catch (SQLException e) {
394+
// If SSL connection fails, fall back to non-SSL
395+
logger.warning("SSL connection failed. Falling back to non-SSL connection. Error: " + e.getMessage());
396+
}
397+
}
398+
356399
try {
357400
return getWrappedDriver().connect(unwrappedUrl, updatedInfo);
358401
} catch (Exception e) {
@@ -379,6 +422,7 @@ public Connection connect(String url, Properties info) throws SQLException {
379422
}
380423

381424
String unwrappedUrl = "";
425+
boolean isSecretId = false;
382426
if (url.startsWith(SCHEME)) { // If this is a URL in the correct scheme, unwrap it
383427
unwrappedUrl = unwrapUrl(url);
384428
} else { // Else, assume this is a secret ID and try to retrieve it
@@ -395,6 +439,7 @@ public Connection connect(String url, Properties info) throws SQLException {
395439
JsonNode dbnameNode = jsonObject.get("dbname");
396440
String dbname = dbnameNode == null ? null : dbnameNode.asText();
397441
unwrappedUrl = constructUrlFromEndpointPortDatabase(endpoint, port, dbname);
442+
isSecretId = true;
398443
} catch (IOException e) {
399444
// Most likely to occur in the event that the data is not JSON.
400445
// Or the secret has been modified and is no longer valid.
@@ -406,7 +451,7 @@ public Connection connect(String url, Properties info) throws SQLException {
406451
if (info != null && info.getProperty("user") != null) {
407452
String credentialsSecretId = info.getProperty("user");
408453
try {
409-
return connectWithSecret(unwrappedUrl, info, credentialsSecretId);
454+
return connectWithSecret(unwrappedUrl, info, credentialsSecretId, isSecretId);
410455
} catch (InterruptedException e) {
411456
// User driven exception. Throw a runtime exception.
412457
throw new RuntimeException(e);

src/main/java/com/amazonaws/secretsmanager/sql/AWSSecretsManagerMSSQLServerDriver.java

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
package com.amazonaws.secretsmanager.sql;
1414

1515
import java.sql.SQLException;
16+
import com.amazonaws.secretsmanager.util.URLBuilder;
1617

1718
import com.amazonaws.secretsmanager.caching.SecretCache;
1819
import com.amazonaws.secretsmanager.caching.SecretCacheConfiguration;
@@ -126,6 +127,21 @@ public String constructUrlFromEndpointPortDatabase(String endpoint, String port,
126127
return url;
127128
}
128129

130+
@Override
131+
public String enforceSSL(String url, String sslMode) {
132+
URLBuilder builder = new URLBuilder(url);
133+
switch(sslMode) {
134+
case "TLS":
135+
case "TLSv1":
136+
case "TLSv1.1":
137+
case "TLSv1.2":
138+
return builder.appendProperty("sslProtocol", sslMode).build();
139+
default:
140+
break;
141+
}
142+
return builder.appendProperty("sslProtocol", "TLS").build();
143+
}
144+
129145
@Override
130146
public String getDefaultDriverClass() {
131147
return "com.microsoft.sqlserver.jdbc.SQLServerDriver";

src/main/java/com/amazonaws/secretsmanager/sql/AWSSecretsManagerMariaDBDriver.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
import com.amazonaws.secretsmanager.caching.SecretCache;
1616
import com.amazonaws.secretsmanager.caching.SecretCacheConfiguration;
1717
import com.amazonaws.secretsmanager.util.SQLExceptionUtils;
18+
import com.amazonaws.secretsmanager.util.URLBuilder;
1819

1920
import software.amazon.awssdk.services.secretsmanager.SecretsManagerClient;
2021
import software.amazon.awssdk.services.secretsmanager.SecretsManagerClientBuilder;
@@ -121,6 +122,22 @@ public String constructUrlFromEndpointPortDatabase(String endpoint, String port,
121122
return url;
122123
}
123124

125+
@Override
126+
public String enforceSSL(String url, String sslMode) {
127+
URLBuilder builder = new URLBuilder(url);
128+
switch(sslMode) {
129+
case "disable":
130+
break;
131+
case "trust":
132+
case "verify-full":
133+
case "verify-ca":
134+
return builder.appendParameter("sslMode", sslMode, !url.contains("?")).build();
135+
default:
136+
return builder.appendParameter("sslMode", "trust", !url.contains("?")).build();
137+
}
138+
return url;
139+
}
140+
124141
@Override
125142
public String getDefaultDriverClass() {
126143
return "org.mariadb.jdbc.Driver";

src/main/java/com/amazonaws/secretsmanager/sql/AWSSecretsManagerMySQLDriver.java

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
import com.amazonaws.secretsmanager.caching.SecretCache;
1616
import com.amazonaws.secretsmanager.caching.SecretCacheConfiguration;
1717
import com.amazonaws.secretsmanager.util.SQLExceptionUtils;
18+
import com.amazonaws.secretsmanager.util.URLBuilder;
1819

1920
import software.amazon.awssdk.services.secretsmanager.SecretsManagerClient;
2021
import software.amazon.awssdk.services.secretsmanager.SecretsManagerClientBuilder;
@@ -121,6 +122,23 @@ public String constructUrlFromEndpointPortDatabase(String endpoint, String port,
121122
return url;
122123
}
123124

125+
@Override
126+
public String enforceSSL(String url, String sslMode) {
127+
URLBuilder builder = new URLBuilder(url);
128+
switch(sslMode) {
129+
case "DISABLED":
130+
break;
131+
case "PREFERRED":
132+
case "REQUIRED":
133+
case "VERIFY_CA":
134+
case "VERIFY_IDENTITY":
135+
return builder.appendParameter("sslMode", sslMode, !url.contains("?")).build();
136+
default:
137+
return builder.appendParameter("sslMode", "PREFERRED", !url.contains("?")).build();
138+
}
139+
return url;
140+
}
141+
124142
@Override
125143
public String getDefaultDriverClass() {
126144
try {

src/main/java/com/amazonaws/secretsmanager/sql/AWSSecretsManagerOracleDriver.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,16 @@ public String constructUrlFromEndpointPortDatabase(String endpoint, String port,
143143
return url;
144144
}
145145

146+
@Override
147+
public String enforceSSL(String url, String sslMode) {
148+
if("true".equalsIgnoreCase(sslMode)) {
149+
if (url.startsWith("jdbc:oracle:thin:@//")) {
150+
return url.replace("jdbc:oracle:thin:@//", "jdbc:oracle:thin:@tcps://");
151+
}
152+
}
153+
return url;
154+
}
155+
146156
@Override
147157
public String getDefaultDriverClass() {
148158
return "oracle.jdbc.OracleDriver";

src/main/java/com/amazonaws/secretsmanager/sql/AWSSecretsManagerPostgreSQLDriver.java

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
package com.amazonaws.secretsmanager.sql;
1414

1515
import java.sql.SQLException;
16+
import com.amazonaws.secretsmanager.util.URLBuilder;
1617

1718
import com.amazonaws.secretsmanager.caching.SecretCache;
1819
import com.amazonaws.secretsmanager.caching.SecretCacheConfiguration;
@@ -134,6 +135,34 @@ public String constructUrlFromEndpointPortDatabase(String endpoint, String port,
134135
return url;
135136
}
136137

138+
@Override
139+
public String enforceSSL(String url, String sslMode) {
140+
141+
if (url.endsWith("/")) {
142+
url = url.substring(0, url.length() - 1);
143+
}
144+
145+
URLBuilder builder = new URLBuilder(url);
146+
147+
if ("true".equalsIgnoreCase(sslMode)) {
148+
return builder.appendParameter("sslMode", "verify-full", !url.contains("?")).build();
149+
} else {
150+
switch(sslMode) {
151+
case "disable":
152+
break;
153+
case "allow":
154+
case "prefer":
155+
case "require":
156+
case "verify-ca":
157+
case "verify-full":
158+
return builder.appendParameter("sslMode", sslMode, !url.contains("?")).build();
159+
default:
160+
return builder.appendParameter("sslMode", "prefer", !url.contains("?")).build();
161+
}
162+
}
163+
return url;
164+
}
165+
137166
@Override
138167
public String getDefaultDriverClass() {
139168
return "org.postgresql.Driver";

src/main/java/com/amazonaws/secretsmanager/sql/AWSSecretsManagerRedshiftDriver.java

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
package com.amazonaws.secretsmanager.sql;
1414

1515
import java.sql.SQLException;
16+
import com.amazonaws.secretsmanager.util.URLBuilder;
1617

1718
import com.amazonaws.secretsmanager.caching.SecretCache;
1819
import com.amazonaws.secretsmanager.caching.SecretCacheConfiguration;
@@ -128,6 +129,24 @@ public String constructUrlFromEndpointPortDatabase(String endpoint, String port,
128129
return url;
129130
}
130131

132+
@Override
133+
public String enforceSSL(String url, String sslMode) {
134+
URLBuilder builder = new URLBuilder(url);
135+
switch(sslMode) {
136+
case "disable":
137+
break;
138+
case "allow":
139+
case "prefer":
140+
case "require":
141+
case "verify-ca":
142+
case "verify-full":
143+
return builder.appendProperty("sslmode", sslMode).build();
144+
default:
145+
return builder.appendProperty("sslmode", "prefer").build();
146+
}
147+
return url;
148+
}
149+
131150
@Override
132151
public String getDefaultDriverClass() {
133152
return "com.amazon.redshift.Driver";

0 commit comments

Comments
 (0)