-
Notifications
You must be signed in to change notification settings - Fork 4.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
add SSL options to mssql #33071
add SSL options to mssql #33071
Changes from all commits
43e370e
e6b7395
ed91686
d24b1ab
7d2ee0a
26302e1
a06a3f0
7715617
e683c0c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1 @@ | ||
version=0.10.3 | ||
version=0.10.4 |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -26,6 +26,7 @@ | |
import io.airbyte.cdk.db.jdbc.JdbcDatabase; | ||
import io.airbyte.cdk.db.jdbc.JdbcUtils; | ||
import io.airbyte.cdk.db.jdbc.streaming.AdaptiveStreamingQueryConfig; | ||
import io.airbyte.cdk.db.util.SSLCertificateUtils; | ||
import io.airbyte.cdk.integrations.base.IntegrationRunner; | ||
import io.airbyte.cdk.integrations.base.Source; | ||
import io.airbyte.cdk.integrations.base.adaptive.AdaptiveSourceRunner; | ||
|
@@ -51,7 +52,11 @@ | |
import io.airbyte.protocol.models.v0.ConfiguredAirbyteStream; | ||
import io.airbyte.protocol.models.v0.SyncMode; | ||
import io.debezium.connector.sqlserver.Lsn; | ||
import java.io.File; | ||
import java.io.IOException; | ||
import java.net.URI; | ||
import java.security.KeyStoreException; | ||
import java.security.NoSuchAlgorithmException; | ||
import java.security.cert.CertificateException; | ||
import java.sql.Connection; | ||
import java.sql.JDBCType; | ||
import java.sql.PreparedStatement; | ||
|
@@ -67,6 +72,7 @@ | |
import java.util.Optional; | ||
import java.util.OptionalInt; | ||
import java.util.Set; | ||
import org.apache.commons.lang3.RandomStringUtils; | ||
import org.slf4j.Logger; | ||
import org.slf4j.LoggerFactory; | ||
|
||
|
@@ -94,6 +100,8 @@ SELECT CAST(IIF(EXISTS(SELECT TOP 1 1 FROM "%s"."%s" WHERE "%s" IS NULL), 1, 0) | |
public static final String NO_TUNNEL = "NO_TUNNEL"; | ||
public static final String SSL_METHOD = "ssl_method"; | ||
public static final String SSL_METHOD_UNENCRYPTED = "unencrypted"; | ||
|
||
public static final String JDBC_DELIMITER = ";"; | ||
private List<String> schemas; | ||
|
||
public static Source sshWrappedSource(MssqlSource source) { | ||
|
@@ -599,30 +607,33 @@ private static AirbyteStream addCdcMetadataColumns(final AirbyteStream stream) { | |
private void readSsl(final JsonNode sslMethod, final List<String> additionalParameters) { | ||
final JsonNode config = sslMethod.get("ssl_method"); | ||
switch (config.get("ssl_method").asText()) { | ||
case "unencrypted" -> additionalParameters.add("encrypt=false"); | ||
case "unencrypted" -> { | ||
additionalParameters.add("encrypt=false"); | ||
additionalParameters.add("trustServerCertificate=true"); | ||
} | ||
case "encrypted_trust_server_certificate" -> { | ||
additionalParameters.add("encrypt=true"); | ||
additionalParameters.add("trustServerCertificate=true"); | ||
} | ||
case "encrypted_verify_certificate" -> { | ||
additionalParameters.add("encrypt=true"); | ||
|
||
// trust store location code found at https://stackoverflow.com/a/56570588 | ||
final String trustStoreLocation = Optional | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this was really not doing anything. If anything, it'd guarantee that the connection would fail (because with this, the sever certificate would have to be signed by one of the root authorities in the default trust store) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. the old behavior is equivalent to not pushing anything as a trustStorelocation JDBC parameer |
||
.ofNullable(System.getProperty("javax.net.ssl.trustStore")) | ||
.orElseGet(() -> System.getProperty("java.home") + "/lib/security/cacerts"); | ||
final File trustStoreFile = new File(trustStoreLocation); | ||
if (!trustStoreFile.exists()) { | ||
throw new RuntimeException( | ||
"Unable to locate the Java TrustStore: the system property javax.net.ssl.trustStore is undefined or " | ||
+ trustStoreLocation + " does not exist."); | ||
} | ||
final String trustStorePassword = System.getProperty("javax.net.ssl.trustStorePassword"); | ||
additionalParameters.add("trustStore=" + trustStoreLocation); | ||
if (trustStorePassword != null && !trustStorePassword.isEmpty()) { | ||
additionalParameters.add("trustServerCertificate=false"); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. added for clarity. By default, encrypt=true implies trustServerCertificate=false |
||
|
||
if (config.has("certificate")) { | ||
stephane-airbyte marked this conversation as resolved.
Show resolved
Hide resolved
|
||
String certificate = config.get("certificate").asText(); | ||
String password = RandomStringUtils.randomAlphanumeric(100); | ||
final URI keyStoreUri; | ||
try { | ||
keyStoreUri = SSLCertificateUtils.keyStoreFromCertificate(certificate, password, null, null); | ||
} catch (IOException | KeyStoreException | NoSuchAlgorithmException | CertificateException e) { | ||
throw new RuntimeException(e); | ||
} | ||
additionalParameters | ||
.add("trustStorePassword=" + config.get("trustStorePassword").asText()); | ||
.add("trustStore=" + keyStoreUri.getPath()); | ||
additionalParameters | ||
.add("trustStorePassword=" + password); | ||
} | ||
|
||
if (config.has("hostNameInCertificate")) { | ||
additionalParameters | ||
.add("hostNameInCertificate=" + config.get("hostNameInCertificate").asText()); | ||
|
@@ -639,6 +650,11 @@ public Duration getConnectionTimeoutMssql(final Map<String, String> connectionPr | |
return getConnectionTimeout(connectionProperties); | ||
} | ||
|
||
@Override | ||
public JdbcDatabase createDatabase(final JsonNode sourceConfig) throws SQLException { | ||
return createDatabase(sourceConfig, JDBC_DELIMITER); | ||
} | ||
|
||
public static void main(final String[] args) throws Exception { | ||
final Source source = MssqlSource.sshWrappedSource(new MssqlSource()); | ||
LOGGER.info("starting source: {}", MssqlSource.class); | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4,7 +4,7 @@ | |
"$schema": "http://json-schema.org/draft-07/schema#", | ||
"title": "MSSQL Source Spec", | ||
"type": "object", | ||
"required": ["host", "port", "database", "username"], | ||
"required": ["host", "port", "database", "username", "password"], | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. the password is required in the parameter parsing logic in MssqlSource.java. Making it mandatory in the UI as well There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this a breaking change? What is the expected behavior here? |
||
"properties": { | ||
"host": { | ||
"description": "The hostname of the database.", | ||
|
@@ -90,7 +90,7 @@ | |
{ | ||
"title": "Encrypted (verify certificate)", | ||
"description": "Verify and use the certificate provided by the server.", | ||
"required": ["ssl_method", "trustStoreName", "trustStorePassword"], | ||
"required": ["ssl_method"], | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. unless I'm missing something, the previous required fields didn't even exist in the spec.json. Removing them... |
||
"properties": { | ||
"ssl_method": { | ||
"type": "string", | ||
|
@@ -100,7 +100,15 @@ | |
"title": "Host Name In Certificate", | ||
"type": "string", | ||
"description": "Specifies the host name of the server. The value of this property must match the subject property of the certificate.", | ||
"order": 7 | ||
"order": 0 | ||
}, | ||
"certificate": { | ||
"title": "Certificate", | ||
"type": "string", | ||
"description": "certificate of the server, or of the CA that signed the server certificate", | ||
"order": 1, | ||
"airbyte_secret": true, | ||
"multiline": true | ||
} | ||
} | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,5 +2,6 @@ | |
"host": "default", | ||
"port": 5555, | ||
"database": "default", | ||
"username": "default" | ||
"username": "default", | ||
"password": "default" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. now that the password is a mandatory field, it needs to be added here too |
||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Only added for clarity. By default, encrypt=false implies trustServerCertificate=true.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
trustServerCertificate is trust all essentially.
This will trust any server certificate. I assume for the exchange of credentials which is still encrypted event when
encrypt=false
(you mentioned this offline)