diff --git a/README.md b/README.md index 83343f8..f6f85f8 100644 --- a/README.md +++ b/README.md @@ -95,23 +95,39 @@ To run the Lambda function outside of a container, we need to execute the `handl **Please Note:** If uncommenting the above in `main.py`, make sure you re-comment the code _before_ pushing back to GitHub. -2. Export the required environment variables: +2. Sign in with AWS SSO, and export the correct profile for this service: - ```bash - export AWS_ACCESS_KEY_ID= - export AWS_SECRET_ACCESS_KEY= - export AWS_DEFAULT_REGION=eu-west-2 - export AWS_SECRET_NAME= - export GITHUB_ORG=ONSDigital - export GITHUB_APP_CLIENT_ID= - export AWS_ACCOUNT_NAME= - ``` + ```bash + aws sso login -3. Run the script. + export AWS_PROFILE=github-copilot-usage-lambda + ``` - ```bash - python3 src/main.py - ``` + This allows you to assume the AWS IAM role for the service, enabling the most secure development experience. This also means you will have limited permissions until you exit out of the profile. + + **Note:** See the Developer Onboarding Guide on the "Using AWS SSO for Local Development" page on Confluence to set up service profile selection on your local machine. + +3. Export the required environment variables: + + ```bash + export AWS_DEFAULT_REGION=eu-west-2 + export AWS_SECRET_NAME= + export AWS_ACCOUNT_NAME= + export GITHUB_ORG=ONSDigital + export GITHUB_APP_CLIENT_ID= + ``` + +4. Run the script. + + ```bash + python3 src/main.py + ``` + +5. To exit the profile: + + ```bash + unset AWS_PROFILE + ``` ### Running in a container @@ -133,14 +149,20 @@ To run the Lambda function outside of a container, we need to execute the `handl | --------------------------- | ------ | ------------ | -------------- | ----- | | copilot-usage-lambda-script | latest | 0bbe73d9256f | 11 seconds ago | 224MB | -3. Run the image locally mapping local host port (9000) to container port (8080) and passing in AWS credentials to download a .pem file from the AWS Secrets Manager to the running container. These credentials will also be used to upload and download `historic_usage_data.json` to and from S3. +3. Sign in with AWS SSO: + + ```bash + aws sso login + ``` + + **Note:** See the Developer Onboarding Guide on the "Using AWS SSO for Local Development" page on Confluence to set up service profile selection on your local machine. This is essential as the `~/.aws` directory is mounted to the container, so it can use the SSO session for AWS authentication. - The credentials used in the below command are for a user in AWS that has permissions to retrieve secrets from AWS Secrets Manager and upload and download files from AWS S3. +4. Run the image locally mapping local host port (9000) to container port (8080). ```bash docker run --platform linux/amd64 -p 9000:8080 \ - -e AWS_ACCESS_KEY_ID= \ - -e AWS_SECRET_ACCESS_KEY= \ + -v ~/.aws:/root/.aws \ + -e AWS_PROFILE=github-copilot-usage-lambda \ -e AWS_DEFAULT_REGION=eu-west-2 \ -e AWS_SECRET_NAME= \ -e GITHUB_ORG=ONSDigital \ @@ -151,7 +173,7 @@ To run the Lambda function outside of a container, we need to execute the `handl Once the container is running, a local endpoint is created at `localhost:9000/2015-03-31/functions/function/invocations`. -4. Post to the endpoint to trigger the function +5. Post to the endpoint to trigger the function ```bash curl "http://localhost:9000/2015-03-31/functions/function/invocations" -d '{}' @@ -159,7 +181,7 @@ To run the Lambda function outside of a container, we need to execute the `handl This should return a message if successful. -5. Once testing is finished, stop the running container +6. Once testing is finished, stop the running container To check the container is running diff --git a/poetry.lock b/poetry.lock index 2b72617..f5935d0 100644 --- a/poetry.lock +++ b/poetry.lock @@ -632,18 +632,18 @@ colorama = ">=0.4" [[package]] name = "idna" -version = "3.10" +version = "3.15" description = "Internationalized Domain Names in Applications (IDNA)" optional = false -python-versions = ">=3.6" +python-versions = ">=3.8" groups = ["main", "docs"] files = [ - {file = "idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3"}, - {file = "idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9"}, + {file = "idna-3.15-py3-none-any.whl", hash = "sha256:048adeaf8c2d788c40fee287673ccaa74c24ffd8dcf09ffa555a2fbb59f10ac8"}, + {file = "idna-3.15.tar.gz", hash = "sha256:ca962446ea538f7092a95e057da437618e886f4d349216d2b1e294abfdb65fdc"}, ] [package.extras] -all = ["flake8 (>=7.1.1)", "mypy (>=1.11.2)", "pytest (>=8.3.2)", "ruff (>=0.6.2)"] +all = ["mypy (>=1.11.2)", "pytest (>=8.3.2)", "ruff (>=0.6.2)"] [[package]] name = "iniconfig" diff --git a/terraform/main.tf b/terraform/main.tf index ca6d394..20275c5 100644 --- a/terraform/main.tf +++ b/terraform/main.tf @@ -73,6 +73,20 @@ resource "aws_iam_role" "lambda_function_role" { Principal = { Service = "lambda.amazonaws.com" } + }, + { + Action = "sts:AssumeRole" + Effect = "Allow" + Principal = { + AWS = "arn:aws:iam::${var.aws_account_id}:root" + } + Condition = { + ArnLike = { + "aws:PrincipalArn" = [ + "arn:aws:iam::${var.aws_account_id}:role/aws-reserved/sso.amazonaws.com/eu-west-2/AWSReservedSSO_Standard_Administrator_Access_*" + ] + } + } } ] }) @@ -138,59 +152,3 @@ resource "aws_cloudwatch_log_group" "loggroup" { name = "/aws/lambda/${aws_lambda_function.lambda_function.function_name}" retention_in_days = var.log_retention_days } - -# IAM User Group -resource "aws_iam_group" "group" { - name = "${var.env_name}-${var.lambda_name}-user-group" - path = "/" -} - -resource "aws_iam_group_policy_attachment" "group_vpc_permissions_attachment" { - group = aws_iam_group.group.name - policy_arn = aws_iam_policy.vpc_permissions.arn -} - -resource "aws_iam_group_policy_attachment" "group_lambda_logging_attachment" { - group = aws_iam_group.group.name - policy_arn = aws_iam_policy.lambda_logging.arn -} - -resource "aws_iam_group_policy_attachment" "group_lambda_s3_policy_attachment" { - group = aws_iam_group.group.name - policy_arn = aws_iam_policy.lambda_s3_policy.arn -} - -resource "aws_iam_group_policy_attachment" "group_lambda_secret_manager_policy_attachment" { - group = aws_iam_group.group.name - policy_arn = aws_iam_policy.lambda_secret_manager_policy.arn -} - -resource "aws_iam_group_policy_attachment" "group_lambda_eventbridge_policy_attachment" { - group = aws_iam_group.group.name - policy_arn = aws_iam_policy.lambda_eventbridge_policy.arn -} - -# IAM User -resource "aws_iam_user" "user" { - name = "${var.env_name}-${var.lambda_name}" - path = "/" -} - -# Assign IAM User to group -resource "aws_iam_user_group_membership" "user_group_attach" { - user = aws_iam_user.user.name - - groups = [ - aws_iam_group.group.name - ] -} - -# IAM Key Rotation Module -module "iam_key_rotation" { - source = "git::https://github.com/ONS-Innovation/keh-aws-iam-key-rotation.git?ref=v0.1.1" - - iam_username = aws_iam_user.user.name - access_key_secret_arn = aws_secretsmanager_secret.access_key.arn - secret_key_secret_arn = aws_secretsmanager_secret.secret_key.arn - rotation_in_days = 45 -} \ No newline at end of file diff --git a/terraform/secrets.tf b/terraform/secrets.tf deleted file mode 100644 index c0dd165..0000000 --- a/terraform/secrets.tf +++ /dev/null @@ -1,14 +0,0 @@ -# Secrets for rotated IAM user access keys -resource "aws_secretsmanager_secret" "access_key" { - name = "${var.env_name}-${var.lambda_name}-access-key" - description = "Access Key ID for copilot usage lambda IAM user" - recovery_window_in_days = 0 // Secret will be deleted immediately - force_overwrite_replica_secret = true // Allow overwriting the secret in case of changes -} - -resource "aws_secretsmanager_secret" "secret_key" { - name = "${var.env_name}-${var.lambda_name}-secret-key" - description = "Secret Access Key for copilot usage lambda IAM user" - recovery_window_in_days = 0 // Secret will be deleted immediately - force_overwrite_replica_secret = true // Allow overwriting the secret in case of changes -}