Skip to content

Commit

Permalink
🐛Destination-dynamodb: enforce ssl connection (#18672)
Browse files Browse the repository at this point in the history
* [16283] Destination-dynamodb: Added strict-encrypt version and enforced ssl connection on cloud
  • Loading branch information
etsybaev authored Nov 3, 2022
1 parent f7bef97 commit 5c870c1
Show file tree
Hide file tree
Showing 10 changed files with 138 additions and 5 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@
- name: DynamoDB
destinationDefinitionId: 8ccd8909-4e99-4141-b48d-4984b70b2d89
dockerRepository: airbyte/destination-dynamodb
dockerImageTag: 0.1.5
dockerImageTag: 0.1.7
documentationUrl: https://docs.airbyte.com/integrations/destinations/dynamodb
icon: dynamodb.svg
releaseStage: alpha
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1460,7 +1460,7 @@
supported_destination_sync_modes:
- "overwrite"
- "append"
- dockerImage: "airbyte/destination-dynamodb:0.1.5"
- dockerImage: "airbyte/destination-dynamodb:0.1.7"
spec:
documentationUrl: "https://docs.airbyte.com/integrations/destinations/dynamodb"
connectionSpecification:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,5 @@ ENV APPLICATION destination-dynamodb

COPY --from=build /airbyte /airbyte

LABEL io.airbyte.version=0.1.5
LABEL io.airbyte.version=0.1.7
LABEL io.airbyte.name=airbyte/destination-dynamodb
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ plugins {
}

application {
mainClass = 'io.airbyte.integrations.destination.dynamodb.DynamodbDestination'
mainClass = 'io.airbyte.integrations.destination.dynamodb.DynamodbDestinationRunner'
applicationDefaultJvmArgs = ['-XX:+ExitOnOutOfMemoryError', '-XX:MaxRAMPercentage=75.0']
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,4 +77,18 @@ public static AmazonDynamoDB getAmazonDynamoDB(final DynamodbDestinationConfig d
}
}

/**
* Checks that DynamoDb custom endpoint uses a variant that only uses HTTPS
*
* @param endpoint URL string representing an accessible S3 bucket
*/
public static boolean testCustomEndpointSecured(final String endpoint) {
// if user does not use a custom endpoint, do not fail
if (endpoint == null || endpoint.length() == 0) {
return true;
} else {
return endpoint.startsWith("https://");
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,10 @@ public static void main(final String[] args) throws Exception {
@Override
public AirbyteConnectionStatus check(final JsonNode config) {
try {
DynamodbChecker.attemptDynamodbWriteAndDelete(DynamodbDestinationConfig.getDynamodbDestinationConfig(config));
final DynamodbDestinationConfig dynamodbDestinationConfig =
DynamodbDestinationConfig.getDynamodbDestinationConfig(config);

DynamodbChecker.attemptDynamodbWriteAndDelete(dynamodbDestinationConfig);
return new AirbyteConnectionStatus().withStatus(AirbyteConnectionStatus.Status.SUCCEEDED);
} catch (final Exception e) {
LOGGER.error("Exception attempting to access the DynamoDB table: ", e);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/*
* Copyright (c) 2022 Airbyte, Inc., all rights reserved.
*/

package io.airbyte.integrations.destination.dynamodb;

import io.airbyte.integrations.base.adaptive.AdaptiveDestinationRunner;

public class DynamodbDestinationRunner {

public static void main(final String[] args) throws Exception {
AdaptiveDestinationRunner.baseOnEnv()
.withOssDestination(DynamodbDestination::new)
.withCloudDestination(DynamodbDestinationStrictEncrypt::new)
.run(args);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/*
* Copyright (c) 2022 Airbyte, Inc., all rights reserved.
*/

package io.airbyte.integrations.destination.dynamodb;

import com.fasterxml.jackson.databind.JsonNode;
import io.airbyte.protocol.models.AirbyteConnectionStatus;

public class DynamodbDestinationStrictEncrypt extends DynamodbDestination {

protected static final String NON_SECURE_URL_ERR_MSG = "Server Endpoint requires HTTPS";

public DynamodbDestinationStrictEncrypt() {
super();
}

@Override
public AirbyteConnectionStatus check(final JsonNode config) {
final DynamodbDestinationConfig dynamodbDestinationConfig =
DynamodbDestinationConfig.getDynamodbDestinationConfig(config);

// enforce ssl connection
if (!DynamodbChecker.testCustomEndpointSecured(dynamodbDestinationConfig.getEndpoint())) {
return new AirbyteConnectionStatus()
.withStatus(AirbyteConnectionStatus.Status.FAILED)
.withMessage(NON_SECURE_URL_ERR_MSG);
}

return super.check(config);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
/*
* Copyright (c) 2022 Airbyte, Inc., all rights reserved.
*/

package io.airbyte.integrations.destination.dynamodb;

import static io.airbyte.integrations.destination.dynamodb.DynamodbDestinationStrictEncrypt.NON_SECURE_URL_ERR_MSG;
import static org.junit.jupiter.api.Assertions.assertEquals;

import com.fasterxml.jackson.databind.JsonNode;
import com.google.common.collect.ImmutableMap;
import io.airbyte.commons.io.IOs;
import io.airbyte.commons.json.Jsons;
import io.airbyte.protocol.models.AirbyteConnectionStatus;
import io.airbyte.protocol.models.AirbyteConnectionStatus.Status;
import java.nio.file.Files;
import java.nio.file.Path;
import org.junit.jupiter.api.Test;

public class DynamodbDestinationStrictEncryptTest {

protected static final Path secretFilePath = Path.of("secrets/config.json");

/**
* Test that check passes if user is using HTTPS connection
*/
@Test
public void checkPassCustomEndpointIsHttpsOnly() {
final DynamodbDestination destinationWithHttpsOnlyEndpoint = new DynamodbDestinationStrictEncrypt();
final AirbyteConnectionStatus status = destinationWithHttpsOnlyEndpoint.check(getBaseConfigJson());
assertEquals(Status.SUCCEEDED, status.getStatus());
}

/**
* Test that check fails if user is using a non-secure (http) connection
*/
@Test
public void checkFailCustomEndpointIsHttpsOnly() {
final DynamodbDestination destinationWithHttpsOnlyEndpoint = new DynamodbDestinationStrictEncrypt();
final AirbyteConnectionStatus status = destinationWithHttpsOnlyEndpoint.check(getUnsecureConfig());
assertEquals(AirbyteConnectionStatus.Status.FAILED, status.getStatus());
assertEquals(NON_SECURE_URL_ERR_MSG, status.getMessage());
}

protected JsonNode getBaseConfigJson() {
if (!Files.exists(secretFilePath)) {
throw new IllegalStateException("Secret config file doesn't exist. Get a valid secret (for airbyter: "
+ "get secret from GSM) and put to ../destination-dynamodb/secrets/secret.json file");
}
return Jsons.deserialize(IOs.readFile(secretFilePath));
}

protected JsonNode getUnsecureConfig() {
return Jsons.jsonNode(ImmutableMap.builder()
.put("dynamodb_endpoint", "http://testurl.com:9000")
.put("dynamodb_table_name_prefix", "integration-test")
.put("dynamodb_region", "us-east-2")
.put("access_key_id", "dummy_access_key_id")
.put("secret_access_key", "dummy_secret_access_key")
.build());
}

}
2 changes: 2 additions & 0 deletions docs/integrations/destinations/dynamodb.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@ This connector by default uses 10 capacity units for both Read and Write in Dyna

| Version | Date | Pull Request | Subject |
| :--- | :--- | :--- | :--- |
| 0.1.7 | 2022-11-03 | [\#18672](https://github.com/airbytehq/airbyte/pull/18672) | Added strict-encrypt cloud runner |
| 0.1.6 | 2022-11-01 | [\#18672](https://github.com/airbytehq/airbyte/pull/18672) | Enforce to use ssl connection |
| 0.1.5 | 2022-08-05 | [\#15350](https://github.com/airbytehq/airbyte/pull/15350) | Added per-stream handling |
| 0.1.4 | 2022-06-16 | [\#13852](https://github.com/airbytehq/airbyte/pull/13852) | Updated stacktrace format for any trace message errors |
| 0.1.3 | 2022-05-17 | [12820](https://github.com/airbytehq/airbyte/pull/12820) | Improved 'check' operation performance |
Expand Down

0 comments on commit 5c870c1

Please sign in to comment.