diff --git a/CHANGELOG.md b/CHANGELOG.md index 326a089..a274095 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,16 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [3.3.11] - 2024-10-31 + +### Security + +- Bumped http-proxy-middleware to `2.0.7` to mitigate [CVE-2024-21536](https://github.com/advisories/GHSA-c7qv-q95q-8v27) + +### Changed + +- Moved spoke service linked role template as conditional nested stack under spoke stack + ## [3.3.10] - 2024-10-10 ### Security @@ -15,7 +25,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Security -- Bumped micromatch to `4.0.8` to mitigate [CVE-2024-4067](https://github.com/advisories/GHSA-952p-6rrq-rcjv) +- Bumped micromatch to `4.0.8` to mitigate [CVE-2024-4067](https://github.com/advisories/GHSA-952p-6rrq-rcjv) - Bumped webpack to `5.94.0` to mitigate [CVE-2024-43788](https://github.com/advisories/GHSA-4vvj-4cpr-p986) - Bumped express to `4.21.0` to mitigate CVEs in sub-dependencies - Bump path-to-regexp to `6.3.0` to address [CVE-2024-45296](https://github.com/advisories/GHSA-9wv6-86v2-598j) diff --git a/README.md b/README.md index 2e689a3..06bf1d8 100644 --- a/README.md +++ b/README.md @@ -68,7 +68,7 @@ Use the following steps if you want to customize the solution or extend the solu Clone the repository and make desired code changes. ``` -git clone aws-solutions/network-orchestration-for-aws-transit-gateway +git clone https://github.com/aws-solutions/network-orchestration-for-aws-transit-gateway.git ``` _Note: The following steps have been tested under the preceding pre-requisites._ diff --git a/deployment/build-s3-dist.sh b/deployment/build-s3-dist.sh index 0ef3ffc..d50897a 100755 --- a/deployment/build-s3-dist.sh +++ b/deployment/build-s3-dist.sh @@ -17,9 +17,9 @@ [ "$DEBUG" == 'true' ] && set -x set -e -if [ -z "$1" ] || [ -z "$2" ] || [ -z "$3" ]; then +if [ -z "$1" ] || [ -z "$2" ] || [ -z "$3" ] || [ -z "$4" ]; then echo "Please provide the base source bucket name, trademark approved solution name and version where the lambda code will eventually reside." - echo "For example: ./build-s3-dist.sh solutions trademarked-solution-name v1.0.0" + echo "For example: ./build-s3-dist.sh solutions trademarked-solution-name v1.0.0 solutions-reference" exit 1 fi @@ -159,4 +159,8 @@ else sed -i -e $replace $template_dist_dir/network-orchestration-organization-role.template sed -i -e $replace $template_dist_dir/network-orchestration-hub-service-linked-roles.template sed -i -e $replace $template_dist_dir/network-orchestration-spoke-service-linked-roles.template + + # Replace template bucket name + replace="s/%TEMPLATE_OUTPUT_BUCKET%/$4/g" + sed -i -e $replace $template_dist_dir/network-orchestration-spoke.template fi diff --git a/deployment/network-orchestration-spoke-service-linked-roles.template b/deployment/network-orchestration-spoke-service-linked-roles.template index eb8ebd9..847b25a 100644 --- a/deployment/network-orchestration-spoke-service-linked-roles.template +++ b/deployment/network-orchestration-spoke-service-linked-roles.template @@ -4,13 +4,24 @@ AWSTemplateFormatVersion: "2010-09-09" Description: (SO0058s-slr) - The AWS CloudFormation template (Spoke) for deployment of the %SOLUTION_NAME% Solution. Version %VERSION% +Parameters: + ServiceLinkedRoleExist: + Type: String + Description: Does the service-linked role for AWS Transit Gateway already exist? + Default: "False" + +Conditions: + CreateServiceLinkedRole: !Equals [ !Ref ServiceLinkedRoleExist, "False" ] + Resources: TransitGatewayServiceLinkedRole: Type: "AWS::IAM::ServiceLinkedRole" + Condition: CreateServiceLinkedRole Properties: AWSServiceName: "transitgateway.amazonaws.com" Description: Allows VPC Transit Gateway to access EC2 resources on your behalf. Outputs: TransitGatewayServiceLinkedRoleName: + Condition: CreateServiceLinkedRole Value: !Ref TransitGatewayServiceLinkedRole \ No newline at end of file diff --git a/deployment/network-orchestration-spoke.template b/deployment/network-orchestration-spoke.template index d7561a5..1427af7 100644 --- a/deployment/network-orchestration-spoke.template +++ b/deployment/network-orchestration-spoke.template @@ -28,6 +28,15 @@ Mappings: EventBridge: Bus: Name: "Network-Orchestrator-Event-Bus" + SourceCode: + General: + S3Bucket: "%DIST_BUCKET_NAME%" + KeyPrefix: "network-orchestration-for-aws-transit-gateway/%VERSION%" + Version: "%VERSION%" + TemplateBucket: "%TEMPLATE_OUTPUT_BUCKET%" + LambdaFunction: + Logging: + Level: "info" Conditions: # Adding an EventBus as a target within an account is not allowed. @@ -210,3 +219,77 @@ Resources: - arn:${AWS::Partition}:events:${AWS::Region}:${HubAccount}:event-bus/${EventBusName} - {EventBusName: !FindInMap [EventBridge, Bus, Name]} + # Lambda to check if the Transit Gateway service linked role already exist + ServiceLinkedRoleCheckLambdaFunction: + Type: AWS::Lambda::Function + Metadata: + cfn_nag: + rules_to_suppress: + - id: W92 + reason: "does not require concurrency reservation" + - id: W89 + reason: "not a valid use-case for vpc" + - id: W58 + reason: "log write permission added to CustomResourceLambdaFunctionRole" + Properties: + Environment: + Variables: + LOG_LEVEL: !FindInMap [LambdaFunction, Logging, Level] + PARTITION: !Sub ${AWS::Partition} + Code: + S3Bucket: !Join ["-", [!FindInMap ["SourceCode", "General", "S3Bucket"], !Ref "AWS::Region"]] + S3Key: !Join ["/", [!FindInMap ["SourceCode", "General", "KeyPrefix"],"custom_resource.zip"]] + Description: Network Orchestration for AWS Transit Gateway - custom resource handler + Handler: custom_resource.main.lambda_handler + MemorySize: 512 + Role: !Sub ${CustomResourceLambdaFunctionRole.Arn} + Runtime: python3.11 + Timeout: 900 + + CustomResourceLambdaFunctionRole: + Type: AWS::IAM::Role + Properties: + AssumeRolePolicyDocument: + Version: '2012-10-17' + Statement: + - Effect: Allow + Principal: + Service: + - lambda.amazonaws.com + Action: sts:AssumeRole + Path: / + Policies: + - PolicyName: STNO-CWLogs-Policy + PolicyDocument: + Version: "2012-10-17" + Statement: + - Effect: Allow + Action: + - logs:CreateLogGroup + - logs:CreateLogStream + - logs:PutLogEvents + Resource: !Sub arn:${AWS::Partition}:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/lambda/* + - PolicyName: STNO-IAM-Policy + PolicyDocument: + Version: '2012-10-17' + Statement: + - Effect: Allow + Action: + - iam:GetRole + Resource: !Sub arn:${AWS::Partition}:iam::${AWS::AccountId}:role/* + + CheckServiceLinkedRole: + Type: "Custom::CheckServiceLinkedRole" + Properties: + ServiceToken: !GetAtt ServiceLinkedRoleCheckLambdaFunction.Arn + + ServiceLinkedRoleNestedStack: + Type: 'AWS::CloudFormation::Stack' + Properties: + TemplateURL: !Sub + - "https://${Bucket}.s3.amazonaws.com/${Prefix}/network-orchestration-spoke-service-linked-roles.template" + - Bucket: !FindInMap [ "SourceCode", "General", "TemplateBucket" ] + Prefix: !FindInMap [ "SourceCode", "General", "KeyPrefix" ] + Parameters: + ServiceLinkedRoleExist: + !GetAtt CheckServiceLinkedRole.ServiceLinkedRoleExist diff --git a/source/cognito-trigger/package-lock.json b/source/cognito-trigger/package-lock.json index 1554ab3..b7da4d7 100644 --- a/source/cognito-trigger/package-lock.json +++ b/source/cognito-trigger/package-lock.json @@ -1,12 +1,12 @@ { "name": "cognito-trigger", - "version": "3.3.10", + "version": "3.3.11", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "cognito-trigger", - "version": "3.3.10", + "version": "3.3.11", "license": "Apache-2.0", "dependencies": { "@aws-sdk/client-cognito-identity-provider": "^3.102.0", diff --git a/source/cognito-trigger/package.json b/source/cognito-trigger/package.json index cc60f27..ca37057 100644 --- a/source/cognito-trigger/package.json +++ b/source/cognito-trigger/package.json @@ -1,6 +1,6 @@ { "name": "cognito-trigger", - "version": "3.3.10", + "version": "3.3.11", "description": "Triggered when a new user is confirmed in the user pool to allow for custom actions to be taken", "author": { "name": "Amazon Web Services", diff --git a/source/lambda/custom_resource/__tests__/test_custom_resource_helper.py b/source/lambda/custom_resource/__tests__/test_custom_resource_helper.py index 81b7095..38fbd35 100644 --- a/source/lambda/custom_resource/__tests__/test_custom_resource_helper.py +++ b/source/lambda/custom_resource/__tests__/test_custom_resource_helper.py @@ -7,6 +7,8 @@ from unittest.mock import Mock import pytest +import boto3 +from moto import mock_iam from aws_lambda_powertools import Logger from aws_lambda_powertools.utilities.typing import LambdaContext @@ -17,7 +19,8 @@ handle_metrics, start_state_machine, send, - get_resource_type_details + get_resource_type_details, + check_service_linked_role ) logger = Logger(os.getenv('LOG_LEVEL')) @@ -51,6 +54,9 @@ CREATE_METRICS = deepcopy(CFN_REQUEST_EVENT) CREATE_METRICS["ResourceType"] = "Custom::SendCFNParameters" +CHECK_SERVICE_LINKED_ROLE = deepcopy(CFN_REQUEST_EVENT) +CHECK_SERVICE_LINKED_ROLE["ResourceType"] = "Custom::CheckServiceLinkedRole" + context = Mock() context.get_remaining_time_in_millis = Mock() context.get_remaining_time_in_millis.return_value = 10000 @@ -264,6 +270,26 @@ def test__success(self, mocker): handle_metrics(CREATE_METRICS) +@pytest.mark.BDD +@mock_iam +class TestClassServiceLinkedRole: + """BDD class for testing checking if service linked role already exist""" + iam_client = boto3.client("iam") + service_linked_role_name = "AWSServiceRoleForVPCTransitGateway" + + def test__true(self): + """true""" + self.iam_client.create_role(RoleName=self.service_linked_role_name, AssumeRolePolicyDocument="some policy", Path="/my-path/") + resp = check_service_linked_role(CHECK_SERVICE_LINKED_ROLE) + self.iam_client.delete_role(RoleName=self.service_linked_role_name) + + assert resp["ServiceLinkedRoleExist"] == "True" + + def test__false(self): + """false""" + resp = check_service_linked_role(CHECK_SERVICE_LINKED_ROLE) + assert resp["ServiceLinkedRoleExist"] == "False" + @pytest.mark.TDD class TestClassTriggerSM: """TDD test class to handle state machine execution""" @@ -375,4 +401,4 @@ def test__success__fail(self, mocker): class AWSLambdaContext: def __init__(self): - self.invoked_function_arn = "abc:abc:abc:abc:abc:abc:abc" + self.invoked_function_arn = "abc:abc:abc:abc:abc:abc:abc" \ No newline at end of file diff --git a/source/lambda/custom_resource/lib/custom_resource_helper.py b/source/lambda/custom_resource/lib/custom_resource_helper.py index d1c7b83..2b30bfd 100644 --- a/source/lambda/custom_resource/lib/custom_resource_helper.py +++ b/source/lambda/custom_resource/lib/custom_resource_helper.py @@ -12,6 +12,7 @@ from uuid import uuid4 import boto3 +from botocore.exceptions import ClientError from aws_lambda_powertools import Logger from aws_lambda_powertools.utilities.typing import LambdaContext from aws_lambda_typing import events @@ -83,8 +84,6 @@ def get_tag_state(event): return "-deleted-" - - def timeout(event: events.CloudFormationCustomResourceEvent, context: LambdaContext): """_summary_ @@ -146,6 +145,9 @@ def cfn_handler(event: events.CloudFormationCustomResourceEvent, context: Lambda if event["ResourceType"] == "Custom::SendCFNParameters": handle_metrics(event) + if event["ResourceType"] == "Custom::CheckServiceLinkedRole": + response_data = check_service_linked_role(event) + logger.info("Completed successfully, sending response to cfn") except Exception as err: log_message["EXCEPTION"] = str(err) @@ -284,6 +286,36 @@ def handle_metrics(event: events.CloudFormationCustomResourceEvent): logger.warning(str(err)) +def check_service_linked_role(event: events.CloudFormationCustomResourceEvent): + """Handles checking if service linked role exist + + Args: + event (dict): event from CloudFormation on create, update or delete + + Returns: + dict: service linked role status + + { + ServiceLinkedRoleExist: boolean + } + """ + response = {} + iam_client = boto3.client("iam") + if event["RequestType"] == "Create" or event["RequestType"] == "Update": + try: + service_linked_role = iam_client.get_role(RoleName='AWSServiceRoleForVPCTransitGateway') + logger.info(service_linked_role) + response = {"ServiceLinkedRoleExist": "True"} + except ClientError as e: + logger.exception('%s', e) + if e.response['Error']['Code'] == 'NoSuchEntity': + response = {"ServiceLinkedRoleExist": "False"} + else: + raise e + logger.debug(response) + return response + + def send( event: events.CloudFormationCustomResourceEvent, context: LambdaContext, @@ -336,4 +368,4 @@ def send( except error.URLError as e: # Handle URL errors (e.g., connectivity issues, invalid URL) logger.error("send(..) failed sending PUT request: %s", str(e.reason)) - raise + raise \ No newline at end of file diff --git a/source/lambda/pyproject.toml b/source/lambda/pyproject.toml index e9d4da5..9eea55e 100644 --- a/source/lambda/pyproject.toml +++ b/source/lambda/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "network-orchestration-for-tgw" -version = "3.3.8-dev" +version = "3.3.11" description = "solution packages for network-orchestration-for-tgw" requires-python = ">=3.10" license = { text = "Apache Software License" } diff --git a/source/ui/package-lock.json b/source/ui/package-lock.json index f7b537d..39459ca 100644 --- a/source/ui/package-lock.json +++ b/source/ui/package-lock.json @@ -1,12 +1,12 @@ { "name": "network-orchestrator-for-aws-transit-gateway", - "version": "3.3.10", + "version": "3.3.11", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "network-orchestrator-for-aws-transit-gateway", - "version": "3.3.10", + "version": "3.3.11", "license": "Apache-2.0", "dependencies": { "@aws-amplify/auth": "^5.4.0", @@ -19047,9 +19047,9 @@ } }, "node_modules/http-proxy-middleware": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-2.0.6.tgz", - "integrity": "sha512-ya/UeJ6HVBYxrgYotAZo1KvPWlgB48kUJLDePFeneHsVujFaW5WNj2NgWCAE//B1Dl02BIfYlpNgBy8Kf8Rjmw==", + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-2.0.7.tgz", + "integrity": "sha512-fgVY8AV7qU7z/MmXJ/rxwbrtQH4jBQ9m7kp3llF0liB7glmFeVZFBepQb32T3y8n8k2+AEYuMPCpinYW+/CuRA==", "dependencies": { "@types/http-proxy": "^1.17.8", "http-proxy": "^1.18.1", diff --git a/source/ui/package.json b/source/ui/package.json index fe2fc9a..caea543 100644 --- a/source/ui/package.json +++ b/source/ui/package.json @@ -1,6 +1,6 @@ { "name": "network-orchestrator-for-aws-transit-gateway", - "version": "3.3.10", + "version": "3.3.11", "description": "Network Orchestration for AWS Transit Gateway(SO0058)", "license": "Apache-2.0", "author": {