Skip to content

Commit

Permalink
feat: provide an option to store certificates in a separate folder fr…
Browse files Browse the repository at this point in the history
…om Greengrass root (#1305)

Co-authored-by: Mirrorxxi <[email protected]>
  • Loading branch information
ChangxinDong and Mirrorxxi authored Sep 16, 2022
1 parent ac98147 commit dd8e059
Show file tree
Hide file tree
Showing 7 changed files with 68 additions and 17 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -246,7 +246,7 @@ protected void initKernel()
setupTesRoleAndAlias();
setDefaultRunWithUser(kernel);
deviceProvisioningHelper.updateKernelConfigWithIotConfiguration(kernel, thingInfo, TEST_REGION.toString(),
TES_ROLE_ALIAS_NAME);
TES_ROLE_ALIAS_NAME, null);
// Force context to create TES now to that it subscribes to the role alias changes
kernel.getContext().get(TokenExchangeService.class);
while (kernel.getContext().get(CredentialRequestHandler.class).getAwsCredentialsBypassCache() == null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
@Tag("E2E")
class MqttTest extends BaseE2ETestCase {
public static final int NUM_MESSAGES = 50;
private static final String CERT_PATH = null;
private Kernel kernel;

protected MqttTest() throws Exception {
Expand All @@ -60,7 +61,7 @@ void GIVEN_mqttclient_WHEN_subscribe_and_publish_THEN_receives_all_messages()
kernel = new Kernel().parseArgs("-r", tempRootDir.toAbsolutePath().toString());
setDefaultRunWithUser(kernel);
deviceProvisioningHelper.updateKernelConfigWithIotConfiguration(kernel, thingInfo, TEST_REGION.toString(),
TES_ROLE_ALIAS_NAME);
TES_ROLE_ALIAS_NAME, CERT_PATH);

MqttClient client = kernel.getContext().get(MqttClient.class);
CountDownLatch cdl = new CountDownLatch(NUM_MESSAGES);
Expand All @@ -83,7 +84,7 @@ void GIVEN_mqttclient_WHEN_closes_new_connection_is_created_THEN_previous_sessio
kernel = new Kernel().parseArgs("-r", tempRootDir.toAbsolutePath().toString());
setDefaultRunWithUser(kernel);
deviceProvisioningHelper.updateKernelConfigWithIotConfiguration(kernel, thingInfo, TEST_REGION.toString(),
TES_ROLE_ALIAS_NAME);
TES_ROLE_ALIAS_NAME, CERT_PATH);

MqttClient client = kernel.getContext().get(MqttClient.class);

Expand Down Expand Up @@ -132,7 +133,7 @@ void GIVEN_mqttClient_WHEN_subscribe_interrupted_THEN_does_not_close_connection(
kernel = new Kernel().parseArgs("-r", tempRootDir.toAbsolutePath().toString());
setDefaultRunWithUser(kernel);
deviceProvisioningHelper.updateKernelConfigWithIotConfiguration(kernel, thingInfo, TEST_REGION.toString(),
TES_ROLE_ALIAS_NAME);
TES_ROLE_ALIAS_NAME, CERT_PATH);

MqttClient client = kernel.getContext().get(MqttClient.class);
Future<?> subscribeFuture = executorService.submit(() -> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -232,7 +232,7 @@ void GIVEN_iot_role_alias_WHEN_tes_is_queried_within_kernel_bypassing_http_serve
private static void provision(Kernel kernel) throws IOException, DeviceConfigurationException {
thingInfo = deviceProvisioningHelper.createThingForE2ETests();
deviceProvisioningHelper.setupIoTRoleForTes(roleName, roleAliasName, thingInfo.getCertificateArn());
deviceProvisioningHelper.updateKernelConfigWithIotConfiguration(kernel, thingInfo, AWS_REGION, roleAliasName);
deviceProvisioningHelper.updateKernelConfigWithIotConfiguration(kernel, thingInfo, AWS_REGION, roleAliasName, null);
}

private String getResponseString(URL url, String token) throws Exception {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.util.Arrays;
import java.util.Map;
Expand Down Expand Up @@ -350,16 +351,23 @@ private SdkHttpClient getSdkHttpClient() {
* @param thing thing info
* @param awsRegion aws region
* @param roleAliasName role alias for using IoT credentials endpoint
* @param userCertPath the path of certificates which users specify
* @throws IOException Exception while reading root CA from file
* @throws DeviceConfigurationException when the configuration parameters are not valid
*/
public void updateKernelConfigWithIotConfiguration(Kernel kernel, ThingInfo thing, String awsRegion,
String roleAliasName)
String roleAliasName, String userCertPath)
throws IOException, DeviceConfigurationException {
Path rootDir = kernel.getNucleusPaths().rootPath();
Path caFilePath = rootDir.resolve("rootCA.pem");
Path privKeyFilePath = rootDir.resolve("privKey.key");
Path certFilePath = rootDir.resolve("thingCert.crt");
Path certPath = kernel.getNucleusPaths().rootPath();

if (!Utils.isEmpty(userCertPath)) {
certPath = Paths.get(userCertPath);
Utils.createPaths(certPath);
}

Path caFilePath = certPath.resolve("rootCA.pem");
Path privKeyFilePath = certPath.resolve("privKey.key");
Path certFilePath = certPath.resolve("thingCert.crt");

downloadRootCAToFile(caFilePath.toFile());
try (CommitableFile cf = CommitableFile.of(privKeyFilePath, true)) {
Expand Down
13 changes: 11 additions & 2 deletions src/main/java/com/aws/greengrass/easysetup/GreengrassSetup.java
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,9 @@ public class GreengrassSetup {
+ "setup\n"
+ "\t\t\t\t\tsteps and (optional) provisions resources. Defaults to true.\n"
+ "\n\t--trusted-plugin, -tp\t\t(Optional) Path of a plugin jar file. The plugin will be included as "
+ "trusted plugin in nucleus. Specify multiple times for including multiple plugins.\n";
+ "trusted plugin in nucleus. Specify multiple times for including multiple plugins.\n"
+ "\n\t--cert-path\t\t\t(Optional) Path where certificates and keys are written "
+ "when --provision is true. If no path is specified, the root directory is used.\n";

private static final String SHOW_VERSION_RESPONSE = "AWS Greengrass v%s";

Expand Down Expand Up @@ -186,6 +188,8 @@ public class GreengrassSetup {
private static final String TRUSTED_PLUGIN_ARG = "--trusted-plugin";
private static final String TRUSTED_PLUGIN_ARG_SHORT = "-tp";

private static final String CERT_PATH_ARG = "--cert-path";

private static final String GGC_USER = "ggc_user";
private static final String GGC_GROUP = "ggc_group";
private static final String DEFAULT_POSIX_USER = String.format("%s:%s", GGC_USER, GGC_GROUP);
Expand Down Expand Up @@ -217,6 +221,7 @@ public class GreengrassSetup {
private boolean setupSystemService = SETUP_SYSTEM_SERVICE_ARG_DEFAULT;
private boolean kernelStart = KERNEL_START_ARG_DEFAULT;
private boolean deployDevTools = DEPLOY_DEV_TOOLS_ARG_DEFAULT;
private String certPath;
private Platform platform;
private Kernel kernel;
private List<String> trustedPluginPaths;
Expand Down Expand Up @@ -459,6 +464,9 @@ void parseArgs() {
}
trustedPluginPaths.add(pluginJarPath);
break;
case CERT_PATH_ARG:
this.certPath = getArg();
break;
default:
RuntimeException rte =
new RuntimeException(String.format("Undefined command line argument: %s", arg));
Expand Down Expand Up @@ -519,7 +527,8 @@ void provision(Kernel kernel) throws IOException, DeviceConfigurationException {
deviceProvisioningHelper.setupIoTRoleForTes(tesRoleName, tesRoleAliasName, thingInfo.getCertificateArn());
deviceProvisioningHelper.createAndAttachRolePolicy(tesRoleName, Region.of(awsRegion));
outStream.println("Configuring Nucleus with provisioned resource details...");
deviceProvisioningHelper.updateKernelConfigWithIotConfiguration(kernel, thingInfo, awsRegion, tesRoleAliasName);
deviceProvisioningHelper.updateKernelConfigWithIotConfiguration(kernel, thingInfo, awsRegion, tesRoleAliasName,
certPath);
outStream.println("Successfully configured Nucleus with provisioned resource details!");
if (deployDevTools) {
deviceProvisioningHelper.createInitialDeploymentIfNeeded(thingInfo, thingGroupName,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -411,7 +411,7 @@ void GIVEN_test_update_device_config_WHEN_thing_info_provided_THEN_add_config_to
deviceProvisioningHelper.updateKernelConfigWithIotConfiguration(kernel,
new DeviceProvisioningHelper.ThingInfo(getThingArn(), "thingname", "certarn", "certid", "certpem",
KeyPair.builder().privateKey("privateKey").publicKey("publicKey").build(), "xxxxxx-ats.iot.us-east-1.amazonaws.com",
"xxxxxx.credentials.iot.us-east-1.amazonaws.com"), TEST_REGION, "roleAliasName");
"xxxxxx.credentials.iot.us-east-1.amazonaws.com"), TEST_REGION, "roleAliasName", null);
assertEquals("thingname", kernel.getConfig().lookup(SYSTEM_NAMESPACE_KEY, DEVICE_PARAM_THING_NAME).getOnce());
assertEquals("roleAliasName", kernel.getConfig()
.lookup(SERVICES_NAMESPACE_TOPIC, DEFAULT_NUCLEUS_COMPONENT_NAME, CONFIGURATION_CONFIG_KEY,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import org.junit.jupiter.api.extension.ExtensionContext;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.CsvSource;
import org.junit.jupiter.params.provider.MethodSource;
import org.mockito.Answers;
import org.mockito.ArgumentCaptor;
Expand Down Expand Up @@ -104,11 +105,43 @@ void GIVEN_setup_script_WHEN_script_is_used_THEN_setup_actions_are_performed() t
greengrassSetup.provision(kernel);
verify(deviceProvisioningHelper, times(1)).createThing(any(), any(), any());
verify(deviceProvisioningHelper, times(1)).addThingToGroup(any(), any(), any());
verify(deviceProvisioningHelper, times(1)).updateKernelConfigWithIotConfiguration(any(), any(), any(), any());
verify(deviceProvisioningHelper, times(1)).updateKernelConfigWithIotConfiguration(any(), any(), any(), any(), any());
verify(deviceProvisioningHelper, times(1)).setupIoTRoleForTes(any(), any(), any());
verify(deviceProvisioningHelper, times(1)).createAndAttachRolePolicy(any(), any());
}

@ParameterizedTest
@CsvSource({"--cert-path,/a/b"})
void GIVEN_setup_script_WHEN_script_is_used_THEN_setup_actions_are_performed_for_cert_path_specified(String certOption, String certPath) throws Exception {
when(deviceProvisioningHelper.createThing(any(), any(), any())).thenReturn(thingInfo);
greengrassSetup =
new GreengrassSetup(System.out, System.err, deviceProvisioningHelper, platform, kernel, "--config",
"mock_config_path", "--root", "mock_root", "--thing-name", "mock_thing_name",
"--thing-group-name", "mock_thing_group_name", "--thing-policy-name", "mock_thing_policy_name",
"--tes-role-name", "mock_tes_role_name", "--tes-role-alias-name", "mock_tes_role_alias_name",
"--provision", "true", "--aws-region", "us-east-1", "-ss", "false", certOption, certPath);
greengrassSetup.parseArgs();
greengrassSetup.setDeviceProvisioningHelper(deviceProvisioningHelper);
greengrassSetup.provision(kernel);
verify(deviceProvisioningHelper, times(1)).updateKernelConfigWithIotConfiguration(any(), any(), any(), any(), eq(certPath));
}


@Test
void GIVEN_setup_script_WHEN_script_is_used_THEN_setup_actions_are_performed_for_cert_path_not_specified() throws Exception {
when(deviceProvisioningHelper.createThing(any(), any(), any())).thenReturn(thingInfo);
greengrassSetup =
new GreengrassSetup(System.out, System.err, deviceProvisioningHelper, platform, kernel, "--config",
"mock_config_path", "--root", "mock_root", "--thing-name", "mock_thing_name",
"--thing-group-name", "mock_thing_group_name", "--thing-policy-name", "mock_thing_policy_name",
"--tes-role-name", "mock_tes_role_name", "--tes-role-alias-name", "mock_tes_role_alias_name",
"--provision", "true", "--aws-region", "us-east-1", "-ss", "false");
greengrassSetup.parseArgs();
greengrassSetup.setDeviceProvisioningHelper(deviceProvisioningHelper);
greengrassSetup.provision(kernel);
verify(deviceProvisioningHelper, times(1)).updateKernelConfigWithIotConfiguration(any(), any(), any(), any(), eq(null));
}

@Test
@DisabledOnOs(OS.WINDOWS)
void GIVEN_no_default_user_WHEN_script_is_used_THEN_default_user_created_and_added_to_config() throws Exception {
Expand Down Expand Up @@ -361,7 +394,7 @@ void GIVEN_setup_script_WHEN_no_thing_policy_name_args_provided_THEN_policy_setu
greengrassSetup.setDeviceProvisioningHelper(deviceProvisioningHelper);
greengrassSetup.provision(kernel);
verify(deviceProvisioningHelper, times(1)).createThing(any(), any(), any());
verify(deviceProvisioningHelper, times(1)).updateKernelConfigWithIotConfiguration(any(), any(), any(), any());
verify(deviceProvisioningHelper, times(1)).updateKernelConfigWithIotConfiguration(any(), any(), any(), any(), any());
verify(deviceProvisioningHelper, times(1)).setupIoTRoleForTes(any(), any(), any());
verify(deviceProvisioningHelper, times(1)).createAndAttachRolePolicy(any(), any());
}
Expand All @@ -377,7 +410,7 @@ void GIVEN_setup_script_WHEN_no_tes_role_args_provided_THEN_tes_setup_with_defau
greengrassSetup.setDeviceProvisioningHelper(deviceProvisioningHelper);
greengrassSetup.provision(kernel);
verify(deviceProvisioningHelper, times(1)).createThing(any(), any(), any());
verify(deviceProvisioningHelper, times(1)).updateKernelConfigWithIotConfiguration(any(), any(), any(), any());
verify(deviceProvisioningHelper, times(1)).updateKernelConfigWithIotConfiguration(any(), any(), any(), any(), any());
verify(deviceProvisioningHelper, times(1)).setupIoTRoleForTes(any(), any(), any());
verify(deviceProvisioningHelper, times(1)).createAndAttachRolePolicy(any(), any());
}
Expand All @@ -395,7 +428,7 @@ void GIVEN_setup_script_WHEN_script_is_used_with_short_arg_notations_THEN_setup_
greengrassSetup.provision(kernel);
verify(deviceProvisioningHelper, times(1)).createThing(any(), any(), any());
verify(deviceProvisioningHelper, times(1)).addThingToGroup(any(), any(), any());
verify(deviceProvisioningHelper, times(1)).updateKernelConfigWithIotConfiguration(any(), any(), any(), any());
verify(deviceProvisioningHelper, times(1)).updateKernelConfigWithIotConfiguration(any(), any(), any(), any(), any());
verify(deviceProvisioningHelper, times(1)).setupIoTRoleForTes(any(), any(), any());
verify(deviceProvisioningHelper, times(1)).createAndAttachRolePolicy(any(), any());
}
Expand Down

0 comments on commit dd8e059

Please sign in to comment.