From cb0eb058a632d473b51d57ca08936b9e4bb99875 Mon Sep 17 00:00:00 2001 From: Anubhav Rawal Date: Fri, 13 Dec 2024 00:09:03 +0000 Subject: [PATCH 1/5] Add supported sample recipe types --- docs/examples/supported_lifecyle_types/1.json | 53 ++++++ docs/examples/supported_lifecyle_types/2.json | 153 ++++++++++++++++++ docs/examples/supported_lifecyle_types/3.yaml | 43 +++++ docs/examples/supported_lifecyle_types/4.yaml | 35 ++++ misc/dictionary.txt | 4 + 5 files changed, 288 insertions(+) create mode 100644 docs/examples/supported_lifecyle_types/1.json create mode 100644 docs/examples/supported_lifecyle_types/2.json create mode 100644 docs/examples/supported_lifecyle_types/3.yaml create mode 100644 docs/examples/supported_lifecyle_types/4.yaml diff --git a/docs/examples/supported_lifecyle_types/1.json b/docs/examples/supported_lifecyle_types/1.json new file mode 100644 index 000000000..906a98956 --- /dev/null +++ b/docs/examples/supported_lifecyle_types/1.json @@ -0,0 +1,53 @@ +{ + // Notice: All Key's are case sensitive + "RecipeFormatVersion": "2020-01-25", + "ComponentName": "s3.list.bucket.Python", + "ComponentVersion": "1.0.4", + "ComponentType": "aws.greengrass.generic", + "ComponentDescription": "This example Python component", + "ComponentPublisher": "TestUser", + "ComponentDependencies": { + "aws.greengrass.TokenExchangeService": { + "VersionRequirement": ">=0.0.0", + "DependencyType": "HARD" + }, + "aws.greengrass.NucleusLite": { + "VersionRequirement": ">=2.0.0", + "DependencyType": "HARD" + } + }, + "Manifests": [ + { + "Platform": { + "os": "linux", + "runtime": "*", // Notice this is a required new field for GGLite + "architecture": "amd64" + }, + "Lifecycle": { + "run": { + "RequiresPrivilege": true, + "Script": "python3 {artifacts:path}/python_list_S3_bucket.py" + }, + "install": { + "RequiresPrivilege": true, + "Script": "pip install boto3" + } + // Notice bootstrap, shutdown and recover aren't currently supported + }, + "Artifacts": [ + { + // Notice URI needs to Uri(Camel case) + "Uri": "s3://gglite-artifact/python_list_S3_bucket.py", + "Digest": "---", + "Algorithm": "SHA-256", + "Unarchive": "NONE", + "Permission": { + "Read": "OWNER", + "Execute": "NONE" + } + } + ] + } + ], + "Lifecycle": {} +} diff --git a/docs/examples/supported_lifecyle_types/2.json b/docs/examples/supported_lifecyle_types/2.json new file mode 100644 index 000000000..0923ede37 --- /dev/null +++ b/docs/examples/supported_lifecyle_types/2.json @@ -0,0 +1,153 @@ +{ + // Notice: All Key's are case sensitive + "RecipeFormatVersion": "2020-01-25", + "ComponentName": "aws.greengrass.SecureTunneling", + "ComponentVersion": "1.0.100", + "ComponentType": "aws.greengrass.generic", + "ComponentDescription": "Enables AWS IoT Secure Tunneling connections that you can use to establish secure bidirectional communications with Greengrass core devices that are behind restricted firewalls.", + "ComponentPublisher": "AWS", + "ComponentConfiguration": { + "DefaultConfiguration": { + "accessControl": { + "aws.greengrass.ipc.mqttproxy": { + "aws.greengrass.SecureTunneling:mqttproxy:1": { + "policyDescription": "Access to tunnel notification pubsub topic", + "operations": ["aws.greengrass#SubscribeToIoTCore"], + "resources": ["$aws/things/+/tunnels/notify"] + } + } + }, + "OS_DIST_INFO": "auto" + } + }, + "ComponentDependencies": { + "aws.greengrass.NucleusLite": { + "VersionRequirement": ">=2.0.0", + "DependencyType": "SOFT" + } + }, + "Manifests": [ + { + "Platform": { + "os": "linux", + "runtime": "*", + "architecture": "amd64" // Notice regex isn't currently supported + }, + "Lifecycle": { + "run": { + "Script": "java -jar {artifacts:path}/GreengrassV2SecureTunnelingComponent-1.0-all.jar linux x86_64" + } + }, + "Artifacts": [ + { + "Uri": "s3://gglite-artifact/GreengrassV2SecureTunnelingComponent-1.0-all.jar", + "Digest": "----", + "Algorithm": "SHA-256", + "Unarchive": "NONE", + "Permission": { + "Read": "OWNER", + "Execute": "NONE" + } + } + ] + }, + { + "Platform": { + "os": "linux", + "runtime": "*", + "architecture": "x86_64" // Repeated Lifecycle with just architecture change + }, + "Lifecycle": { + "run": { + "Script": "java -jar {artifacts:path}/GreengrassV2SecureTunnelingComponent-1.0-all.jar linux x86_64" + } + }, + "Artifacts": [ + { + "Uri": "s3://gglite-artifact/GreengrassV2SecureTunnelingComponent-1.0-all.jar", + "Digest": "----", + "Algorithm": "SHA-256", + "Unarchive": "NONE", + "Permission": { + "Read": "OWNER", + "Execute": "NONE" + } + } + ] + }, + { + "Platform": { + "os": "linux", + "runtime": "*", + "architecture": "aarch64" + }, + "Lifecycle": { + "run": { + "Script": "java -jar {artifacts:path}/GreengrassV2SecureTunnelingComponent-1.0-all.jar linux aarch64" + } + }, + "Artifacts": [ + { + "Uri": "s3://gglite-artifact/GreengrassV2SecureTunnelingComponent-1.0-all.jar", + "Digest": "----", + "Algorithm": "SHA-256", + "Unarchive": "NONE", + "Permission": { + "Read": "OWNER", + "Execute": "NONE" + } + } + ] + }, + { + "Platform": { + "os": "linux", + "runtime": "*", + "architecture": "armv8" + }, + "Lifecycle": { + "run": { + "Script": "java -jar {artifacts:path}/GreengrassV2SecureTunnelingComponent-1.0-all.jar linux aarch64" + } + }, + "Artifacts": [ + { + "Uri": "s3://gglite-artifact/GreengrassV2SecureTunnelingComponent-1.0-all.jar", + "Digest": "----", + "Algorithm": "SHA-256", + "Unarchive": "NONE", + "Permission": { + "Read": "OWNER", + "Execute": "NONE" + } + } + ] + }, + { + "Platform": { + "os": "linux", + "architecture.detail": "armv7l", + "runtime": "*", + "architecture": "arm" + }, + "Lifecycle": { + "run": { + "Script": "java -jar {artifacts:path}/GreengrassV2SecureTunnelingComponent-1.0-all.jar linux armv7l" + } + }, + "Artifacts": [ + { + "Uri": "s3://gglite-artifact/GreengrassV2SecureTunnelingComponent-1.0-all.jar", + "Digest": "----", + "Algorithm": "SHA-256", + "Unarchive": "NONE", + "Permission": { + "Read": "OWNER", + "Execute": "NONE" + } + } + ] + } + ], + "Lifecycle": {} +} diff --git a/docs/examples/supported_lifecyle_types/3.yaml b/docs/examples/supported_lifecyle_types/3.yaml new file mode 100644 index 000000000..395439bc9 --- /dev/null +++ b/docs/examples/supported_lifecyle_types/3.yaml @@ -0,0 +1,43 @@ +--- +# Notice: All Key's are case sensitive +RecipeFormatVersion: "2020-01-25" +ComponentName: "s3.list.bucket.Python" +ComponentVersion: "1.0.4" +ComponentType: "aws.greengrass.generic" +ComponentDescription: "This example Python component" +ComponentPublisher: "Amazon" +ComponentDependencies: + aws.greengrass.TokenExchangeService: + VersionRequirement: ">=0.0.0" + DependencyType: "HARD" + aws.greengrass.NucleusLite: + VersionRequirement: ">=2.0.0" + DependencyType: "HARD" +ComponentConfiguration: + DefaultConfiguration: + containerMode: "auto" +Manifests: + - Platform: + os: "linux" + runtime: "aws_nucleus_lite" # Notice this is a required new field for GGLite + Lifecycle: {} #Notice No Lifecycle or Selection so Global lifecycle is used + Artifacts: + - Uri: "s3://gglite-artifact/python_list_S3_bucket.py" + Digest: "----" + Algorithm: "SHA-256" + Unarchive: "NONE" + Permission: + Read: "OWNER" + Execute: "NONE" +Lifecycle: + # Notice all is case sensitive and is required when no selection is provided + - all: + Setenv: + TEST_VALUE: "{configuration:/containerMode}" + run: + # Notice Skipif is not supported + RequiresPrivilege: true + Script: "python3 {artifacts:path}/python_list_S3_bucket.py" + Timeout: 200 #Seconds + Setenv: + TEST_VALUE: sample diff --git a/docs/examples/supported_lifecyle_types/4.yaml b/docs/examples/supported_lifecyle_types/4.yaml new file mode 100644 index 000000000..cbe2c5ad5 --- /dev/null +++ b/docs/examples/supported_lifecyle_types/4.yaml @@ -0,0 +1,35 @@ +--- +# Notice: All Key's are case sensitive +RecipeFormatVersion: "2020-01-25" +ComponentName: "s3.list.bucket.Python" +ComponentVersion: "1.0.4" +ComponentType: "aws.greengrass.generic" +ComponentDescription: "This example Python component" +ComponentPublisher: "Amazon" +ComponentDependencies: + aws.greengrass.TokenExchangeService: + VersionRequirement: ">=0.0.0" + DependencyType: "HARD" + aws.greengrass.Nucleus: + VersionRequirement: ">=2.0.0 <3.0.0" + DependencyType: "HARD" +Manifests: + - Platform: + os: "linux" + runtime: "*" # Notice this is a required new field for GGLite + Lifecycle: {} + Selections: + - linux + Artifacts: + - Uri: "s3://gglite-artifact/python_list_S3_bucket.py" + Digest: "----" + Algorithm: "SHA-256" + Unarchive: "NONE" + Permission: + Read: "OWNER" + Execute: "NONE" +Lifecycle: + - linux: + run: + RequiresPrivilege: true + Script: "python3 {artifacts:path}/python_list_S3_bucket.py" diff --git a/misc/dictionary.txt b/misc/dictionary.txt index a010480ad..69687ff2f 100644 --- a/misc/dictionary.txt +++ b/misc/dictionary.txt @@ -1,7 +1,9 @@ aarch64 ALLOCN argp +armv awsiotsdk +boto cgexec clientfd clientv @@ -98,7 +100,9 @@ semp serde serverd SERVUNAVAIL +Setenv sigv4 +Skipif ssssssouso statusd stdenv From a6176c0c3188bc435e9e1ba4767554b0c2f78f93 Mon Sep 17 00:00:00 2001 From: Anubhav Rawal Date: Fri, 13 Dec 2024 01:20:58 +0000 Subject: [PATCH 2/5] Enable Fleet-provisioner to run simultaneously with iotcored --- fleet-provisioning/bin/fleet-provisioning.c | 9 +- .../include/fleet-provisioning.h | 3 +- fleet-provisioning/src/entry.c | 215 ++++++++++++++---- fleet-provisioning/src/provisioner.c | 83 +++++-- fleet-provisioning/src/provisioner.h | 3 +- 5 files changed, 243 insertions(+), 70 deletions(-) diff --git a/fleet-provisioning/bin/fleet-provisioning.c b/fleet-provisioning/bin/fleet-provisioning.c index 98358b020..1d7b79042 100644 --- a/fleet-provisioning/bin/fleet-provisioning.c +++ b/fleet-provisioning/bin/fleet-provisioning.c @@ -3,8 +3,10 @@ // SPDX-License-Identifier: Apache-2.0 #include "fleet-provisioning.h" +#include #include #include +#include #include #include #include @@ -98,8 +100,13 @@ int main(int argc, char **argv) { argp_parse(&argp, argc, argv, 0, 0, &args); args.iotcored_path = iotcored_path; - GglError ret = run_fleet_prov(&args); + pid_t pid = -1; + GglError ret = run_fleet_prov(&args, &pid); if (ret != GGL_ERR_OK) { + if (pid != -1) { + GGL_LOGE("Something went wrong. Killing iotcored"); + ggl_exec_kill_process(pid); + } return 1; } } diff --git a/fleet-provisioning/include/fleet-provisioning.h b/fleet-provisioning/include/fleet-provisioning.h index 52fcc2415..8d29795e1 100644 --- a/fleet-provisioning/include/fleet-provisioning.h +++ b/fleet-provisioning/include/fleet-provisioning.h @@ -5,6 +5,7 @@ #ifndef FLEET_PROVISIONING_H #define FLEET_PROVISIONING_H +#include #include typedef struct { @@ -17,5 +18,5 @@ typedef struct { char *iotcored_path; } FleetProvArgs; -GglError run_fleet_prov(FleetProvArgs *args); +GglError run_fleet_prov(FleetProvArgs *args, pid_t *pid); #endif diff --git a/fleet-provisioning/src/entry.c b/fleet-provisioning/src/entry.c index 87e19f3b3..32a23f3c1 100644 --- a/fleet-provisioning/src/entry.c +++ b/fleet-provisioning/src/entry.c @@ -6,23 +6,27 @@ #include "generate_certificate.h" #include "ggl/exec.h" #include "provisioner.h" +#include "stdbool.h" #include +#include #include #include #include +#include #include #include +#include #include #include #include #include -#include #include #include #define MAX_TEMPLATE_LEN 129 #define MAX_ENDPOINT_LENGTH 129 #define MAX_TEMPLATE_PARAM_LEN 4096 +#define MAX_PATH_LEN 4096 static GglError start_iotcored(FleetProvArgs *args, pid_t *iotcored_pid) { char *iotcore_d_args[] @@ -132,7 +136,7 @@ static GglError fetch_from_db(FleetProvArgs *args) { GGL_STR("iotDataEndpoint") ), GGL_OBJ_BUF(data_endpoint), - &(int64_t) { 0 } + &(int64_t) { 3 } ); if (ret != GGL_ERR_OK) { return ret; @@ -187,8 +191,98 @@ static GglError fetch_from_db(FleetProvArgs *args) { return GGL_ERR_OK; } -GglError run_fleet_prov(FleetProvArgs *args) { - GglError ret = fetch_from_db(args); +static GglError update_cred_access(void) { + char *args[] = { "chown", "-R", "ggcore:ggcore", "/ggcredentials/", NULL }; + + GglError ret = ggl_exec_command(args); + if (ret != GGL_ERR_OK) { + GGL_LOGE("Failed to change ownership of certificates"); + return ret; + } + + char *args_reboot[] = { "systemctl", "reboot", NULL }; + ret = ggl_exec_command(args_reboot); + if (ret != GGL_ERR_OK) { + GGL_LOGE("Failed to reboot the device"); + return ret; + } + + return GGL_ERR_OK; +} + +static GglError update_iot_endpoints(void) { + static uint8_t endpoint_mem[2048] = { 0 }; + GglBuffer data_endpoint = GGL_BUF(endpoint_mem); + GglError ret = ggl_gg_config_read_str( + GGL_BUF_LIST( + GGL_STR("services"), + GGL_STR("aws.greengrass.fleet_provisioning"), + GGL_STR("configuration"), + GGL_STR("iotDataEndpoint") + ), + &data_endpoint + ); + if (ret != GGL_ERR_OK) { + return ret; + } + + ret = ggl_gg_config_write( + GGL_BUF_LIST( + GGL_STR("system"), + GGL_STR("aws.greengrass.NucleusLite"), + GGL_STR("configuration"), + GGL_STR("iotDataEndpoint") + ), + GGL_OBJ_BUF(data_endpoint), + &(int64_t) { 3 } + ); + if (ret != GGL_ERR_OK) { + return ret; + } + + GglBuffer cred_endpoint = GGL_BUF(endpoint_mem); + ret = ggl_gg_config_read_str( + GGL_BUF_LIST( + GGL_STR("services"), + GGL_STR("aws.greengrass.fleet_provisioning"), + GGL_STR("configuration"), + GGL_STR("iotCredEndpoint") + ), + &cred_endpoint + ); + if (ret != GGL_ERR_OK) { + return ret; + } + + ret = ggl_gg_config_write( + GGL_BUF_LIST( + GGL_STR("system"), + GGL_STR("aws.greengrass.NucleusLite"), + GGL_STR("configuration"), + GGL_STR("iotCredEndpoint") + ), + GGL_OBJ_BUF(cred_endpoint), + &(int64_t) { 3 } + ); + if (ret != GGL_ERR_OK) { + return ret; + } + + return GGL_ERR_OK; +} + +GglError run_fleet_prov(FleetProvArgs *args, pid_t *pid) { + GglBuffer ggcredentials_path = GGL_STR("/ggcredentials"); + + int config_dir; + GglError ret + = ggl_dir_open(ggcredentials_path, O_RDONLY, false, &config_dir); + if (ret != GGL_ERR_OK) { + GGL_LOGI("Could not open ggcredentials directory."); + return GGL_ERR_FAILURE; + } + + ret = fetch_from_db(args); if (ret != GGL_ERR_OK) { return ret; } @@ -211,28 +305,60 @@ GglError run_fleet_prov(FleetProvArgs *args) { if (ret != GGL_ERR_OK) { return ret; } + *pid = iotcored_pid; - static char private_file_path[4096] = { 0 }; - static char public_file_path[4096] = { 0 }; - static char csr_file_path[4096] = { 0 }; - static char cert_file_path[4096] = { 0 }; - - strncat(private_file_path, (char *) root_dir.data, root_dir.len); - strncat(public_file_path, (char *) root_dir.data, root_dir.len); - strncat(csr_file_path, (char *) root_dir.data, root_dir.len); - strncat(cert_file_path, (char *) root_dir.data, root_dir.len); + static uint8_t private_file_path_mem[MAX_PATH_LEN] = { 0 }; + GglByteVec private_file_path_vec = GGL_BYTE_VEC(private_file_path_mem); + ret = ggl_byte_vec_append(&private_file_path_vec, ggcredentials_path); + ggl_byte_vec_chain_append( + &ret, &private_file_path_vec, GGL_STR("/private_key.pem.key") + ); + ggl_byte_vec_chain_push(&ret, &private_file_path_vec, '\0'); + if (ret != GGL_ERR_OK) { + GGL_LOGE("Error to append private key path"); + return ret; + } - strncat( - private_file_path, "/private_key.pem", strlen("/private_key.pem.key") + static uint8_t public_file_path_mem[MAX_PATH_LEN] = { 0 }; + GglByteVec public_file_path_vec = GGL_BYTE_VEC(public_file_path_mem); + ret = ggl_byte_vec_append(&public_file_path_vec, ggcredentials_path); + ggl_byte_vec_chain_append( + &ret, &public_file_path_vec, GGL_STR("/public_key.pem.key") ); - strncat(public_file_path, "/public_key.pem", strlen("/public_key.pem")); - strncat(csr_file_path, "/csr.pem", strlen("/csr.pem")); - strncat( - cert_file_path, "/certificate.pem.crt", strlen("/certificate.pem.crt") + ggl_byte_vec_chain_push(&ret, &public_file_path_vec, '\0'); + if (ret != GGL_ERR_OK) { + GGL_LOGE("Error to append public key path"); + return ret; + } + + static uint8_t cert_file_path_mem[MAX_PATH_LEN] = { 0 }; + GglByteVec cert_file_path_vec = GGL_BYTE_VEC(cert_file_path_mem); + ret = ggl_byte_vec_append(&cert_file_path_vec, ggcredentials_path); + ggl_byte_vec_chain_append( + &ret, &cert_file_path_vec, GGL_STR("/certificate.pem.crt") ); + ggl_byte_vec_chain_push(&ret, &cert_file_path_vec, '\0'); + if (ret != GGL_ERR_OK) { + GGL_LOGE("Error to append certificate key path"); + return ret; + } + + static uint8_t csr_file_path_mem[MAX_PATH_LEN] = { 0 }; + GglByteVec csr_file_path_vec = GGL_BYTE_VEC(csr_file_path_mem); + ret = ggl_byte_vec_append(&csr_file_path_vec, ggcredentials_path); + ggl_byte_vec_chain_append(&ret, &csr_file_path_vec, GGL_STR("/csr.pem")); + ggl_byte_vec_chain_push(&ret, &csr_file_path_vec, '\0'); + if (ret != GGL_ERR_OK) { + GGL_LOGE("Error to append csr path"); + return ret; + } generate_key_files( - pkey, csr_req, private_file_path, public_file_path, csr_file_path + pkey, + csr_req, + (char *) private_file_path_vec.buf.data, + (char *) public_file_path_vec.buf.data, + (char *) csr_file_path_vec.buf.data ); EVP_PKEY_free(pkey); @@ -240,48 +366,43 @@ GglError run_fleet_prov(FleetProvArgs *args) { ret = ggl_gg_config_write( GGL_BUF_LIST(GGL_STR("system"), GGL_STR("privateKeyPath")), - GGL_OBJ_BUF((GglBuffer) { .data = (uint8_t *) private_file_path, - .len = strlen(private_file_path) }), - &(int64_t) { 0 } + GGL_OBJ_BUF(private_file_path_vec.buf), + &(int64_t) { 3 } ); if (ret != GGL_ERR_OK) { - ggl_exec_kill_process(iotcored_pid); return ret; } - static char csr_buf[2048] = { 0 }; - FILE *fp; - ulong file_size; + static uint8_t csr_mem[2048] = { 0 }; - // Open the file in binary mode - fp = fopen("./csr.pem", "rb"); - if (fp == NULL) { - perror("Error opening file"); + // Try to read csr into memory + int fd = -1; + ret = ggl_file_open(csr_file_path_vec.buf, O_RDONLY, 0, &fd); + if (ret != GGL_ERR_OK) { + GGL_LOGE("Error opening csr file %d", ret); return 1; } - // Get the file size - fseek(fp, 0, SEEK_END); - file_size = (ulong) ftell(fp); - fseek(fp, 0, SEEK_SET); - - // Read the file into the buffer - size_t read_size = fread(csr_buf, 1, file_size, fp); - - // Close the file - fclose(fp); - - if (read_size != file_size) { - GGL_LOGE("Failed to read th whole file."); + GglBuffer csr_buf = GGL_BUF(csr_mem); + ret = ggl_file_read(fd, &csr_buf); + if (ret != GGL_ERR_OK) { + GGL_LOGE("Failed to read csr file."); return GGL_ERR_FAILURE; } + GGL_LOGD("CSR successfully read.."); - ret = make_request(csr_buf, cert_file_path, iotcored_pid); + ret = make_request(csr_buf, cert_file_path_vec.buf, iotcored_pid); + if (ret != GGL_ERR_OK) { + return ret; + } + ret = update_iot_endpoints(); if (ret != GGL_ERR_OK) { - GGL_LOGE("Something went wrong. Killing iotcored"); - ggl_exec_kill_process(iotcored_pid); + return ret; + } + ret = update_cred_access(); + if (ret != GGL_ERR_OK) { return ret; } diff --git a/fleet-provisioning/src/provisioner.c b/fleet-provisioning/src/provisioner.c index 7eaf84443..e6996cdcb 100644 --- a/fleet-provisioning/src/provisioner.c +++ b/fleet-provisioning/src/provisioner.c @@ -20,6 +20,8 @@ #include #include #include +#include +#include #include #define TEMPLATE_PARAM_BUFFER_SIZE 10000 @@ -37,6 +39,8 @@ static uint8_t big_buffer_for_bump[4096]; GglObject csr_payload_json_obj; char *global_cert_file_path; +atomic_bool complete_status = false; + static GglBuffer iotcored = GGL_STR("iotcoredfleet"); static const char *certificate_response_url @@ -47,7 +51,7 @@ static const char *certificate_response_reject_url static const char *cert_request_url = "$aws/certificates/create-from-csr/json"; -static int request_thing_name(GglObject *cert_owner_gg_obj) { +static GglError request_thing_name(GglObject *cert_owner_gg_obj) { static uint8_t temp_payload_alloc2[2000] = { 0 }; GglBuffer thing_request_buf = GGL_BUF(temp_payload_alloc2); @@ -104,7 +108,7 @@ static int request_thing_name(GglObject *cert_owner_gg_obj) { (int) iotcored.len, iotcored.data ); - return EPROTO; + return GGL_ERR_FAILURE; } GGL_LOGI("Sent MQTT thing Register publish."); @@ -160,7 +164,7 @@ static GglError set_global_values(pid_t iotcored_pid) { global_register_thing_url, strlen(global_register_thing_url) ); - strncat(global_register_thing_reject_url, "/rejected", strlen("/accepted")); + strncat(global_register_thing_reject_url, "/rejected", strlen("/rejected")); // Fetch Template Parameters // TODO: Use args passed from entry.c @@ -195,6 +199,14 @@ static GglError subscribe_callback(void *ctx, uint32_t handle, GglObject data) { return ret; } + GGL_LOGI( + "Got message from IoT Core; topic: %.*s, payload: %.*s.", + (int) topic->len, + topic->data, + (int) payload->len, + payload->data + ); + if (strncmp((char *) topic->data, certificate_response_url, topic->len) == 0) { GglBumpAlloc balloc = ggl_bump_alloc_init(GGL_BUF(big_buffer_for_bump)); @@ -241,7 +253,7 @@ static GglError subscribe_callback(void *ctx, uint32_t handle, GglObject data) { GGL_OBJ_BUF((GglBuffer ) { .data = (uint8_t *) global_cert_file_path, .len = strlen(global_cert_file_path) }), - &(int64_t) { 0 } + &(int64_t) { 3 } ); if (ret != GGL_ERR_OK) { return ret; @@ -266,7 +278,11 @@ static GglError subscribe_callback(void *ctx, uint32_t handle, GglObject data) { // Now that we have a certificate make a call to register a // thing based on that certificate - request_thing_name(val); + ret = request_thing_name(val); + if (ret != GGL_ERR_OK) { + GGL_LOGE("Requesting thing name failed"); + return ret; + } } } } else if (strncmp( @@ -298,7 +314,7 @@ static GglError subscribe_callback(void *ctx, uint32_t handle, GglObject data) { ret = ggl_gg_config_write( GGL_BUF_LIST(GGL_STR("system"), GGL_STR("thingName")), *val, - &(int64_t) { 0 } + &(int64_t) { 3 } ); if (ret != GGL_ERR_OK) { return ret; @@ -309,6 +325,7 @@ static GglError subscribe_callback(void *ctx, uint32_t handle, GglObject data) { ggl_exec_kill_process(global_iotcored_pid); // TODO: Find a way to terminate cleanly with iotcored + atomic_store(&complete_status, true); } } else { GGL_LOGI( @@ -324,9 +341,9 @@ static GglError subscribe_callback(void *ctx, uint32_t handle, GglObject data) { } GglError make_request( - char *csr_as_string, char *cert_file_path, pid_t iotcored_pid + GglBuffer csr_as_ggl_buffer, GglBuffer cert_file_path, pid_t iotcored_pid ) { - global_cert_file_path = cert_file_path; + global_cert_file_path = (char *) cert_file_path.data; GglError ret = set_global_values(iotcored_pid); if (ret != GGL_ERR_OK) { @@ -366,7 +383,6 @@ GglError make_request( } GGL_LOGI("Successfully set csr accepted subscription."); - // NOLINTNEXTLINE(concurrency-mt-unsafe) ggl_sleep(2); // Subscribe to csr reject topic @@ -398,7 +414,6 @@ GglError make_request( } GGL_LOGI("Successfully set csr rejected subscription."); - // NOLINTNEXTLINE(concurrency-mt-unsafe) ggl_sleep(2); // Subscribe to register thing success topic @@ -430,15 +445,41 @@ GglError make_request( } GGL_LOGI("Successfully set thing accepted subscription."); - // NOLINTNEXTLINE(concurrency-mt-unsafe) + // Subscribe to register thing success topic + GglMap subscribe_thing_reject_args = GGL_MAP( + { GGL_STR("topic_filter"), + GGL_OBJ_BUF((GglBuffer + ) { .len = strlen(global_register_thing_reject_url), + .data = (uint8_t *) global_register_thing_reject_url }) }, + ); + + GglError return_thing_sub_reject = ggl_subscribe( + iotcored, + GGL_STR("subscribe"), + subscribe_thing_reject_args, + subscribe_callback, + NULL, + NULL, + NULL, + NULL + ); + if (return_thing_sub_reject != GGL_ERR_OK) { + GGL_LOGE( + "Failed to send thing accepted notify message to %.*s, Error: %d", + (int) iotcored.len, + iotcored.data, + EPROTO + ); + return GGL_ERR_FAILURE; + } + GGL_LOGI("Successfully set thing rejected subscription."); + ggl_sleep(2); // Create a json payload object - GglObject csr_payload_obj = GGL_OBJ_MAP( - GGL_MAP({ GGL_STR("certificateSigningRequest"), - GGL_OBJ_BUF((GglBuffer) { .data = (uint8_t *) csr_as_string, - .len = strlen(csr_as_string) }) }) - ); + GglObject csr_payload_obj + = GGL_OBJ_MAP(GGL_MAP({ GGL_STR("certificateSigningRequest"), + GGL_OBJ_BUF(csr_as_ggl_buffer) })); GglError ret_err_json = ggl_json_encode(csr_payload_obj, &csr_buf); if (ret_err_json != GGL_ERR_OK) { return GGL_ERR_PARSE; @@ -455,8 +496,7 @@ GglError make_request( { GGL_STR("payload"), GGL_OBJ_BUF(csr_buf) }, ); - // NOLINTNEXTLINE(concurrency-mt-unsafe) - ggl_sleep(5); + ggl_sleep(2); // Make Publish request to get the new certificate GglError ret_publish = ggl_notify(iotcored, GGL_STR("publish"), args); @@ -470,7 +510,10 @@ GglError make_request( return GGL_ERR_FAILURE; } - // NOLINTNEXTLINE(concurrency-mt-unsafe) - ggl_sleep(300); + while (!atomic_load(&complete_status)) { // Continuously check the flag + GGL_LOGI("Wating for thing to register"); + ggl_sleep(5); + } + return GGL_ERR_OK; } diff --git a/fleet-provisioning/src/provisioner.h b/fleet-provisioning/src/provisioner.h index a190b4853..493bfe737 100644 --- a/fleet-provisioning/src/provisioner.h +++ b/fleet-provisioning/src/provisioner.h @@ -6,10 +6,11 @@ #define PROVISIONER_H #include +#include #include GglError make_request( - char *csr_as_string, char *cert_file_path, pid_t iotcored_pid + GglBuffer csr_as_ggl_buffer, GglBuffer cert_file_path, pid_t iotcored_pid ); #endif From 6e9d032312e60f460a1d7f7d37089b59feee23a2 Mon Sep 17 00:00:00 2001 From: Anubhav Rawal Date: Fri, 13 Dec 2024 01:21:54 +0000 Subject: [PATCH 3/5] Update docs for fleet prov. Adds detail on how to run it when iotcored is simultaneously running in background --- docs/Fleet-provisioning.md | 60 ++++++++++++++++++++------------------ 1 file changed, 32 insertions(+), 28 deletions(-) diff --git a/docs/Fleet-provisioning.md b/docs/Fleet-provisioning.md index 1c513a33b..2d3c49f5c 100644 --- a/docs/Fleet-provisioning.md +++ b/docs/Fleet-provisioning.md @@ -9,6 +9,21 @@ can get valid certificates. you can follow the link [here](https://docs.aws.amazon.com/greengrass/v2/developerguide/fleet-provisioning-setup.html) to learn how to create appropriate policies and claim certificate. +``` +Note: +Currently, fleet provisioning can only be run manually. +Hence you will need to follow few important pre-steps + +1. Make sure you are logged in as root +2. Allow read access to all user for your certificates + chmod -R +rx /ggcredentials/ +3. Make sure you do not fill iotCredEndpoint/iotDataEndpoint under + `aws.greengrass.NucleusLite` you should only fill these fields + under `aws.greengrass.fleet_provisioning`'s config +4. If this is your not first run, remove the socket at + /run/greengrass/iotcoredfleet, if it exists +``` + Sample Fleet provisioning template: ```json @@ -73,52 +88,41 @@ config should roughly look as below. system: privateKeyPath: "" certificateFilePath: "" - rootCaPath: "/home/ubuntu/repo/fleetClaim/AmazonRootCA1.pem" - rootPath: "/home/ubuntu/aws-greengrass-lite/run_fleet/" - thingName: "" + rootCaPath: "/ggcredentials/fleetClaim/AmazonRootCA1.pem" #[Modify here] + rootPath: "/var/lib/greengrass/" #[Modify here] + thingName: "" #[Must leave blank] services: aws.greengrass.NucleusLite: componentType: "NUCLEUS" configuration: awsRegion: "us-east-1" - iotCredEndpoint: "" - iotDataEndpoint: "" + iotCredEndpoint: "" #[Must leave blank] + iotDataEndpoint: "" #[Must leave blank] iotRoleAlias: "GreengrassV2TokenExchangeRoleAlias" runWithDefault: - posixUser: "ubuntu:ubuntu" + posixUser: "user:group" #[Modify here] greengrassDataPlanePort: "8443" - tesCredUrl: "http://127.0.0.1:8080/" aws.greengrass.fleet_provisioning: configuration: - iotDataEndpoint: "dddddddddddddd-ats.iot.us-east-1.amazonaws.com" - iotCredEndpoint: "aaaaaaaaaaaaaa.credentials.iot.us-east-1.amazonaws.com" - claimKeyPath: "/home/ubuntu/fleetClaim/private.pem.key" - claimCertPath: "/home/ubuntu/fleetClaim/certificate.pem.crt" - templateName: "FleetTestNew" - templateParams: '{"SerialNumber": "14ALES55UFA"}' -``` - -With all this setup for IoT core now let's begin provisioning the device. First -we will start an instance of ggconfigd - -```sh -cd ./run -../build/bin/ggconfigd + iotDataEndpoint: "aaaaaaaaaaaaaa-ats.iot.us-east-1.amazonaws.com" #[Modify here] + iotCredEndpoint: "cccccccccccccc.credentials.iot.us-east-1.amazonaws.com" #[Modify here] + claimKeyPath: "/ggcredentials/fleetClaim/private.pem.key" #[Modify here] + claimCertPath: "/ggcredentials/fleetClaim/certificate.pem.crt" #[Modify here] + templateName: "FleetTestNew" #[Modify here] + templateParams: '{"SerialNumber": "AAA55555"}' #[Modify here] ``` -In another shell, run the config script and the fleet provisioning +In root user shell, run fleet provisioning ```sh cd ./run -../build/bin/ggl-config-init --config ./init_config.yml ../build/bin/fleet-provisioning ``` Now this will trigger the fleet provisioning script which will take a few -minutes to complete, the shell doesn't automatically exits so look for a Info -level log: `Process Complete, Your device is now provisioned`. then you can kill -the process or wait for auto terminate of `300 seconds`. +minutes to complete. -You can then kill the config daemon as well. +> Note: Device will reboot in case of successful run -Now you can return to `## Running the nucleus` step in [SETUP.md](SETUP.md) +If you are storing the standard output then look for log: +`Process Complete, Your device is now provisioned`. From 3578db09f2ad35143537eec9e1e53577886917af Mon Sep 17 00:00:00 2001 From: Anubhav Rawal Date: Fri, 13 Dec 2024 02:47:19 +0000 Subject: [PATCH 4/5] Remove stale documents --- docs/design/executable/recipe-runner.md | 26 ---------------- docs/spec/executable/recipe-runner.md | 41 ------------------------- docs/spec/executable/recipe2unit.md | 21 ------------- 3 files changed, 88 deletions(-) diff --git a/docs/design/executable/recipe-runner.md b/docs/design/executable/recipe-runner.md index b4f92bea0..8305ca4a7 100644 --- a/docs/design/executable/recipe-runner.md +++ b/docs/design/executable/recipe-runner.md @@ -1,27 +1 @@ # `recipe-runner` design - -See [`recipe-runnerd` spec](../../spec/executable/recipe-runner.md) for the -public interface for `recipe-runnerd`. - -`recipe-runner` is designed to run as a wrapper on the recipe's lifecycle script -section. It will take the script section as is and will replace the recipe -variables with appropriate values during runtime. The executable also -understands the gg-global config and how to interact with it. - -Once a recipe is translated to a unit file, the selected lifecycle will be -converted to a json file with its different lifecycle section. Each lifecycle -section will generate a unit file that is suffixed with it's phase. For an -example a recipe names `sampleComponent-0.1.0` will have unit files named -`sampleComponent-0.1.0_install` and `sampleComponent-0.1.0_run` to represent -install and run phase of lifecycle. As per the recipe2unit's design. - -Once a unit file is created `ggdeploymentd` will use `recipe2unit`'s functions -to execute the specific unit file that will run provided lifecycle phase as a -first time installation process. - -`recipe-runner` will use the provided selected lifecycle section and use -`execvp` to execute the argument provided lifecycle section as a bash script. It -will also forward any environment variables set during runtime. As a side effect -it will create a temporary bash script file with all the gg-recipe variables -replaced with appropriate actual values from the global config and then use will -provide the newly created script file to `execvp`. diff --git a/docs/spec/executable/recipe-runner.md b/docs/spec/executable/recipe-runner.md index 625a91766..d74da0605 100644 --- a/docs/spec/executable/recipe-runner.md +++ b/docs/spec/executable/recipe-runner.md @@ -1,42 +1 @@ # `recipe-runnerd` spec - -`recipe-runnerd` will act like a wrapper around the generic component to -dynamically update the GG-recipe variables - -- [recipe-runnerd-1] The executable will execute all the commands within a - selected phase as a bash script. -- [recipe-runnerd-2] The executable will also forward its environment variables - to the running script using global config. -- [recipe-runnerd-3] On execution failure it prints the error message to stderr -- [recipe-runnerd-4] The executable will take only 1 file as an argument and - phase - -## CLI parameters - -## phase - -- [recipe-runnerd-param-phase-1] The argument will dectate which phase needs to - be executed. -- [recipe-runnerd-param-phase-2] The phase argument can be provided by `--phase` - or `-p`. -- [recipe-runnerd-param-phase-3] The phase argument is required. - -### filePath - -- [recipe-runnerd-param-filePath-1] The argument will provide the path to - selected lifecycle json. -- [recipe-runnerd-param-filePath-2] The filePath argument can be provided by - `--filepath` or `-f`. -- [recipe-runnerd-param-filePath-3] The filePath argument is required. - -### timeout - -- [recipe-runnerd-param-timeout-1] The argument will allow user to edit the - timeout seting for the given script in seconds. -- [recipe-runnerd-param-timeout-2] The deafult value for the parmeter is 30 - seconds. -- [recipe-runnerd-param-timeout-3] The timeout argument can be provided by - `--timeout` or `-t`. -- [recipe-runnerd-param-timeout-4] The timeout argument is optional. - -## Environment Variables diff --git a/docs/spec/executable/recipe2unit.md b/docs/spec/executable/recipe2unit.md index 0118e30e3..37a609a34 100644 --- a/docs/spec/executable/recipe2unit.md +++ b/docs/spec/executable/recipe2unit.md @@ -1,22 +1 @@ # `recipe2unit` spec - -`recipe2unit` converts a GG recipe into a linux specific systemd unit file so -that it can be deployed within the edge device. - -- [recipe2unit-1] The executable intakes a GG recipe file and spits out a .unit - file with all the features represented within the recipe. -- [recipe2unit-2] In case of an error it will output `Error Parsing Recipe` - along with appropriate error message. - -## CLI parameters - -### recipe-path - -- [recipe2unit-param-path-1] This argument will allow user to specify the - recipe's location within the disk. -- [recipe2unit-param-path-2] The argument must be provided by `--recipe-path`. -- [recipe2unit-param-path-3] The argument is a required field - -## Environment Variables - -## Core Bus API From 271aaa775eec2e31ace2db9edf3996e9399d3ec9 Mon Sep 17 00:00:00 2001 From: Anubhav Rawal Date: Fri, 13 Dec 2024 03:17:25 +0000 Subject: [PATCH 5/5] Add in warning for user and group in docs --- docs/Fleet-provisioning.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/Fleet-provisioning.md b/docs/Fleet-provisioning.md index 2d3c49f5c..1dea500a4 100644 --- a/docs/Fleet-provisioning.md +++ b/docs/Fleet-provisioning.md @@ -22,6 +22,9 @@ Hence you will need to follow few important pre-steps under `aws.greengrass.fleet_provisioning`'s config 4. If this is your not first run, remove the socket at /run/greengrass/iotcoredfleet, if it exists +5. Fleet provisioning assumes the your GGL_SYSTEMD_SYSTEM_USER + and GGL_SYSTEMD_SYSTEM_GROUP to be ggcore:ggcore please change + appropriately if you change these values during compile time ``` Sample Fleet provisioning template: