Skip to content

Commit

Permalink
added use cert and key from secret manager (#39)
Browse files Browse the repository at this point in the history
  • Loading branch information
Owen-Choh authored Dec 27, 2024
1 parent 8113e96 commit ac88451
Show file tree
Hide file tree
Showing 5 changed files with 273 additions and 0 deletions.
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"
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
})
}

0 comments on commit ac88451

Please sign in to comment.