Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use secrets from secret manager #39

Merged
merged 1 commit into from
Dec 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 50 additions & 0 deletions SSG-API-Testing-Application-v2/lambda/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# Introduction
This directory is still **WIP**

It will contain POC of obtaining secrets from AWS Systems Manager Parameter Store.

This POC is meant to showcase how you can put your authentication secrets in AWS Systems Manager Parameter Store and retrieve them to use when calling our APIs

There are also samples in the `secrets-manager-samples` folder if you wish to use AWS Secrets Manager instead.

# Description of files

`lambda_function.py` contains the lambda function POC

- Permissions given to the lambda function:
- AWSLambdaBasicExecutionRole (AWS managed)
- KmsDecrypt
```
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "VisualEditor0",
"Effect": "Allow",
"Action": "kms:Decrypt",
"Resource": "*"
}
]
}
```
- SecretsPolicy
```
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "VisualEditor0",
"Effect": "Allow",
"Action": [
"secretsmanager:GetSecretValue"
],
"Resource": "*"
}
]
}
```

To import dependencies into AWS Lambda, you can add them via custom layers. The steps to do so are described [here](https://stackoverflow.com/questions/65975883/aws-lambda-python-error-runtime-importmoduleerror)

The minimum dependancy required to run the lambda function provided is:
- requests
86 changes: 86 additions & 0 deletions SSG-API-Testing-Application-v2/lambda/lambda_function.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
# Use this code snippet in your app.
# If you need more information about configurations
# or implementing the sample code, visit the AWS docs:
# https://aws.amazon.com/developer/language/python/

import certifi
import requests
import json
from tempfile import NamedTemporaryFile # noqa: E402

import boto3
from botocore.exceptions import ClientError

course_run_id = "35423"
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if this is hardcoded, does it mean we only run this course run id?

endpoint = f"https://uat-api.ssg-wsg.sg/courses/courseRuns/id/{course_run_id}"
params = {"includeExpiredCourses": "true"}
header = {
"accept": "application/json",
"Content-Type": "application/json"}


def lambda_handler(event, context):
secrets = json.loads(get_secret())
cert_pem = create_temp_file(secrets["cert"])
key_pem = create_temp_file(secrets["key"])
response = view_course_run(cert_pem, key_pem)
print(response)


def get_secret():

secret_name = "SampleApp/testing"
region_name = "ap-southeast-1"

# Create a Secrets Manager client
retrieve_secrets_session = assume_role()
session = boto3.session.Session()
client = retrieve_secrets_session.client(
service_name='secretsmanager',
region_name=region_name
)

try:
get_secret_value_response = client.get_secret_value(
SecretId=secret_name
)
except ClientError as e:
# For a list of exceptions thrown, see
# https://docs.aws.amazon.com/secretsmanager/latest/apireference/API_GetSecretValue.html
raise e

secret = get_secret_value_response['SecretString']

# Your code goes here.
return secret


def assume_role():
sts_client = boto3.client('sts')
response = sts_client.assume_role(
RoleArn="arn:aws:iam::767397936445:role/SampleAppRetrieveSecret",
RoleSessionName="retrieve-secret-session"
)

new_session = boto3.Session(aws_access_key_id=response['Credentials']['AccessKeyId'],
aws_secret_access_key=response['Credentials']['SecretAccessKey'],
aws_session_token=response['Credentials']['SessionToken'])

return new_session


def create_temp_file(inputStuff):
''' save input into temporary file and return file name '''
temp_file = NamedTemporaryFile(
delete=False, delete_on_close=False, suffix=".pem")
with open(temp_file.name, 'w') as f:
f.write(inputStuff)
return temp_file.name


def view_course_run(cert_pem, key_pem):
return requests.get(endpoint,
params=params,
headers=header,
verify=certifi.where(),
cert=(cert_pem, key_pem))
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
This directory contains files specific to using AWS Secrets Manager instead of parameter store.

`deploy-secrets.yml` is the workflow file for github actions to deploy the secret to AWS Secret Manager

The lambda function is given the role `SampleAppLambda` with the policies below:
- AWSLambdaBasicExecutionRole (AWS managed)
- StsAssumeRole (Customer inline)

```
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "VisualEditor0",
"Effect": "Allow",
"Action": "sts:AssumeRole",
"Resource": "arn:aws:iam::YourAccountNumber:role/SampleAppRetrieveSecret"
}
]
}
```

The role that the lambda function will assume when retrieving the secret from AWS Secrets Manager `SampleAppRetrieveSecret` with the policies below:
- AWSLambdaBasicExecutionRole (AWS managed)
- KmsDecrypt
```
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "VisualEditor0",
"Effect": "Allow",
"Action": "kms:Decrypt",
"Resource": "*"
}
]
}
```
- SecretsPolicy
```
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "VisualEditor0",
"Effect": "Allow",
"Action": [
"secretsmanager:GetSecretValue"
],
"Resource": "*"
}
]
}
```

> [!NOTE]
> You can choose to remove the `AWSLambdaBasicExecutionRole` policy from the `SampleAppRetrieveSecret` role if logging to CloudWatch is not required. This ensures the role adheres to the principle of least privilege by only granting the permissions necessary for its task.
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
name: "Deploy Secrets to AWS Secrets Manager"

on: workflow_dispatch

jobs:
deploy-secrets:
name: "Deploy to AWS Secrets Manager"
runs-on: ubuntu-latest
environment: dev

steps:
- name: Checkout Repository
uses: actions/checkout@v4

- name: Set up Terraform
uses: hashicorp/setup-terraform@v3

- name: Verify Terraform Script
id: create-backend-verify
working-directory: ./SSG-API-Testing-Application-v2/lambda/deploy/
run: |
terraform fmt
terraform fmt -check

- name: Initialise Backend
id: init-backend
working-directory: ./SSG-API-Testing-Application-v2/lambda/deploy/
run: terraform init

- name: Validate Terraform Script
id: create-backend-validate
working-directory: ./SSG-API-Testing-Application-v2/lambda/deploy/
run: terraform validate

# Generates an execution plan for Terraform
- name: Terraform Plan
id: plan
working-directory: ./SSG-API-Testing-Application-v2/lambda/deploy/
env:
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
AWS_REGION: ${{ env.AWS_REGION }}
# this is the secret value that will be placed in aws secret manager
TF_VAR_secret_value: ${{ secrets.AWS_SECRET }}
run: terraform plan

# On push to "main", build or change infrastructure according to Terraform configuration files
- name: Terraform Apply
working-directory: ./SSG-API-Testing-Application-v2/lambda/deploy/
id: apply
env:
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
AWS_REGION: ${{ env.AWS_REGION }}
# this is the secret value that will be placed in aws secret manager
TF_VAR_secret_value: ${{ secrets.AWS_SECRET }}
run: terraform apply -auto-approve
continue-on-error: true
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# Define input variable for the secret value
variable "secret_value" {
description = "The value of the secret to be stored"
type = string
sensitive = true
}

provider "aws" {
region = "ap-southeast-1"
}

resource "aws_secretsmanager_secret" "example_secret" {
name = "SampleApp/test" # Name of the secret
}

resource "aws_secretsmanager_secret_version" "example_secret_version" {
secret_id = aws_secretsmanager_secret.example_secret.id
secret_string = jsonencode({
test_secret = var.secret_value
})
}

Loading