diff --git a/Makefile b/Makefile index 7d7c2e6..f934ec7 100644 --- a/Makefile +++ b/Makefile @@ -22,10 +22,15 @@ format: ## Format the code. poetry run black src poetry run ruff check src --fix +.PHONY: md-fix +md-fix: ## Run markdown linting with Markdownlint and fix issues. + sh ./shell_scripts/md_fix.sh + .PHONY: lint -lint: ## Run all linters (black/ruff/pylint/mypy). +lint: ## Run all linters (black/ruff/pylint/mypy/markdownlint). poetry run black --check src poetry run ruff check src + make md-fix make mypy .PHONY: test diff --git a/README.md b/README.md index cc39aba..e87969b 100644 --- a/README.md +++ b/README.md @@ -128,18 +128,26 @@ Before the doing the following, make sure your Daemon is running. If using Colim github-repository-archive-script latest b4a1e32ce51b 12 minutes ago 840MB ``` -3. Run the image. +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. + +4. Run the image. ```bash docker run --platform linux/amd64 -p 9000:8080 \ - -e AWS_ACCESS_KEY_ID= \ - -e AWS_SECRET_ACCESS_KEY= \ - -e AWS_DEFAULT_REGION= \ + -v ~/.aws:/root/.aws \ + -e AWS_PROFILE=github-repository-archive-script \ + -e AWS_DEFAULT_REGION=eu-west-2 \ -e AWS_SECRET_NAME= \ -e GITHUB_ORG= \ -e GITHUB_APP_CLIENT_ID= \ - -e S3_BUCKET_NAME=\ - -e AWS_LAMBDA_FUNCTION_TIMEOUT=300 + -e S3_BUCKET_NAME= \ + -e AWS_LAMBDA_FUNCTION_TIMEOUT=300 \ github-repository-archive-script ``` @@ -156,7 +164,7 @@ Before the doing the following, make sure your Daemon is running. If using Colim Once the container is running, a local endpoint is created at `localhost:9000/2015-03-31/functions/function/invocations`. -4. Check the container is running (Optional). +5. Check the container is running (Optional). ```bash docker ps @@ -169,7 +177,7 @@ Before the doing the following, make sure your Daemon is running. If using Colim ca890d30e24d github-repository-archive-script "/lambda-entrypoint.…" 5 seconds ago Up 4 seconds 0.0.0.0:9000->8080/tcp, :::9000->8080/tcp recursing_bartik ``` -5. Post to the endpoint (`localhost:9000/2015-03-31/functions/function/invocations`). +6. Post to the endpoint (`localhost:9000/2015-03-31/functions/function/invocations`). ```bash curl "http://localhost:9000/2015-03-31/functions/function/invocations" -d '{}' @@ -177,7 +185,7 @@ Before the doing the following, make sure your Daemon is running. If using Colim This will run the Lambda function and, once complete, will return a success message. -6. After testing stop the container. +7. After testing stop the container. ```bash docker stop @@ -198,11 +206,21 @@ 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 + aws sso login + + export AWS_PROFILE=github-repository-archive-script + ``` + + 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_ACCESS_KEY_ID= - export AWS_SECRET_ACCESS_KEY= export AWS_DEFAULT_REGION=eu-west-2 export AWS_SECRET_NAME= export S3_BUCKET_NAME= @@ -212,7 +230,7 @@ To run the Lambda function outside of a container, we need to execute the `handl An explanation of each variable is available within the [containerised instructions](#containerised-recommended). -3. Run the script. +4. Run the script. ```bash python3 src/main.py diff --git a/poetry.lock b/poetry.lock index dc7a0a1..65662ae 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 2.2.1 and should not be changed by hand. +# This file is automatically @generated by Poetry 2.1.4 and should not be changed by hand. [[package]] name = "astroid" @@ -397,7 +397,7 @@ files = [ {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, ] -markers = {dev = "platform_system == \"Windows\" or sys_platform == \"win32\""} +markers = {dev = "sys_platform == \"win32\" or platform_system == \"Windows\""} [[package]] name = "coverage" @@ -707,18 +707,18 @@ pypi = ["pip (>=24.0)", "platformdirs (>=4.2)", "wheel (>=0.42)"] [[package]] name = "idna" -version = "3.11" +version = "3.15" description = "Internationalized Domain Names in Applications (IDNA)" optional = false python-versions = ">=3.8" groups = ["main", "docs"] files = [ - {file = "idna-3.11-py3-none-any.whl", hash = "sha256:771a87f49d9defaf64091e6e6fe9c18d4833f140bd19464795bc32d966ca37ea"}, - {file = "idna-3.11.tar.gz", hash = "sha256:795dafcc9c04ed0c1fb032c2aa73654d8e8c5023a7df64a53f39190ada629902"}, + {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/shell_scripts/md_fix.sh b/shell_scripts/md_fix.sh new file mode 100644 index 0000000..e8359b2 --- /dev/null +++ b/shell_scripts/md_fix.sh @@ -0,0 +1,3 @@ +#!/bin/bash + +docker run -v "$PWD:/workdir" ghcr.io/igorshubovych/markdownlint-cli:latest "**/*.md" --fix diff --git a/terraform/service/main.tf b/terraform/service/main.tf index e7205dc..f43df53 100644 --- a/terraform/service/main.tf +++ b/terraform/service/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 -} diff --git a/terraform/service/secrets.tf b/terraform/service/secrets.tf deleted file mode 100644 index 32fd11d..0000000 --- a/terraform/service/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 archive script 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 archive script 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 -}