From 798e26350a6b1b9b7f0e161bd3a8bc9fa9002b34 Mon Sep 17 00:00:00 2001 From: sudo-rgorai Date: Thu, 22 Jun 2023 18:15:14 +0530 Subject: [PATCH 01/36] Add files for private CA --- private-ca/README.md | 41 ++ private-ca/auth-header.py | 29 + private-ca/create-lambda.sh | 23 + private-ca/create-layer.sh | 21 + private-ca/create-role.sh | 22 + private-ca/create-secret.sh | 29 + private-ca/deploy-resources.sh | 88 +++ private-ca/event.json | 16 + private-ca/get-certificate.sh | 66 ++ private-ca/lambda/generate-client-ssh-cert.js | 30 + .../lambda/generate-client-x509-cert.js | 65 ++ private-ca/lambda/generate-host-ssh-cert.js | 30 + private-ca/lambda/generate-root-x509-cert.js | 36 ++ private-ca/lambda/get-caller-identity.js | 52 ++ private-ca/lambda/index.js | 56 ++ private-ca/lambda/package-lock.json | 596 ++++++++++++++++++ private-ca/lambda/package.json | 17 + private-ca/lambda/secret-manager-utils.js | 20 + private-ca/response.json | 1 + 19 files changed, 1238 insertions(+) create mode 100644 private-ca/README.md create mode 100644 private-ca/auth-header.py create mode 100755 private-ca/create-lambda.sh create mode 100755 private-ca/create-layer.sh create mode 100755 private-ca/create-role.sh create mode 100755 private-ca/create-secret.sh create mode 100755 private-ca/deploy-resources.sh create mode 100644 private-ca/event.json create mode 100755 private-ca/get-certificate.sh create mode 100644 private-ca/lambda/generate-client-ssh-cert.js create mode 100644 private-ca/lambda/generate-client-x509-cert.js create mode 100644 private-ca/lambda/generate-host-ssh-cert.js create mode 100644 private-ca/lambda/generate-root-x509-cert.js create mode 100644 private-ca/lambda/get-caller-identity.js create mode 100644 private-ca/lambda/index.js create mode 100644 private-ca/lambda/package-lock.json create mode 100644 private-ca/lambda/package.json create mode 100644 private-ca/lambda/secret-manager-utils.js create mode 100644 private-ca/response.json diff --git a/private-ca/README.md b/private-ca/README.md new file mode 100644 index 0000000..bb7c94a --- /dev/null +++ b/private-ca/README.md @@ -0,0 +1,41 @@ +# Private Certificate Authority (CA) for SSH and SSL Certificates + +This project provides a private Certificate Authority (CA) implementation for generating both SSH and SSL certificates. It allows you to issue certificates for SSH hosts and users, as well as root and client SSL certificates for secure communication. + +## Installation + + +Deploy the resources by running: + + ```bash + ./deploy-resources.sh + ``` + +This creates the following resources on AWS: +- Secret to store the keys for signing certificates +- A role for the lambda function +- A policy to be attached to the role giving read/update access to created secret +- An openSSH layer to facilitate SSH operations +- The lambda function to act as a privateCA + + +## Usage + +Certificates can be generated by running: + + ```bash + ./get-certificate.sh + ``` + +You can modify the variables in the script to change the payload: + +- `CA_ACTION` + - getHostSSHCert + - getClientSSHCert + - generateRootX509Cert + - generateClientX509Cert +- `SSL_ATTRS_VALIDITY` - The validity of the generated certificate in days +- `SSL_CLIENT_PUBKEY_PEM` - Public Key of the client to be signed for generating client SSL certificate +- `SSH_ATTRS_VALIDITY` - The validity of the generated certificate in weeks +- `SSH_HOST_RSA_PUBKEY` - Public Key of the host to be signed for generating host SSH certificate +- `SSH_CLIENT_RSA_PUBKEY` - Public Key of the client to be signed for generating client SSH certificate \ No newline at end of file diff --git a/private-ca/auth-header.py b/private-ca/auth-header.py new file mode 100644 index 0000000..0581082 --- /dev/null +++ b/private-ca/auth-header.py @@ -0,0 +1,29 @@ +import boto3 +from botocore.auth import SigV4Auth +from botocore.awsrequest import AWSRequest +from botocore.credentials import Credentials +import sys + +if __name__ == "__main__": + access_key_id = sys.argv[1] + secret_access_key = sys.argv[2] + session_token = sys.argv[3] + + region = "ap-south-1" + sts_host = "sts.ap-south-1.amazonaws.com" + request_parameters = 'Action=GetCallerIdentity&Version=2011-06-15' + request_headers = { + 'Host': sts_host, + 'X-Amz-Date': '20230608T112738Z' + } + request = AWSRequest(method="POST", url="/", data=request_parameters, + headers=request_headers) + boto_creds = Credentials(access_key_id, secret_access_key,token=session_token) + auth = SigV4Auth(boto_creds, "sts", region) + auth.add_auth(request) + + authorization = request.headers["Authorization"] + date = request.headers["X-Amz-Date"] + + response = f'{{"Authorization": "{authorization}", "Date": "{date}"}}' + print(response) diff --git a/private-ca/create-lambda.sh b/private-ca/create-lambda.sh new file mode 100755 index 0000000..d907cec --- /dev/null +++ b/private-ca/create-lambda.sh @@ -0,0 +1,23 @@ +#!/bin/bash + +ROLE_ARN=$1 +LAYER_ARN=$2 +FUNCTION_NAME=${3:-"privateCA"} + +# Create lambda function +cd lambda +npm i +zip -r ./lambda.zip . +mv lambda.zip ../ +cd .. + +aws lambda create-function \ + --function-name $FUNCTION_NAME \ + --runtime nodejs18.x \ + --handler index.handler \ + --zip-file fileb://lambda.zip \ + --layers $LAYER_ARN \ + --role $ROLE_ARN + +# Clean up +rm *.zip \ No newline at end of file diff --git a/private-ca/create-layer.sh b/private-ca/create-layer.sh new file mode 100755 index 0000000..3f276f1 --- /dev/null +++ b/private-ca/create-layer.sh @@ -0,0 +1,21 @@ +#!/bin/bash + +LAYER_NAME=${1:-"openssh"} + +echo $LAYER_NAME + +# Create OpenSSH layer +docker run --rm -v $(pwd)/openssh-layer:/lambda/opt lambci/yumda:2 yum install -y openssh +cd openssh-layer +echo "**************************************************************************************************************************************************************************************************************************************************************************************************************" +echo $LAYER_NAME +zip -yr ./openssh-layer.zip . > /dev/null +LAYER_ARN=$(aws lambda publish-layer-version \ + --layer-name $LAYER_NAME \ + --zip-file fileb://openssh-layer.zip \ + --query 'LayerVersionArn' \ + --output text) +echo $LAYER_ARN +cd .. + +rm -r openssh-layer/ *.zip \ No newline at end of file diff --git a/private-ca/create-role.sh b/private-ca/create-role.sh new file mode 100755 index 0000000..7b82be8 --- /dev/null +++ b/private-ca/create-role.sh @@ -0,0 +1,22 @@ +#!/bin/bash + +SECRET_ARN=$1 +ROLE_NAME=${2:-"privateCALambdaRole"} +POLICY_NAME=${3:-"AccessPrivateCASecretsPolicy"} + +# Create role for lambda +echo "{\"Version\": \"2012-10-17\",\"Statement\": [{\"Sid\": \"AllowLambdaAssumeRole\",\"Effect\": \"Allow\",\"Principal\": {\"Service\": \"lambda.amazonaws.com\"},\"Action\": \"sts:AssumeRole\"}]}" | jq . > Trust-Policy.json + +ROLE_ARN=$(aws iam create-role \ + --role-name $ROLE_NAME\ + --assume-role-policy-document file://Trust-Policy.json | jq ".Role.Arn" | tr -d '"') + +# Create Policy for Lambda Role to Read and Update Secrets +echo "{\"Version\": \"2012-10-17\",\"Statement\": [{\"Sid\": \"VisualEditor0\",\"Effect\": \"Allow\",\"Action\": [\"secretsmanager:GetSecretValue\",\"secretsmanager:UpdateSecret\"],\"Resource\": \"${SECRET_ARN}\"}]}" | jq . > Policy.json + +POLICY_ARN=$(aws iam create-policy --policy-name $POLICY_NAME --policy-document file://Policy.json | jq ".Policy.Arn" | tr -d '"') + +# Attach policy to role +aws iam attach-role-policy --role-name $ROLE_NAME --policy-arn $POLICY_ARN + +rm *.json \ No newline at end of file diff --git a/private-ca/create-secret.sh b/private-ca/create-secret.sh new file mode 100755 index 0000000..f520e70 --- /dev/null +++ b/private-ca/create-secret.sh @@ -0,0 +1,29 @@ +#!/bin/bash + +SECRET_NAME=${1:-"privateCASecret"} + +# Generate Keys +ssh-keygen -t rsa -b 4096 -f host_ca -C host_ca -N "" +ssh-keygen -t rsa -b 4096 -f user_ca -C user_ca -N "" + +openssl genrsa -out key.pem 2048 +openssl rsa -in key.pem -outform PEM -pubout -out public.pem + +HOST_CA_PRIVATE_KEY=$(cat host_ca | base64 -w 0) +HOST_CA_PUBLIC_KEY=$(cat host_ca.pub | base64 -w 0) +USER_CA_PRIVATE_KEY=$(cat user_ca | base64 -w 0) +USER_CA_PUBLIC_KEY=$(cat user_ca.pub | base64 -w 0) +ROOT_SSL_PRIVATE_KEY=$(cat key.pem | base64 -w 0) +ROOT_SSL_PUBLIC_KEY=$(cat public.pem | base64 -w 0) + +echo "{\"host_ca\": \"${HOST_CA_PRIVATE_KEY}\", \"host_ca.pub\": \"${HOST_CA_PUBLIC_KEY}\", \"user_ca\": \"${USER_CA_PRIVATE_KEY}\",\"user_ca.pub\": \"${USER_CA_PUBLIC_KEY}\",\"root_ssl_private_key\": \"${ROOT_SSL_PRIVATE_KEY}\",\"root_ssl_public_key\": \"${ROOT_SSL_PUBLIC_KEY}\"}" | jq . > secret.json + +# Create Secret +SECRET_ARN=$(aws secretsmanager create-secret \ + --name $SECRET_NAME \ + --secret-string file://secret.json | jq ".ARN" | tr -d '"') + +echo SECRET_ARN + +# Clean up +rm host_ca user_ca *.pub *.pem *.json \ No newline at end of file diff --git a/private-ca/deploy-resources.sh b/private-ca/deploy-resources.sh new file mode 100755 index 0000000..aefcbd5 --- /dev/null +++ b/private-ca/deploy-resources.sh @@ -0,0 +1,88 @@ +#!/bin/bash + +SECRET_NAME=${1:-"privateCASecret"} +ROLE_NAME=${2:-"privateCALambdaRole"} +POLICY_NAME=${3:-"AccessPrivateCASecretsPolicy"} +LAYER_NAME=${4:-"openssh"} +FUNCTION_NAME=${5:-"privateCA"} + +################## Secret ################## + +# Generate Keys +ssh-keygen -t rsa -b 4096 -f host_ca -C host_ca -N "" +ssh-keygen -t rsa -b 4096 -f user_ca -C user_ca -N "" + +openssl genrsa -out key.pem 2048 +openssl rsa -in key.pem -outform PEM -pubout -out public.pem + +HOST_CA_PRIVATE_KEY=$(cat host_ca | base64 -w 0) +HOST_CA_PUBLIC_KEY=$(cat host_ca.pub | base64 -w 0) +USER_CA_PRIVATE_KEY=$(cat user_ca | base64 -w 0) +USER_CA_PUBLIC_KEY=$(cat user_ca.pub | base64 -w 0) +ROOT_SSL_PRIVATE_KEY=$(cat key.pem | base64 -w 0) +ROOT_SSL_PUBLIC_KEY=$(cat public.pem | base64 -w 0) + +echo "{\"host_ca\": \"${HOST_CA_PRIVATE_KEY}\", \"host_ca.pub\": \"${HOST_CA_PUBLIC_KEY}\", \"user_ca\": \"${USER_CA_PRIVATE_KEY}\",\"user_ca.pub\": \"${USER_CA_PUBLIC_KEY}\",\"root_ssl_private_key\": \"${ROOT_SSL_PRIVATE_KEY}\",\"root_ssl_public_key\": \"${ROOT_SSL_PUBLIC_KEY}\"}" | jq . > secret.json + +# Create Secret +SECRET_ARN=$(aws secretsmanager create-secret \ + --name $SECRET_NAME \ + --secret-string file://secret.json | jq ".ARN" | tr -d '"') + + +############################################ + +################### Role ################### +# Create role for lambda +echo "{\"Version\": \"2012-10-17\",\"Statement\": [{\"Sid\": \"AllowLambdaAssumeRole\",\"Effect\": \"Allow\",\"Principal\": {\"Service\": \"lambda.amazonaws.com\"},\"Action\": \"sts:AssumeRole\"}]}" | jq . > Trust-Policy.json + +ROLE_ARN=$(aws iam create-role \ + --role-name $ROLE_NAME\ + --assume-role-policy-document file://Trust-Policy.json | jq ".Role.Arn" | tr -d '"') + +# Create Policy for Lambda Role to Read and Update Secrets +echo "{\"Version\": \"2012-10-17\",\"Statement\": [{\"Sid\": \"VisualEditor0\",\"Effect\": \"Allow\",\"Action\": [\"secretsmanager:GetSecretValue\",\"secretsmanager:UpdateSecret\"],\"Resource\": \"${SECRET_ARN}\"}]}" | jq . > Policy.json + +POLICY_ARN=$(aws iam create-policy --policy-name $POLICY_NAME --policy-document file://Policy.json | jq ".Policy.Arn" | tr -d '"') + +# Attach policy to role +aws iam attach-role-policy --role-name $ROLE_NAME --policy-arn $POLICY_ARN + +############################################ + +################### Layer ################## + +# Create OpenSSH layer +sudo docker run --rm -v $(pwd)/openssh-layer:/lambda/opt lambci/yumda:2 yum install -y openssh +cd openssh-layer +sudo zip -yr ./openssh-layer.zip . > /dev/null +LAYER_ARN=$(aws lambda publish-layer-version \ + --layer-name $LAYER_NAME \ + --zip-file fileb://openssh-layer.zip \ + --query 'LayerVersionArn' \ + --output text) +cd .. + +############################################ + +################## Lambda ################## + +# Create lambda function +cd lambda +npm i +zip -r ./lambda.zip . +mv lambda.zip ../ +cd .. + +aws lambda create-function \ + --function-name $FUNCTION_NAME \ + --runtime nodejs18.x \ + --handler index.handler \ + --zip-file fileb://lambda.zip \ + --layers $LAYER_ARN \ + --role $ROLE_ARN + +########################################### + +# Clean up +sudo rm -r openssh-layer/ host_ca user_ca *.pub *.pem *.json *.zip \ No newline at end of file diff --git a/private-ca/event.json b/private-ca/event.json new file mode 100644 index 0000000..c7fc0fd --- /dev/null +++ b/private-ca/event.json @@ -0,0 +1,16 @@ +{"auth": { + "amzDate": "20230622T124133Z", + "authorizationHeader": "AWS4-HMAC-SHA256 Credential=ASIAVKZF47HIBE5VYGHP/20230622/ap-south-1/sts/aws4_request, SignedHeaders=host;x-amz-date;x-amz-security-token, Signature=cd5dc4732c5967c067691ea1bac2a4f988189f2bb71c3d53aabff3c9d1478864", + "sessionToken": "IQoJb3JpZ2luX2VjEH0aCmFwLXNvdXRoLTEiSDBGAiEAwKCn4CisD/5vCwGqVlBa6e7GJqyRwniflw6lv+nHcT8CIQCxrgwTTYY+Dl/bxNjpYjiDxOOvyeodqyFPYDz5ltmcASr0AQjW//////////8BEAEaDDM2Njc2MjMyNjQ4MCIMtsxFJMeOiT1nikSGKsgBsNXYCmBVHMKmGxJLBUOgxlr+faS+xEEBre6Yo4GlFsHCwMMCBdowAi1PF+2qV3MB/U+Mj6FHeka/iSa+6mNcTYFA/bSx30KxVbO1QDYfQBRgShzRfMTGA3aNUJSIMj8dSgTFR2/HAqAUor/ROAicO7XO5URLCdlgt6Y4qrw/sfpq8wZOFVswoADhW0ABIHINJlZehLb2LV6gexgnl4jiKFgBj2k/0w5w5Gon0YYoPhY4b105Oh+JzkUUT1UHWVVdp4s8d+azrN4w24HRpAY6lwEXFuSzxh+otNnXHbR918jboXfwEUi8sqHtJcud3iwWS1mYToW8ZdYbkRtt1nS9ZObBgE7abIApAF0iiKUPWq8P9Sxrt9L+OTUKTy2gXzq0G3SNUbfRFqj6bMpVXdwU28ih1u8qaK83N7S+DcBurj7gylAx9eKvc1kNpQguTbfLXvQYellm62NCgNE+ArwBqiJhw9plfdd2" + }, + "sslAttrs": { + "validityPeriod": "", + "clientPublicKeyPem": "" + }, + "sshAttrs": { + "validity": "", + "sshHostRSAKey": "", + "sshClientRSAKey": "" + }, + "action": "generateRootX509Cert" + } diff --git a/private-ca/get-certificate.sh b/private-ca/get-certificate.sh new file mode 100755 index 0000000..85dd95d --- /dev/null +++ b/private-ca/get-certificate.sh @@ -0,0 +1,66 @@ +#!/bin/bash + +CA_LAMBDA_FUNCTION_NAME="privateCA" + +# Edit values here +###################################################### +# CA_ACTION="getHostSSHCert" +# CA_ACTION="getClientSSHCert" +CA_ACTION="generateRootX509Cert" +# CA_ACTION="generateClientX509Cert" + +# # Get client SSL certificate +# SSL_ATTRS_VALIDITY="" +# SSL_CLIENT_PUBKEY_PEM="" + +# # Get host SSH certificate +# SSH_ATTRS_VALIDITY="" +# SSH_HOST_RSA_PUBKEY="" + +# # Get client SSH certificate +# SSH_ATTRS_VALIDITY="" +# SSH_CLIENT_RSA_PUBKEY="" +###################################################### + +# Temporary Credentials +TEMP_CREDS=$(aws sts get-session-token) + +ACCESS_KEY_ID=$(echo $TEMP_CREDS | jq -r ".Credentials.AccessKeyId") +SECRET_ACCESS_KEY=$(echo $TEMP_CREDS | jq -r ".Credentials.SecretAccessKey") +SESSION_TOKEN=$(echo $TEMP_CREDS | jq -r ".Credentials.SessionToken") + +# Auth Headers +python -m venv env && source env/bin/activate +pip install boto3 + +output=$(python auth-header.py $ACCESS_KEY_ID $SECRET_ACCESS_KEY $SESSION_TOKEN) +auth_header=$(echo $output | jq -r ".Authorization") +date=$(echo $output | jq -r ".Date") + +echo "{\"auth\": { + \"amzDate\": \"${date}\", + \"authorizationHeader\": \"${auth_header}\", + \"sessionToken\": \"${SESSION_TOKEN}\" + }, + \"sslAttrs\": { + \"validityPeriod\": \"${SSL_ATTRS_VALIDITY}\", + \"clientPublicKeyPem\": \"${SSL_CLIENT_PUBKEY_PEM}\" + }, + \"sshAttrs\": { + \"validity\": \"${SSH_ATTRS_VALIDITY}\", + \"sshHostRSAKey\": \"${SSH_HOST_RSA_PUBKEY}\", + \"sshClientRSAKey\": \"${SSH_CLIENT_RSA_PUBKEY}\" + }, + \"action\": \"${CA_ACTION}\" + }" > event.json + + +aws lambda invoke --function-name ${CA_LAMBDA_FUNCTION_NAME} --cli-binary-format raw-in-base64-out --payload file://event.json response.json +response_body=$(cat response.json | jq -r ".body" | tr -d '"' | sed 's/\\r\\n/ \ +/g') + +echo ${response_body} + +# Clean up +deactivate +sudo rm -r env *.json \ No newline at end of file diff --git a/private-ca/lambda/generate-client-ssh-cert.js b/private-ca/lambda/generate-client-ssh-cert.js new file mode 100644 index 0000000..20eb9ff --- /dev/null +++ b/private-ca/lambda/generate-client-ssh-cert.js @@ -0,0 +1,30 @@ +import fs from 'fs'; +import child_process from 'child_process'; +import util from 'util'; + +const exec = util.promisify(child_process.exec); + +export const signClientSSHCertificate = async (callerIdentity, secret, sshAttrs) => { + + const arn = callerIdentity.GetCallerIdentityResponse.GetCallerIdentityResult.Arn; + const roleName = arn.match(/\/([^/]+)$/)?.[1]; + + const caKeyPath = "/tmp/client_ca"; + const publicKeyName = "ssh_client_rsa_key"; + const publicKeyPath = "/tmp/" + publicKeyName + ".pub"; + const certificatePath = "/tmp/" + publicKeyName + "-cert.pub"; + const user_ca = Buffer.from(secret.user_ca, 'base64').toString('utf-8'); + fs.writeFileSync(caKeyPath, user_ca); + fs.writeFileSync(publicKeyPath, sshAttrs['sshClientRSAKey']); + + let { stdout, stderr } = await exec(`chmod 600 ${caKeyPath}`); + console.log('stdout:', stdout); + console.log('stderr:', stderr); + + ({ stdout, stderr } = await exec(`ssh-keygen -s ${caKeyPath} -I host_${roleName} -n ${roleName} -V +${sshAttrs['validity']} ${publicKeyPath}`)); + console.log('stdout:', stdout); + console.log('stderr:', stderr); + + const certificate = fs.readFileSync(certificatePath, 'utf8'); + return certificate; +}; diff --git a/private-ca/lambda/generate-client-x509-cert.js b/private-ca/lambda/generate-client-x509-cert.js new file mode 100644 index 0000000..61c5758 --- /dev/null +++ b/private-ca/lambda/generate-client-x509-cert.js @@ -0,0 +1,65 @@ +import forge from 'node-forge'; +import md from 'node-forge'; +import { generateRootX509Cert } from './generate-root-x509-cert.js'; +import { getSecret } from './secret-manager-utils.js'; + +export const generateClientX509Cert = async (callerIdentity, secret, sslAttrs) => { + + const pki = forge.pki; + + const arn = callerIdentity.GetCallerIdentityResponse.GetCallerIdentityResult.Arn; + const roleName = arn.match(/\/([^/]+)$/)?.[1]; + + // Load the root certificate private key from a file or string + const rootKeyPem = Buffer.from(secret.root_ssl_private_key, 'base64').toString('utf-8'); + const rootKey = pki.privateKeyFromPem(rootKeyPem); + + // Load the root certificate public key from a file or string + let rootCertKey = 'rootX509cert'; + if(!(rootCertKey in secret)) + { + console.log("No root certificate found. Creating new root certificate.") + await generateRootX509Cert(secret); + secret = await getSecret('privateCA'); + } + let rootCertPem = Buffer.from(secret['rootX509cert'], 'base64').toString('utf-8'); + const rootCert = pki.certificateFromPem(rootCertPem); + + // openssl genrsa -out key.pem 2048 + // openssl rsa -in key.pem -outform PEM -pubout -out public.pem + const clientPublicKey = pki.publicKeyFromPem(sslAttrs.clientPublicKeyPem); + + // Create a client certificate signing request (CSR) + const clientCertReq = pki.createCertificationRequest(); + clientCertReq.publicKey = clientPublicKey; + clientCertReq.setSubject([ + { name: 'countryName', value: 'US' }, + { name: 'localityName', value: 'California' }, + { name: 'organizationName', value: 'Fundwave' }, + { name: 'organizationalUnitName', value: 'Fundwave' }, + { name: 'commonName', value: roleName } + ]); + + // Sign the client certificate request with the root certificate and private key + const clientCert = pki.createCertificate(); + clientCert.publicKey = clientCertReq.publicKey; + clientCert.serialNumber = '01'; // Set a unique serial number + + const startDate = new Date(); // Valid from the current date and time + const endDate = new Date(); + endDate.setDate(startDate.getDate() + sslAttrs.validityPeriod); + clientCert.validity.notBefore = startDate; + clientCert.validity.notAfter = endDate; + + clientCert.setSubject(clientCertReq.subject.attributes); + clientCert.setIssuer(rootCert.subject.attributes); + clientCert.setExtensions([ + { name: 'basicConstraints', cA: false }, + { name: 'keyUsage', digitalSignature: true, nonRepudiation: true, keyEncipherment: true }, + ]); + clientCert.sign(rootKey, md.sha256.create()); + + // Convert the signed client certificate to PEM format + const clientCertPem = pki.certificateToPem(clientCert); + return clientCertPem; +} \ No newline at end of file diff --git a/private-ca/lambda/generate-host-ssh-cert.js b/private-ca/lambda/generate-host-ssh-cert.js new file mode 100644 index 0000000..1aa90fa --- /dev/null +++ b/private-ca/lambda/generate-host-ssh-cert.js @@ -0,0 +1,30 @@ +import fs from 'fs'; +import child_process from 'child_process'; +import util from 'util'; + +const exec = util.promisify(child_process.exec); + +export const signHostSSHCertificate = async (callerIdentity, secret, sshAttrs) => { + + const arn = callerIdentity.GetCallerIdentityResponse.GetCallerIdentityResult.Arn; + const roleName = arn.match(/\/([^/]+)$/)?.[1]; + + const caKeyPath = "/tmp/host_ca"; + const publicKeyName = "ssh_host_rsa_key"; + const publicKeyPath = "/tmp/" + publicKeyName + ".pub"; + const certificatePath = "/tmp/" + publicKeyName + "-cert.pub"; + const host_ca = Buffer.from(secret.host_ca, 'base64').toString('utf-8'); + fs.writeFileSync(caKeyPath, host_ca); + fs.writeFileSync(publicKeyPath, sshAttrs['sshHostRSAKey']); + + let { stdout, stderr } = await exec(`chmod 600 ${caKeyPath}`); + console.log('stdout:', stdout); + console.log('stderr:', stderr); + + ({ stdout, stderr } = await exec(`ssh-keygen -s ${caKeyPath} -I host_${roleName} -h -n ${roleName} -V +${sshAttrs['validity']} ${publicKeyPath}`)); + console.log('stdout:', stdout); + console.log('stderr:', stderr); + + const certificate = fs.readFileSync(certificatePath, 'utf8'); + return certificate; +}; diff --git a/private-ca/lambda/generate-root-x509-cert.js b/private-ca/lambda/generate-root-x509-cert.js new file mode 100644 index 0000000..eb3c44a --- /dev/null +++ b/private-ca/lambda/generate-root-x509-cert.js @@ -0,0 +1,36 @@ +import forge from 'node-forge'; +import { updateSecret } from './secret-manager-utils.js'; + +export const generateRootX509Cert = async (secret) => { + + let publicKeyPem = Buffer.from(secret.root_ssl_public_key, 'base64').toString('utf-8'); + let publicKey = forge.pki.publicKeyFromPem(publicKeyPem); + let privateKeyPem = Buffer.from(secret.root_ssl_private_key, 'base64').toString('utf-8'); + let privateKey = forge.pki.privateKeyFromPem(privateKeyPem); + + let cert = forge.pki.createCertificate(); + cert.publicKey = publicKey; + cert.serialNumber = '01'; + cert.validity.notBefore = new Date(); + cert.validity.notAfter = new Date(); + cert.validity.notAfter.setFullYear(cert.validity.notBefore.getFullYear() + 1); + let attrs = [ + { name: 'countryName', value: 'US' }, + { name: 'localityName', value: 'California' }, + { name: 'organizationName', value: 'Fundwave' }, + { name: 'organizationalUnitName', value: 'Fundwave' }, + { name: 'commonName', value: 'FundwaveCA' } + ]; + cert.setSubject(attrs); + cert.setIssuer(attrs); + cert.setExtensions([{ + name: 'basicConstraints', + cA: true + }]); + + cert.sign(privateKey, forge.md.sha256.create()); + + let rootX509Cert = forge.pki.certificateToPem(cert); + await updateSecret('privateCA', 'rootX509cert', Buffer.from(rootX509Cert).toString('base64')); + +} \ No newline at end of file diff --git a/private-ca/lambda/get-caller-identity.js b/private-ca/lambda/get-caller-identity.js new file mode 100644 index 0000000..6fc70c5 --- /dev/null +++ b/private-ca/lambda/get-caller-identity.js @@ -0,0 +1,52 @@ +import https from 'https'; + +export const getCallerIdentity = (auth) => { + + const host = 'sts.ap-south-1.amazonaws.com'; + const path = '/'; + const payload = 'Action=GetCallerIdentity&Version=2011-06-15'; + + // Set the headers + const headers = { + 'accept': 'application/json', + 'Content-Type': 'application/x-www-form-urlencoded', + 'X-Amz-Date': auth.amzDate, + 'Authorization': auth.authorizationHeader, + 'X-Amz-Security-Token': auth.sessionToken + }; + + const options = { + hostname: host, + path: path, + method: 'POST', + headers: headers + }; + + return new Promise((resolve, reject) => { + + const req = https.request(options, (res) => { + let data = ''; + + res.on('data', (chunk) => { + data += chunk; + }); + + res.on('end', () => { + try { + resolve(JSON.parse(data)); + } catch (err) { + reject(new Error(err)); + } + }); + }); + + req.on('error', (error) => { + // console.error('Error:', error); + reject(new Error(error)); + }); + + req.write(payload); + req.end(); + }); + +}; \ No newline at end of file diff --git a/private-ca/lambda/index.js b/private-ca/lambda/index.js new file mode 100644 index 0000000..51db91b --- /dev/null +++ b/private-ca/lambda/index.js @@ -0,0 +1,56 @@ +import { signHostSSHCertificate } from './generate-host-ssh-cert.js'; +import { signClientSSHCertificate } from './generate-client-ssh-cert.js'; +import { getCallerIdentity } from './get-caller-identity.js'; +import { generateRootX509Cert } from './generate-root-x509-cert.js'; +import { generateClientX509Cert } from './generate-client-x509-cert.js'; +import { getSecret } from './secret-manager-utils.js'; + +export const handler = async (event) => { + + // auth + const callerIdentity = await getCallerIdentity(event.auth); + + // secret + const secret = await getSecret('privateCA'); + + // action + let res = {}; + switch(event.action) { + case "getHostSSHCert": + const hostSSHCert = await signHostSSHCertificate(callerIdentity, secret, event.sshAttrs); + res = { + statusCode: 200, + body: JSON.stringify(hostSSHCert) + } + return res; + case "getClientSSHCert": + const clientSSHCert = await signClientSSHCertificate(callerIdentity, secret, event.sshAttrs); + console.log(clientSSHCert) + res = { + statusCode: 200, + body: JSON.stringify(clientSSHCert) + } + return res; + case "generateRootX509Cert": + generateRootX509Cert(secret) + res = { + statusCode: 200, + body: "Generated root X.509 certificate successfully" + } + return res; + case "generateClientX509Cert": + const clientSSLCert = await generateClientX509Cert(callerIdentity, secret, event.sslAttrs); + res = { + statusCode: 200, + body: JSON.stringify(clientSSLCert) + } + return res; + default: + console.log("Invalid Action") + res = { + statusCode: 400, + body: JSON.stringify('Invalid Action') + }; + return res; + } +}; diff --git a/private-ca/lambda/package-lock.json b/private-ca/lambda/package-lock.json new file mode 100644 index 0000000..611abaf --- /dev/null +++ b/private-ca/lambda/package-lock.json @@ -0,0 +1,596 @@ +{ + "name": "private-ca", + "version": "1.0.0", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "name": "private-ca", + "version": "1.0.0", + "license": "ISC", + "dependencies": { + "aws-sdk": "^2.1398.0", + "node-forge": "^1.3.1" + } + }, + "node_modules/available-typed-arrays": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz", + "integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/aws-sdk": { + "version": "2.1398.0", + "resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.1398.0.tgz", + "integrity": "sha512-jiHAhKPPKDHZdwsbY9FD/WfX9RfQ7+7eKvXyXr7BYf0JIShok4TWan/iLdNWCtU0BhXYl+bfG9W0pG4rwJ9Ivg==", + "dependencies": { + "buffer": "4.9.2", + "events": "1.1.1", + "ieee754": "1.1.13", + "jmespath": "0.16.0", + "querystring": "0.2.0", + "sax": "1.2.1", + "url": "0.10.3", + "util": "^0.12.4", + "uuid": "8.0.0", + "xml2js": "0.5.0" + }, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/buffer": { + "version": "4.9.2", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.2.tgz", + "integrity": "sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg==", + "dependencies": { + "base64-js": "^1.0.2", + "ieee754": "^1.1.4", + "isarray": "^1.0.0" + } + }, + "node_modules/call-bind": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", + "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "dependencies": { + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/events": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz", + "integrity": "sha512-kEcvvCBByWXGnZy6JUlgAp2gBIUjfCAV6P6TgT1/aaQKcmuAEC4OZTV1I4EWQLz2gxZw76atuVyvHhTxvi0Flw==", + "engines": { + "node": ">=0.4.x" + } + }, + "node_modules/for-each": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", + "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", + "dependencies": { + "is-callable": "^1.1.3" + } + }, + "node_modules/function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" + }, + "node_modules/get-intrinsic": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.1.tgz", + "integrity": "sha512-2DcsyfABl+gVHEfCOaTrWgyt+tb6MSEGmKq+kI5HwLbIYgjgmMcV8KQ41uaKz1xxUcn9tJtgFbQUEVcEbd0FYw==", + "dependencies": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-proto": "^1.0.1", + "has-symbols": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gopd": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", + "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", + "dependencies": { + "get-intrinsic": "^1.1.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dependencies": { + "function-bind": "^1.1.1" + }, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/has-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz", + "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", + "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", + "dependencies": { + "has-symbols": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/ieee754": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz", + "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==" + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "node_modules/is-arguments": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz", + "integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==", + "dependencies": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-callable": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-generator-function": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.10.tgz", + "integrity": "sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==", + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-typed-array": { + "version": "1.1.10", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.10.tgz", + "integrity": "sha512-PJqgEHiWZvMpaFZ3uTc8kHPM4+4ADTlDniuQL7cU/UDA0Ql7F70yGfHph3cLNe+c9toaigv+DFzTJKhc2CtO6A==", + "dependencies": { + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==" + }, + "node_modules/jmespath": { + "version": "0.16.0", + "resolved": "https://registry.npmjs.org/jmespath/-/jmespath-0.16.0.tgz", + "integrity": "sha512-9FzQjJ7MATs1tSpnco1K6ayiYE3figslrXA72G2HQ/n76RzvYlofyi5QM+iX4YRs/pu3yzxlVQSST23+dMDknw==", + "engines": { + "node": ">= 0.6.0" + } + }, + "node_modules/node-forge": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz", + "integrity": "sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==", + "engines": { + "node": ">= 6.13.0" + } + }, + "node_modules/punycode": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", + "integrity": "sha512-RofWgt/7fL5wP1Y7fxE7/EmTLzQVnB0ycyibJ0OOHIlJqTNzglYFxVwETOcIoJqJmpDXJ9xImDv+Fq34F/d4Dw==" + }, + "node_modules/querystring": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", + "integrity": "sha512-X/xY82scca2tau62i9mDyU9K+I+djTMUsvwf7xnUX5GLvVzgJybOJf4Y6o9Zx3oJK/LSXg5tTZBjwzqVPaPO2g==", + "deprecated": "The querystring API is considered Legacy. new code should use the URLSearchParams API instead.", + "engines": { + "node": ">=0.4.x" + } + }, + "node_modules/sax": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.1.tgz", + "integrity": "sha512-8I2a3LovHTOpm7NV5yOyO8IHqgVsfK4+UuySrXU8YXkSRX7k6hCV9b3HrkKCr3nMpgj+0bmocaJJWpvp1oc7ZA==" + }, + "node_modules/url": { + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/url/-/url-0.10.3.tgz", + "integrity": "sha512-hzSUW2q06EqL1gKM/a+obYHLIO6ct2hwPuviqTTOcfFVc61UbfJ2Q32+uGL/HCPxKqrdGB5QUwIe7UqlDgwsOQ==", + "dependencies": { + "punycode": "1.3.2", + "querystring": "0.2.0" + } + }, + "node_modules/util": { + "version": "0.12.5", + "resolved": "https://registry.npmjs.org/util/-/util-0.12.5.tgz", + "integrity": "sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==", + "dependencies": { + "inherits": "^2.0.3", + "is-arguments": "^1.0.4", + "is-generator-function": "^1.0.7", + "is-typed-array": "^1.1.3", + "which-typed-array": "^1.1.2" + } + }, + "node_modules/uuid": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.0.0.tgz", + "integrity": "sha512-jOXGuXZAWdsTH7eZLtyXMqUb9EcWMGZNbL9YcGBJl4MH4nrxHmZJhEHvyLFrkxo+28uLb/NYRcStH48fnD0Vzw==", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/which-typed-array": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.9.tgz", + "integrity": "sha512-w9c4xkx6mPidwp7180ckYWfMmvxpjlZuIudNtDf4N/tTAUB8VJbX25qZoAsrtGuYNnGw3pa0AXgbGKRB8/EceA==", + "dependencies": { + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-tostringtag": "^1.0.0", + "is-typed-array": "^1.1.10" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/xml2js": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.5.0.tgz", + "integrity": "sha512-drPFnkQJik/O+uPKpqSgr22mpuFHqKdbS835iAQrUC73L2F5WkboIRd63ai/2Yg6I1jzifPFKH2NTK+cfglkIA==", + "dependencies": { + "sax": ">=0.6.0", + "xmlbuilder": "~11.0.0" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/xmlbuilder": { + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz", + "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==", + "engines": { + "node": ">=4.0" + } + } + }, + "dependencies": { + "available-typed-arrays": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz", + "integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==" + }, + "aws-sdk": { + "version": "2.1398.0", + "resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.1398.0.tgz", + "integrity": "sha512-jiHAhKPPKDHZdwsbY9FD/WfX9RfQ7+7eKvXyXr7BYf0JIShok4TWan/iLdNWCtU0BhXYl+bfG9W0pG4rwJ9Ivg==", + "requires": { + "buffer": "4.9.2", + "events": "1.1.1", + "ieee754": "1.1.13", + "jmespath": "0.16.0", + "querystring": "0.2.0", + "sax": "1.2.1", + "url": "0.10.3", + "util": "^0.12.4", + "uuid": "8.0.0", + "xml2js": "0.5.0" + } + }, + "base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==" + }, + "buffer": { + "version": "4.9.2", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.2.tgz", + "integrity": "sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg==", + "requires": { + "base64-js": "^1.0.2", + "ieee754": "^1.1.4", + "isarray": "^1.0.0" + } + }, + "call-bind": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", + "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "requires": { + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.2" + } + }, + "events": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz", + "integrity": "sha512-kEcvvCBByWXGnZy6JUlgAp2gBIUjfCAV6P6TgT1/aaQKcmuAEC4OZTV1I4EWQLz2gxZw76atuVyvHhTxvi0Flw==" + }, + "for-each": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", + "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", + "requires": { + "is-callable": "^1.1.3" + } + }, + "function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" + }, + "get-intrinsic": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.1.tgz", + "integrity": "sha512-2DcsyfABl+gVHEfCOaTrWgyt+tb6MSEGmKq+kI5HwLbIYgjgmMcV8KQ41uaKz1xxUcn9tJtgFbQUEVcEbd0FYw==", + "requires": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-proto": "^1.0.1", + "has-symbols": "^1.0.3" + } + }, + "gopd": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", + "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", + "requires": { + "get-intrinsic": "^1.1.3" + } + }, + "has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "requires": { + "function-bind": "^1.1.1" + } + }, + "has-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz", + "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==" + }, + "has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==" + }, + "has-tostringtag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", + "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", + "requires": { + "has-symbols": "^1.0.2" + } + }, + "ieee754": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz", + "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==" + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "is-arguments": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz", + "integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==", + "requires": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + } + }, + "is-callable": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==" + }, + "is-generator-function": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.10.tgz", + "integrity": "sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==", + "requires": { + "has-tostringtag": "^1.0.0" + } + }, + "is-typed-array": { + "version": "1.1.10", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.10.tgz", + "integrity": "sha512-PJqgEHiWZvMpaFZ3uTc8kHPM4+4ADTlDniuQL7cU/UDA0Ql7F70yGfHph3cLNe+c9toaigv+DFzTJKhc2CtO6A==", + "requires": { + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-tostringtag": "^1.0.0" + } + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==" + }, + "jmespath": { + "version": "0.16.0", + "resolved": "https://registry.npmjs.org/jmespath/-/jmespath-0.16.0.tgz", + "integrity": "sha512-9FzQjJ7MATs1tSpnco1K6ayiYE3figslrXA72G2HQ/n76RzvYlofyi5QM+iX4YRs/pu3yzxlVQSST23+dMDknw==" + }, + "node-forge": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz", + "integrity": "sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==" + }, + "punycode": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", + "integrity": "sha512-RofWgt/7fL5wP1Y7fxE7/EmTLzQVnB0ycyibJ0OOHIlJqTNzglYFxVwETOcIoJqJmpDXJ9xImDv+Fq34F/d4Dw==" + }, + "querystring": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", + "integrity": "sha512-X/xY82scca2tau62i9mDyU9K+I+djTMUsvwf7xnUX5GLvVzgJybOJf4Y6o9Zx3oJK/LSXg5tTZBjwzqVPaPO2g==" + }, + "sax": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.1.tgz", + "integrity": "sha512-8I2a3LovHTOpm7NV5yOyO8IHqgVsfK4+UuySrXU8YXkSRX7k6hCV9b3HrkKCr3nMpgj+0bmocaJJWpvp1oc7ZA==" + }, + "url": { + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/url/-/url-0.10.3.tgz", + "integrity": "sha512-hzSUW2q06EqL1gKM/a+obYHLIO6ct2hwPuviqTTOcfFVc61UbfJ2Q32+uGL/HCPxKqrdGB5QUwIe7UqlDgwsOQ==", + "requires": { + "punycode": "1.3.2", + "querystring": "0.2.0" + } + }, + "util": { + "version": "0.12.5", + "resolved": "https://registry.npmjs.org/util/-/util-0.12.5.tgz", + "integrity": "sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==", + "requires": { + "inherits": "^2.0.3", + "is-arguments": "^1.0.4", + "is-generator-function": "^1.0.7", + "is-typed-array": "^1.1.3", + "which-typed-array": "^1.1.2" + } + }, + "uuid": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.0.0.tgz", + "integrity": "sha512-jOXGuXZAWdsTH7eZLtyXMqUb9EcWMGZNbL9YcGBJl4MH4nrxHmZJhEHvyLFrkxo+28uLb/NYRcStH48fnD0Vzw==" + }, + "which-typed-array": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.9.tgz", + "integrity": "sha512-w9c4xkx6mPidwp7180ckYWfMmvxpjlZuIudNtDf4N/tTAUB8VJbX25qZoAsrtGuYNnGw3pa0AXgbGKRB8/EceA==", + "requires": { + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-tostringtag": "^1.0.0", + "is-typed-array": "^1.1.10" + } + }, + "xml2js": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.5.0.tgz", + "integrity": "sha512-drPFnkQJik/O+uPKpqSgr22mpuFHqKdbS835iAQrUC73L2F5WkboIRd63ai/2Yg6I1jzifPFKH2NTK+cfglkIA==", + "requires": { + "sax": ">=0.6.0", + "xmlbuilder": "~11.0.0" + } + }, + "xmlbuilder": { + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz", + "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==" + } + } +} diff --git a/private-ca/lambda/package.json b/private-ca/lambda/package.json new file mode 100644 index 0000000..ecb3f6b --- /dev/null +++ b/private-ca/lambda/package.json @@ -0,0 +1,17 @@ +{ + "name": "private-ca", + "version": "1.0.0", + "description": "", + "main": "index.js", + "type": "module", + "scripts": { + "start": "node index.js", + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "", + "license": "ISC", + "dependencies": { + "aws-sdk": "^2.1398.0", + "node-forge": "^1.3.1" + } +} diff --git a/private-ca/lambda/secret-manager-utils.js b/private-ca/lambda/secret-manager-utils.js new file mode 100644 index 0000000..7399110 --- /dev/null +++ b/private-ca/lambda/secret-manager-utils.js @@ -0,0 +1,20 @@ +import aws from "aws-sdk"; + +var secretsmanager = new aws.SecretsManager({ region: 'ap-south-1' }); + +export const getSecret = async (secretId) => { + const secretString = await secretsmanager.getSecretValue({ SecretId: secretId }).promise(); + const secret = JSON.parse(secretString.SecretString); + return secret; +}; + +export const updateSecret = async (secretId, key, value) => { + let secret = await getSecret(secretId); + secret[key] = value; + var params = { + SecretId: secretId, + SecretString: JSON.stringify(secret) + }; + const updateRes = await secretsmanager.updateSecret(params).promise(); + return updateRes; +} diff --git a/private-ca/response.json b/private-ca/response.json new file mode 100644 index 0000000..b5afd74 --- /dev/null +++ b/private-ca/response.json @@ -0,0 +1 @@ +{"errorMessage":"2023-06-22T12:39:13.501Z db4da47d-93f8-4e4c-b49c-ee1daa1e6c55 Task timed out after 3.02 seconds"} \ No newline at end of file From 84a9e1d3e563e2b1f2af72ba2848ae809e346737 Mon Sep 17 00:00:00 2001 From: sudo-rgorai Date: Fri, 23 Jun 2023 23:37:18 +0530 Subject: [PATCH 02/36] Take AWS region as input --- private-ca/auth-header.py | 6 ++-- private-ca/create-lambda.sh | 23 --------------- private-ca/create-layer.sh | 21 -------------- private-ca/create-role.sh | 22 -------------- private-ca/create-secret.sh | 29 ------------------- private-ca/event.json | 16 ---------- private-ca/get-certificate.sh | 10 +++++-- .../lambda/generate-client-x509-cert.js | 7 +++-- private-ca/lambda/generate-root-x509-cert.js | 4 +-- private-ca/lambda/get-caller-identity.js | 11 +++---- private-ca/lambda/index.js | 8 ++--- private-ca/lambda/secret-manager-utils.js | 8 ++--- private-ca/response.json | 1 - 13 files changed, 30 insertions(+), 136 deletions(-) delete mode 100755 private-ca/create-lambda.sh delete mode 100755 private-ca/create-layer.sh delete mode 100755 private-ca/create-role.sh delete mode 100755 private-ca/create-secret.sh delete mode 100644 private-ca/event.json delete mode 100644 private-ca/response.json diff --git a/private-ca/auth-header.py b/private-ca/auth-header.py index 0581082..ed538ce 100644 --- a/private-ca/auth-header.py +++ b/private-ca/auth-header.py @@ -8,9 +8,9 @@ access_key_id = sys.argv[1] secret_access_key = sys.argv[2] session_token = sys.argv[3] + aws_region = sys.argv[4] - region = "ap-south-1" - sts_host = "sts.ap-south-1.amazonaws.com" + sts_host = "sts." + aws_region + ".amazonaws.com" request_parameters = 'Action=GetCallerIdentity&Version=2011-06-15' request_headers = { 'Host': sts_host, @@ -19,7 +19,7 @@ request = AWSRequest(method="POST", url="/", data=request_parameters, headers=request_headers) boto_creds = Credentials(access_key_id, secret_access_key,token=session_token) - auth = SigV4Auth(boto_creds, "sts", region) + auth = SigV4Auth(boto_creds, "sts", aws_region) auth.add_auth(request) authorization = request.headers["Authorization"] diff --git a/private-ca/create-lambda.sh b/private-ca/create-lambda.sh deleted file mode 100755 index d907cec..0000000 --- a/private-ca/create-lambda.sh +++ /dev/null @@ -1,23 +0,0 @@ -#!/bin/bash - -ROLE_ARN=$1 -LAYER_ARN=$2 -FUNCTION_NAME=${3:-"privateCA"} - -# Create lambda function -cd lambda -npm i -zip -r ./lambda.zip . -mv lambda.zip ../ -cd .. - -aws lambda create-function \ - --function-name $FUNCTION_NAME \ - --runtime nodejs18.x \ - --handler index.handler \ - --zip-file fileb://lambda.zip \ - --layers $LAYER_ARN \ - --role $ROLE_ARN - -# Clean up -rm *.zip \ No newline at end of file diff --git a/private-ca/create-layer.sh b/private-ca/create-layer.sh deleted file mode 100755 index 3f276f1..0000000 --- a/private-ca/create-layer.sh +++ /dev/null @@ -1,21 +0,0 @@ -#!/bin/bash - -LAYER_NAME=${1:-"openssh"} - -echo $LAYER_NAME - -# Create OpenSSH layer -docker run --rm -v $(pwd)/openssh-layer:/lambda/opt lambci/yumda:2 yum install -y openssh -cd openssh-layer -echo "**************************************************************************************************************************************************************************************************************************************************************************************************************" -echo $LAYER_NAME -zip -yr ./openssh-layer.zip . > /dev/null -LAYER_ARN=$(aws lambda publish-layer-version \ - --layer-name $LAYER_NAME \ - --zip-file fileb://openssh-layer.zip \ - --query 'LayerVersionArn' \ - --output text) -echo $LAYER_ARN -cd .. - -rm -r openssh-layer/ *.zip \ No newline at end of file diff --git a/private-ca/create-role.sh b/private-ca/create-role.sh deleted file mode 100755 index 7b82be8..0000000 --- a/private-ca/create-role.sh +++ /dev/null @@ -1,22 +0,0 @@ -#!/bin/bash - -SECRET_ARN=$1 -ROLE_NAME=${2:-"privateCALambdaRole"} -POLICY_NAME=${3:-"AccessPrivateCASecretsPolicy"} - -# Create role for lambda -echo "{\"Version\": \"2012-10-17\",\"Statement\": [{\"Sid\": \"AllowLambdaAssumeRole\",\"Effect\": \"Allow\",\"Principal\": {\"Service\": \"lambda.amazonaws.com\"},\"Action\": \"sts:AssumeRole\"}]}" | jq . > Trust-Policy.json - -ROLE_ARN=$(aws iam create-role \ - --role-name $ROLE_NAME\ - --assume-role-policy-document file://Trust-Policy.json | jq ".Role.Arn" | tr -d '"') - -# Create Policy for Lambda Role to Read and Update Secrets -echo "{\"Version\": \"2012-10-17\",\"Statement\": [{\"Sid\": \"VisualEditor0\",\"Effect\": \"Allow\",\"Action\": [\"secretsmanager:GetSecretValue\",\"secretsmanager:UpdateSecret\"],\"Resource\": \"${SECRET_ARN}\"}]}" | jq . > Policy.json - -POLICY_ARN=$(aws iam create-policy --policy-name $POLICY_NAME --policy-document file://Policy.json | jq ".Policy.Arn" | tr -d '"') - -# Attach policy to role -aws iam attach-role-policy --role-name $ROLE_NAME --policy-arn $POLICY_ARN - -rm *.json \ No newline at end of file diff --git a/private-ca/create-secret.sh b/private-ca/create-secret.sh deleted file mode 100755 index f520e70..0000000 --- a/private-ca/create-secret.sh +++ /dev/null @@ -1,29 +0,0 @@ -#!/bin/bash - -SECRET_NAME=${1:-"privateCASecret"} - -# Generate Keys -ssh-keygen -t rsa -b 4096 -f host_ca -C host_ca -N "" -ssh-keygen -t rsa -b 4096 -f user_ca -C user_ca -N "" - -openssl genrsa -out key.pem 2048 -openssl rsa -in key.pem -outform PEM -pubout -out public.pem - -HOST_CA_PRIVATE_KEY=$(cat host_ca | base64 -w 0) -HOST_CA_PUBLIC_KEY=$(cat host_ca.pub | base64 -w 0) -USER_CA_PRIVATE_KEY=$(cat user_ca | base64 -w 0) -USER_CA_PUBLIC_KEY=$(cat user_ca.pub | base64 -w 0) -ROOT_SSL_PRIVATE_KEY=$(cat key.pem | base64 -w 0) -ROOT_SSL_PUBLIC_KEY=$(cat public.pem | base64 -w 0) - -echo "{\"host_ca\": \"${HOST_CA_PRIVATE_KEY}\", \"host_ca.pub\": \"${HOST_CA_PUBLIC_KEY}\", \"user_ca\": \"${USER_CA_PRIVATE_KEY}\",\"user_ca.pub\": \"${USER_CA_PUBLIC_KEY}\",\"root_ssl_private_key\": \"${ROOT_SSL_PRIVATE_KEY}\",\"root_ssl_public_key\": \"${ROOT_SSL_PUBLIC_KEY}\"}" | jq . > secret.json - -# Create Secret -SECRET_ARN=$(aws secretsmanager create-secret \ - --name $SECRET_NAME \ - --secret-string file://secret.json | jq ".ARN" | tr -d '"') - -echo SECRET_ARN - -# Clean up -rm host_ca user_ca *.pub *.pem *.json \ No newline at end of file diff --git a/private-ca/event.json b/private-ca/event.json deleted file mode 100644 index c7fc0fd..0000000 --- a/private-ca/event.json +++ /dev/null @@ -1,16 +0,0 @@ -{"auth": { - "amzDate": "20230622T124133Z", - "authorizationHeader": "AWS4-HMAC-SHA256 Credential=ASIAVKZF47HIBE5VYGHP/20230622/ap-south-1/sts/aws4_request, SignedHeaders=host;x-amz-date;x-amz-security-token, Signature=cd5dc4732c5967c067691ea1bac2a4f988189f2bb71c3d53aabff3c9d1478864", - "sessionToken": "IQoJb3JpZ2luX2VjEH0aCmFwLXNvdXRoLTEiSDBGAiEAwKCn4CisD/5vCwGqVlBa6e7GJqyRwniflw6lv+nHcT8CIQCxrgwTTYY+Dl/bxNjpYjiDxOOvyeodqyFPYDz5ltmcASr0AQjW//////////8BEAEaDDM2Njc2MjMyNjQ4MCIMtsxFJMeOiT1nikSGKsgBsNXYCmBVHMKmGxJLBUOgxlr+faS+xEEBre6Yo4GlFsHCwMMCBdowAi1PF+2qV3MB/U+Mj6FHeka/iSa+6mNcTYFA/bSx30KxVbO1QDYfQBRgShzRfMTGA3aNUJSIMj8dSgTFR2/HAqAUor/ROAicO7XO5URLCdlgt6Y4qrw/sfpq8wZOFVswoADhW0ABIHINJlZehLb2LV6gexgnl4jiKFgBj2k/0w5w5Gon0YYoPhY4b105Oh+JzkUUT1UHWVVdp4s8d+azrN4w24HRpAY6lwEXFuSzxh+otNnXHbR918jboXfwEUi8sqHtJcud3iwWS1mYToW8ZdYbkRtt1nS9ZObBgE7abIApAF0iiKUPWq8P9Sxrt9L+OTUKTy2gXzq0G3SNUbfRFqj6bMpVXdwU28ih1u8qaK83N7S+DcBurj7gylAx9eKvc1kNpQguTbfLXvQYellm62NCgNE+ArwBqiJhw9plfdd2" - }, - "sslAttrs": { - "validityPeriod": "", - "clientPublicKeyPem": "" - }, - "sshAttrs": { - "validity": "", - "sshHostRSAKey": "", - "sshClientRSAKey": "" - }, - "action": "generateRootX509Cert" - } diff --git a/private-ca/get-certificate.sh b/private-ca/get-certificate.sh index 85dd95d..ac3951a 100755 --- a/private-ca/get-certificate.sh +++ b/private-ca/get-certificate.sh @@ -1,6 +1,8 @@ #!/bin/bash -CA_LAMBDA_FUNCTION_NAME="privateCA" +CA_LAMBDA_FUNCTION_NAME=${1:-"privateCA"} +AWS_STS_REGION=${2:-"ap-south-1"} +AWS_SECRETS_REGION=${3:-"ap-south-1"} # Edit values here ###################################################### @@ -33,7 +35,7 @@ SESSION_TOKEN=$(echo $TEMP_CREDS | jq -r ".Credentials.SessionToken") python -m venv env && source env/bin/activate pip install boto3 -output=$(python auth-header.py $ACCESS_KEY_ID $SECRET_ACCESS_KEY $SESSION_TOKEN) +output=$(python auth-header.py $ACCESS_KEY_ID $SECRET_ACCESS_KEY $SESSION_TOKEN $AWS_REGION) auth_header=$(echo $output | jq -r ".Authorization") date=$(echo $output | jq -r ".Date") @@ -51,7 +53,9 @@ echo "{\"auth\": { \"sshHostRSAKey\": \"${SSH_HOST_RSA_PUBKEY}\", \"sshClientRSAKey\": \"${SSH_CLIENT_RSA_PUBKEY}\" }, - \"action\": \"${CA_ACTION}\" + \"action\": \"${CA_ACTION}\", + \"awsSTSRegion\": \"${AWS_STS_REGION}\", + \"awsSecretsRegion\": \"${AWS_SECRETS_REGION}\" }" > event.json diff --git a/private-ca/lambda/generate-client-x509-cert.js b/private-ca/lambda/generate-client-x509-cert.js index 61c5758..db35391 100644 --- a/private-ca/lambda/generate-client-x509-cert.js +++ b/private-ca/lambda/generate-client-x509-cert.js @@ -3,9 +3,10 @@ import md from 'node-forge'; import { generateRootX509Cert } from './generate-root-x509-cert.js'; import { getSecret } from './secret-manager-utils.js'; -export const generateClientX509Cert = async (callerIdentity, secret, sslAttrs) => { +export const generateClientX509Cert = async (callerIdentity, secret, event) => { const pki = forge.pki; + const sslAttrs = event.sslAttrs; const arn = callerIdentity.GetCallerIdentityResponse.GetCallerIdentityResult.Arn; const roleName = arn.match(/\/([^/]+)$/)?.[1]; @@ -19,8 +20,8 @@ export const generateClientX509Cert = async (callerIdentity, secret, sslAttrs) = if(!(rootCertKey in secret)) { console.log("No root certificate found. Creating new root certificate.") - await generateRootX509Cert(secret); - secret = await getSecret('privateCA'); + await generateRootX509Cert(secret, event); + secret = await getSecret(event.awsSecretsRegion, 'privateCA'); } let rootCertPem = Buffer.from(secret['rootX509cert'], 'base64').toString('utf-8'); const rootCert = pki.certificateFromPem(rootCertPem); diff --git a/private-ca/lambda/generate-root-x509-cert.js b/private-ca/lambda/generate-root-x509-cert.js index eb3c44a..e72aeee 100644 --- a/private-ca/lambda/generate-root-x509-cert.js +++ b/private-ca/lambda/generate-root-x509-cert.js @@ -1,7 +1,7 @@ import forge from 'node-forge'; import { updateSecret } from './secret-manager-utils.js'; -export const generateRootX509Cert = async (secret) => { +export const generateRootX509Cert = async (secret, event) => { let publicKeyPem = Buffer.from(secret.root_ssl_public_key, 'base64').toString('utf-8'); let publicKey = forge.pki.publicKeyFromPem(publicKeyPem); @@ -31,6 +31,6 @@ export const generateRootX509Cert = async (secret) => { cert.sign(privateKey, forge.md.sha256.create()); let rootX509Cert = forge.pki.certificateToPem(cert); - await updateSecret('privateCA', 'rootX509cert', Buffer.from(rootX509Cert).toString('base64')); + await updateSecret(event.awsSecretsRegion, 'privateCA', 'rootX509cert', Buffer.from(rootX509Cert).toString('base64')); } \ No newline at end of file diff --git a/private-ca/lambda/get-caller-identity.js b/private-ca/lambda/get-caller-identity.js index 6fc70c5..5d918ab 100644 --- a/private-ca/lambda/get-caller-identity.js +++ b/private-ca/lambda/get-caller-identity.js @@ -1,10 +1,12 @@ import https from 'https'; -export const getCallerIdentity = (auth) => { +export const getCallerIdentity = (event) => { - const host = 'sts.ap-south-1.amazonaws.com'; - const path = '/'; - const payload = 'Action=GetCallerIdentity&Version=2011-06-15'; + const auth = event.auth; + const region = event.awsSTSRegion; + const host = 'sts.' + region + '.amazonaws.com'; + const path = '/'; + const payload = 'Action=GetCallerIdentity&Version=2011-06-15'; // Set the headers const headers = { @@ -41,7 +43,6 @@ export const getCallerIdentity = (auth) => { }); req.on('error', (error) => { - // console.error('Error:', error); reject(new Error(error)); }); diff --git a/private-ca/lambda/index.js b/private-ca/lambda/index.js index 51db91b..abb5478 100644 --- a/private-ca/lambda/index.js +++ b/private-ca/lambda/index.js @@ -8,10 +8,10 @@ import { getSecret } from './secret-manager-utils.js'; export const handler = async (event) => { // auth - const callerIdentity = await getCallerIdentity(event.auth); + const callerIdentity = await getCallerIdentity(event); // secret - const secret = await getSecret('privateCA'); + const secret = await getSecret(event.awsSecretsRegion, 'privateCA'); // action let res = {}; @@ -32,14 +32,14 @@ export const handler = async (event) => { } return res; case "generateRootX509Cert": - generateRootX509Cert(secret) + generateRootX509Cert(secret, event) res = { statusCode: 200, body: "Generated root X.509 certificate successfully" } return res; case "generateClientX509Cert": - const clientSSLCert = await generateClientX509Cert(callerIdentity, secret, event.sslAttrs); + const clientSSLCert = await generateClientX509Cert(callerIdentity, secret, event); res = { statusCode: 200, body: JSON.stringify(clientSSLCert) diff --git a/private-ca/lambda/secret-manager-utils.js b/private-ca/lambda/secret-manager-utils.js index 7399110..c645c29 100644 --- a/private-ca/lambda/secret-manager-utils.js +++ b/private-ca/lambda/secret-manager-utils.js @@ -1,14 +1,14 @@ import aws from "aws-sdk"; -var secretsmanager = new aws.SecretsManager({ region: 'ap-south-1' }); - -export const getSecret = async (secretId) => { +export const getSecret = async (secretRegion, secretId) => { + var secretsmanager = new aws.SecretsManager({ region: secretRegion }); const secretString = await secretsmanager.getSecretValue({ SecretId: secretId }).promise(); const secret = JSON.parse(secretString.SecretString); return secret; }; -export const updateSecret = async (secretId, key, value) => { +export const updateSecret = async (secretRegion, secretId, key, value) => { + var secretsmanager = new aws.SecretsManager({ region: secretRegion }); let secret = await getSecret(secretId); secret[key] = value; var params = { diff --git a/private-ca/response.json b/private-ca/response.json deleted file mode 100644 index b5afd74..0000000 --- a/private-ca/response.json +++ /dev/null @@ -1 +0,0 @@ -{"errorMessage":"2023-06-22T12:39:13.501Z db4da47d-93f8-4e4c-b49c-ee1daa1e6c55 Task timed out after 3.02 seconds"} \ No newline at end of file From 1e8aa25f5ad772feaf95439de041311ccf4d5a4c Mon Sep 17 00:00:00 2001 From: sudo-rgorai Date: Sat, 24 Jun 2023 17:14:02 +0530 Subject: [PATCH 03/36] Remove action for generating root X.509 certificate --- .gitignore | 2 ++ private-ca/README.md | 1 - private-ca/deploy-resources.sh | 6 ++-- private-ca/get-certificate.sh | 1 - .../lambda/generate-client-x509-cert.js | 15 ++++---- private-ca/lambda/generate-root-x509-cert.js | 36 ------------------- private-ca/lambda/index.js | 14 +------- 7 files changed, 16 insertions(+), 59 deletions(-) create mode 100644 .gitignore delete mode 100644 private-ca/lambda/generate-root-x509-cert.js diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..9444a47 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +lambda/node_modules/ +.talismanrc \ No newline at end of file diff --git a/private-ca/README.md b/private-ca/README.md index bb7c94a..9dba9f4 100644 --- a/private-ca/README.md +++ b/private-ca/README.md @@ -32,7 +32,6 @@ You can modify the variables in the script to change the payload: - `CA_ACTION` - getHostSSHCert - getClientSSHCert - - generateRootX509Cert - generateClientX509Cert - `SSL_ATTRS_VALIDITY` - The validity of the generated certificate in days - `SSL_CLIENT_PUBKEY_PEM` - Public Key of the client to be signed for generating client SSL certificate diff --git a/private-ca/deploy-resources.sh b/private-ca/deploy-resources.sh index aefcbd5..cb5123f 100755 --- a/private-ca/deploy-resources.sh +++ b/private-ca/deploy-resources.sh @@ -13,7 +13,8 @@ ssh-keygen -t rsa -b 4096 -f host_ca -C host_ca -N "" ssh-keygen -t rsa -b 4096 -f user_ca -C user_ca -N "" openssl genrsa -out key.pem 2048 -openssl rsa -in key.pem -outform PEM -pubout -out public.pem +openssl rsa -in key.pem -outform PEM -pubout -out public.PEM +openssl req -new -x509 -key key.pem -out root.crt -days 365 -subj "/C=US/ST=California/L=YourCity/O=Fundwave/OU=Fundwave/CN=FundwaveCA" HOST_CA_PRIVATE_KEY=$(cat host_ca | base64 -w 0) HOST_CA_PUBLIC_KEY=$(cat host_ca.pub | base64 -w 0) @@ -21,8 +22,9 @@ USER_CA_PRIVATE_KEY=$(cat user_ca | base64 -w 0) USER_CA_PUBLIC_KEY=$(cat user_ca.pub | base64 -w 0) ROOT_SSL_PRIVATE_KEY=$(cat key.pem | base64 -w 0) ROOT_SSL_PUBLIC_KEY=$(cat public.pem | base64 -w 0) +ROOT_SSL_CERT=$(cat root.pem | base64 -w 0) -echo "{\"host_ca\": \"${HOST_CA_PRIVATE_KEY}\", \"host_ca.pub\": \"${HOST_CA_PUBLIC_KEY}\", \"user_ca\": \"${USER_CA_PRIVATE_KEY}\",\"user_ca.pub\": \"${USER_CA_PUBLIC_KEY}\",\"root_ssl_private_key\": \"${ROOT_SSL_PRIVATE_KEY}\",\"root_ssl_public_key\": \"${ROOT_SSL_PUBLIC_KEY}\"}" | jq . > secret.json +echo "{\"host_ca\": \"${HOST_CA_PRIVATE_KEY}\", \"host_ca.pub\": \"${HOST_CA_PUBLIC_KEY}\", \"user_ca\": \"${USER_CA_PRIVATE_KEY}\",\"user_ca.pub\": \"${USER_CA_PUBLIC_KEY}\",\"root_ssl_private_key\": \"${ROOT_SSL_PRIVATE_KEY}\",\"root_ssl_public_key\": \"${ROOT_SSL_PUBLIC_KEY}\", \"rootX509cert\": \"${ROOT_SSL_CERT}\"}" | jq . > secret.json # Create Secret SECRET_ARN=$(aws secretsmanager create-secret \ diff --git a/private-ca/get-certificate.sh b/private-ca/get-certificate.sh index ac3951a..8aad8da 100755 --- a/private-ca/get-certificate.sh +++ b/private-ca/get-certificate.sh @@ -8,7 +8,6 @@ AWS_SECRETS_REGION=${3:-"ap-south-1"} ###################################################### # CA_ACTION="getHostSSHCert" # CA_ACTION="getClientSSHCert" -CA_ACTION="generateRootX509Cert" # CA_ACTION="generateClientX509Cert" # # Get client SSL certificate diff --git a/private-ca/lambda/generate-client-x509-cert.js b/private-ca/lambda/generate-client-x509-cert.js index db35391..52f59c7 100644 --- a/private-ca/lambda/generate-client-x509-cert.js +++ b/private-ca/lambda/generate-client-x509-cert.js @@ -1,7 +1,5 @@ import forge from 'node-forge'; import md from 'node-forge'; -import { generateRootX509Cert } from './generate-root-x509-cert.js'; -import { getSecret } from './secret-manager-utils.js'; export const generateClientX509Cert = async (callerIdentity, secret, event) => { @@ -19,9 +17,11 @@ export const generateClientX509Cert = async (callerIdentity, secret, event) => { let rootCertKey = 'rootX509cert'; if(!(rootCertKey in secret)) { - console.log("No root certificate found. Creating new root certificate.") - await generateRootX509Cert(secret, event); - secret = await getSecret(event.awsSecretsRegion, 'privateCA'); + console.log("No root certificate found. Aborting creation of client X.509 certificate."); + return { + statusCode: 500, + body: "No root certificate found." + }; } let rootCertPem = Buffer.from(secret['rootX509cert'], 'base64').toString('utf-8'); const rootCert = pki.certificateFromPem(rootCertPem); @@ -62,5 +62,8 @@ export const generateClientX509Cert = async (callerIdentity, secret, event) => { // Convert the signed client certificate to PEM format const clientCertPem = pki.certificateToPem(clientCert); - return clientCertPem; + return { + statusCode: 200, + body: clientCertPem + }; } \ No newline at end of file diff --git a/private-ca/lambda/generate-root-x509-cert.js b/private-ca/lambda/generate-root-x509-cert.js deleted file mode 100644 index e72aeee..0000000 --- a/private-ca/lambda/generate-root-x509-cert.js +++ /dev/null @@ -1,36 +0,0 @@ -import forge from 'node-forge'; -import { updateSecret } from './secret-manager-utils.js'; - -export const generateRootX509Cert = async (secret, event) => { - - let publicKeyPem = Buffer.from(secret.root_ssl_public_key, 'base64').toString('utf-8'); - let publicKey = forge.pki.publicKeyFromPem(publicKeyPem); - let privateKeyPem = Buffer.from(secret.root_ssl_private_key, 'base64').toString('utf-8'); - let privateKey = forge.pki.privateKeyFromPem(privateKeyPem); - - let cert = forge.pki.createCertificate(); - cert.publicKey = publicKey; - cert.serialNumber = '01'; - cert.validity.notBefore = new Date(); - cert.validity.notAfter = new Date(); - cert.validity.notAfter.setFullYear(cert.validity.notBefore.getFullYear() + 1); - let attrs = [ - { name: 'countryName', value: 'US' }, - { name: 'localityName', value: 'California' }, - { name: 'organizationName', value: 'Fundwave' }, - { name: 'organizationalUnitName', value: 'Fundwave' }, - { name: 'commonName', value: 'FundwaveCA' } - ]; - cert.setSubject(attrs); - cert.setIssuer(attrs); - cert.setExtensions([{ - name: 'basicConstraints', - cA: true - }]); - - cert.sign(privateKey, forge.md.sha256.create()); - - let rootX509Cert = forge.pki.certificateToPem(cert); - await updateSecret(event.awsSecretsRegion, 'privateCA', 'rootX509cert', Buffer.from(rootX509Cert).toString('base64')); - -} \ No newline at end of file diff --git a/private-ca/lambda/index.js b/private-ca/lambda/index.js index abb5478..4431991 100644 --- a/private-ca/lambda/index.js +++ b/private-ca/lambda/index.js @@ -1,7 +1,6 @@ import { signHostSSHCertificate } from './generate-host-ssh-cert.js'; import { signClientSSHCertificate } from './generate-client-ssh-cert.js'; import { getCallerIdentity } from './get-caller-identity.js'; -import { generateRootX509Cert } from './generate-root-x509-cert.js'; import { generateClientX509Cert } from './generate-client-x509-cert.js'; import { getSecret } from './secret-manager-utils.js'; @@ -31,19 +30,8 @@ export const handler = async (event) => { body: JSON.stringify(clientSSHCert) } return res; - case "generateRootX509Cert": - generateRootX509Cert(secret, event) - res = { - statusCode: 200, - body: "Generated root X.509 certificate successfully" - } - return res; case "generateClientX509Cert": - const clientSSLCert = await generateClientX509Cert(callerIdentity, secret, event); - res = { - statusCode: 200, - body: JSON.stringify(clientSSLCert) - } + const res = await generateClientX509Cert(callerIdentity, secret, event); return res; default: console.log("Invalid Action") From 123b832e6de36785cd3244c5821cf275daeb5ffc Mon Sep 17 00:00:00 2001 From: sudo-rgorai Date: Sat, 24 Jun 2023 19:04:57 +0530 Subject: [PATCH 04/36] Use current timestamp for auth header --- private-ca/auth-header.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/private-ca/auth-header.py b/private-ca/auth-header.py index ed538ce..2794c6e 100644 --- a/private-ca/auth-header.py +++ b/private-ca/auth-header.py @@ -1,8 +1,9 @@ +import sys +from datetime import datetime import boto3 from botocore.auth import SigV4Auth from botocore.awsrequest import AWSRequest from botocore.credentials import Credentials -import sys if __name__ == "__main__": access_key_id = sys.argv[1] @@ -14,7 +15,7 @@ request_parameters = 'Action=GetCallerIdentity&Version=2011-06-15' request_headers = { 'Host': sts_host, - 'X-Amz-Date': '20230608T112738Z' + 'X-Amz-Date': datetime.now().strftime('%Y%m%dT%H%M%SZ') } request = AWSRequest(method="POST", url="/", data=request_parameters, headers=request_headers) From ce72f2369194f3bf0f449f9eba32a13b8a6f7636 Mon Sep 17 00:00:00 2001 From: sudo-rgorai Date: Sat, 24 Jun 2023 20:23:46 +0530 Subject: [PATCH 05/36] Accept lambda event as params --- private-ca/get-certificate.sh | 39 ++++--------------- private-ca/lambda/generate-client-ssh-cert.js | 6 +-- .../lambda/generate-client-x509-cert.js | 5 +-- private-ca/lambda/generate-host-ssh-cert.js | 6 +-- private-ca/lambda/index.js | 4 +- 5 files changed, 18 insertions(+), 42 deletions(-) diff --git a/private-ca/get-certificate.sh b/private-ca/get-certificate.sh index 8aad8da..c5cda01 100755 --- a/private-ca/get-certificate.sh +++ b/private-ca/get-certificate.sh @@ -1,27 +1,11 @@ #!/bin/bash -CA_LAMBDA_FUNCTION_NAME=${1:-"privateCA"} -AWS_STS_REGION=${2:-"ap-south-1"} -AWS_SECRETS_REGION=${3:-"ap-south-1"} - -# Edit values here -###################################################### -# CA_ACTION="getHostSSHCert" -# CA_ACTION="getClientSSHCert" -# CA_ACTION="generateClientX509Cert" - -# # Get client SSL certificate -# SSL_ATTRS_VALIDITY="" -# SSL_CLIENT_PUBKEY_PEM="" - -# # Get host SSH certificate -# SSH_ATTRS_VALIDITY="" -# SSH_HOST_RSA_PUBKEY="" - -# # Get client SSH certificate -# SSH_ATTRS_VALIDITY="" -# SSH_CLIENT_RSA_PUBKEY="" -###################################################### +CA_ACTION=${1} +CERT_PUBKEY=${2} +CERT_VALIDITY=${3:-"1"} +AWS_STS_REGION=${4:-"ap-south-1"} +AWS_SECRETS_REGION=${5:-"ap-south-1"} +CA_LAMBDA_FUNCTION_NAME=${6:-"privateCA"} # Temporary Credentials TEMP_CREDS=$(aws sts get-session-token) @@ -43,15 +27,8 @@ echo "{\"auth\": { \"authorizationHeader\": \"${auth_header}\", \"sessionToken\": \"${SESSION_TOKEN}\" }, - \"sslAttrs\": { - \"validityPeriod\": \"${SSL_ATTRS_VALIDITY}\", - \"clientPublicKeyPem\": \"${SSL_CLIENT_PUBKEY_PEM}\" - }, - \"sshAttrs\": { - \"validity\": \"${SSH_ATTRS_VALIDITY}\", - \"sshHostRSAKey\": \"${SSH_HOST_RSA_PUBKEY}\", - \"sshClientRSAKey\": \"${SSH_CLIENT_RSA_PUBKEY}\" - }, + \"certValidity\": \"${CERT_VALIDITY}\", + \"certPubkey\": \"${CERT_PUBKEY}\", \"action\": \"${CA_ACTION}\", \"awsSTSRegion\": \"${AWS_STS_REGION}\", \"awsSecretsRegion\": \"${AWS_SECRETS_REGION}\" diff --git a/private-ca/lambda/generate-client-ssh-cert.js b/private-ca/lambda/generate-client-ssh-cert.js index 20eb9ff..f6e523a 100644 --- a/private-ca/lambda/generate-client-ssh-cert.js +++ b/private-ca/lambda/generate-client-ssh-cert.js @@ -4,7 +4,7 @@ import util from 'util'; const exec = util.promisify(child_process.exec); -export const signClientSSHCertificate = async (callerIdentity, secret, sshAttrs) => { +export const signClientSSHCertificate = async (callerIdentity, secret, certValidity, certPubkey) => { const arn = callerIdentity.GetCallerIdentityResponse.GetCallerIdentityResult.Arn; const roleName = arn.match(/\/([^/]+)$/)?.[1]; @@ -15,13 +15,13 @@ export const signClientSSHCertificate = async (callerIdentity, secret, sshAttrs) const certificatePath = "/tmp/" + publicKeyName + "-cert.pub"; const user_ca = Buffer.from(secret.user_ca, 'base64').toString('utf-8'); fs.writeFileSync(caKeyPath, user_ca); - fs.writeFileSync(publicKeyPath, sshAttrs['sshClientRSAKey']); + fs.writeFileSync(publicKeyPath, certValidity); let { stdout, stderr } = await exec(`chmod 600 ${caKeyPath}`); console.log('stdout:', stdout); console.log('stderr:', stderr); - ({ stdout, stderr } = await exec(`ssh-keygen -s ${caKeyPath} -I host_${roleName} -n ${roleName} -V +${sshAttrs['validity']} ${publicKeyPath}`)); + ({ stdout, stderr } = await exec(`ssh-keygen -s ${caKeyPath} -I host_${roleName} -n ${roleName} -V +${certPubkey}d ${publicKeyPath}`)); console.log('stdout:', stdout); console.log('stderr:', stderr); diff --git a/private-ca/lambda/generate-client-x509-cert.js b/private-ca/lambda/generate-client-x509-cert.js index 52f59c7..9631408 100644 --- a/private-ca/lambda/generate-client-x509-cert.js +++ b/private-ca/lambda/generate-client-x509-cert.js @@ -4,7 +4,6 @@ import md from 'node-forge'; export const generateClientX509Cert = async (callerIdentity, secret, event) => { const pki = forge.pki; - const sslAttrs = event.sslAttrs; const arn = callerIdentity.GetCallerIdentityResponse.GetCallerIdentityResult.Arn; const roleName = arn.match(/\/([^/]+)$/)?.[1]; @@ -28,7 +27,7 @@ export const generateClientX509Cert = async (callerIdentity, secret, event) => { // openssl genrsa -out key.pem 2048 // openssl rsa -in key.pem -outform PEM -pubout -out public.pem - const clientPublicKey = pki.publicKeyFromPem(sslAttrs.clientPublicKeyPem); + const clientPublicKey = pki.publicKeyFromPem(event.certPubkey); // Create a client certificate signing request (CSR) const clientCertReq = pki.createCertificationRequest(); @@ -48,7 +47,7 @@ export const generateClientX509Cert = async (callerIdentity, secret, event) => { const startDate = new Date(); // Valid from the current date and time const endDate = new Date(); - endDate.setDate(startDate.getDate() + sslAttrs.validityPeriod); + endDate.setDate(startDate.getDate() + event.certValidity); clientCert.validity.notBefore = startDate; clientCert.validity.notAfter = endDate; diff --git a/private-ca/lambda/generate-host-ssh-cert.js b/private-ca/lambda/generate-host-ssh-cert.js index 1aa90fa..5e5bb37 100644 --- a/private-ca/lambda/generate-host-ssh-cert.js +++ b/private-ca/lambda/generate-host-ssh-cert.js @@ -4,7 +4,7 @@ import util from 'util'; const exec = util.promisify(child_process.exec); -export const signHostSSHCertificate = async (callerIdentity, secret, sshAttrs) => { +export const signHostSSHCertificate = async (callerIdentity, secret, certValidity, certPubkey) => { const arn = callerIdentity.GetCallerIdentityResponse.GetCallerIdentityResult.Arn; const roleName = arn.match(/\/([^/]+)$/)?.[1]; @@ -15,13 +15,13 @@ export const signHostSSHCertificate = async (callerIdentity, secret, sshAttrs) = const certificatePath = "/tmp/" + publicKeyName + "-cert.pub"; const host_ca = Buffer.from(secret.host_ca, 'base64').toString('utf-8'); fs.writeFileSync(caKeyPath, host_ca); - fs.writeFileSync(publicKeyPath, sshAttrs['sshHostRSAKey']); + fs.writeFileSync(publicKeyPath, certPubkey); let { stdout, stderr } = await exec(`chmod 600 ${caKeyPath}`); console.log('stdout:', stdout); console.log('stderr:', stderr); - ({ stdout, stderr } = await exec(`ssh-keygen -s ${caKeyPath} -I host_${roleName} -h -n ${roleName} -V +${sshAttrs['validity']} ${publicKeyPath}`)); + ({ stdout, stderr } = await exec(`ssh-keygen -s ${caKeyPath} -I host_${roleName} -h -n ${roleName} -V +${certValidity}d ${publicKeyPath}`)); console.log('stdout:', stdout); console.log('stderr:', stderr); diff --git a/private-ca/lambda/index.js b/private-ca/lambda/index.js index 4431991..6b4e9de 100644 --- a/private-ca/lambda/index.js +++ b/private-ca/lambda/index.js @@ -16,14 +16,14 @@ export const handler = async (event) => { let res = {}; switch(event.action) { case "getHostSSHCert": - const hostSSHCert = await signHostSSHCertificate(callerIdentity, secret, event.sshAttrs); + const hostSSHCert = await signHostSSHCertificate(callerIdentity, secret, event.certValidity, event.certPubkey); res = { statusCode: 200, body: JSON.stringify(hostSSHCert) } return res; case "getClientSSHCert": - const clientSSHCert = await signClientSSHCertificate(callerIdentity, secret, event.sshAttrs); + const clientSSHCert = await signClientSSHCertificate(callerIdentity, secret, event.certValidity, event.certPubkey); console.log(clientSSHCert) res = { statusCode: 200, From 571e24592d95c139fb993a7a779a819706c3223b Mon Sep 17 00:00:00 2001 From: sudo-rgorai Date: Mon, 26 Jun 2023 11:05:23 +0530 Subject: [PATCH 06/36] Use region for all aws services --- private-ca/README.md | 12 ++++++------ private-ca/{auth-header.py => aws-auth-header.py} | 0 private-ca/deploy-resources.sh | 13 +++++++++---- private-ca/get-certificate.sh | 2 +- 4 files changed, 16 insertions(+), 11 deletions(-) rename private-ca/{auth-header.py => aws-auth-header.py} (100%) diff --git a/private-ca/README.md b/private-ca/README.md index 9dba9f4..0b5022a 100644 --- a/private-ca/README.md +++ b/private-ca/README.md @@ -27,14 +27,14 @@ Certificates can be generated by running: ./get-certificate.sh ``` -You can modify the variables in the script to change the payload: +You can pass the following params to modify the payload: - `CA_ACTION` - getHostSSHCert - getClientSSHCert - generateClientX509Cert -- `SSL_ATTRS_VALIDITY` - The validity of the generated certificate in days -- `SSL_CLIENT_PUBKEY_PEM` - Public Key of the client to be signed for generating client SSL certificate -- `SSH_ATTRS_VALIDITY` - The validity of the generated certificate in weeks -- `SSH_HOST_RSA_PUBKEY` - Public Key of the host to be signed for generating host SSH certificate -- `SSH_CLIENT_RSA_PUBKEY` - Public Key of the client to be signed for generating client SSH certificate \ No newline at end of file +- `CERT_PUBKEY` - Public Key of the client to be signed for generating the certificate +- `CERT_VALIDITY` - Validity of the generated certificate in days +- `AWS_STS_REGION` - AWS region for generating sts token +- `AWS_SECRETS_REGION` - AWS region of the secret +- `CA_LAMBDA_FUNCTION_NAME` - Name of the CA lambda function \ No newline at end of file diff --git a/private-ca/auth-header.py b/private-ca/aws-auth-header.py similarity index 100% rename from private-ca/auth-header.py rename to private-ca/aws-auth-header.py diff --git a/private-ca/deploy-resources.sh b/private-ca/deploy-resources.sh index cb5123f..4971fbb 100755 --- a/private-ca/deploy-resources.sh +++ b/private-ca/deploy-resources.sh @@ -5,6 +5,7 @@ ROLE_NAME=${2:-"privateCALambdaRole"} POLICY_NAME=${3:-"AccessPrivateCASecretsPolicy"} LAYER_NAME=${4:-"openssh"} FUNCTION_NAME=${5:-"privateCA"} +AWS_REGION=${6:-"ap-south-1"} ################## Secret ################## @@ -29,7 +30,8 @@ echo "{\"host_ca\": \"${HOST_CA_PRIVATE_KEY}\", \"host_ca.pub\": \"${HOST_CA_PUB # Create Secret SECRET_ARN=$(aws secretsmanager create-secret \ --name $SECRET_NAME \ - --secret-string file://secret.json | jq ".ARN" | tr -d '"') + --secret-string file://secret.json \ + --region $AWS_REGION | jq ".ARN" | tr -d '"') ############################################ @@ -39,16 +41,17 @@ SECRET_ARN=$(aws secretsmanager create-secret \ echo "{\"Version\": \"2012-10-17\",\"Statement\": [{\"Sid\": \"AllowLambdaAssumeRole\",\"Effect\": \"Allow\",\"Principal\": {\"Service\": \"lambda.amazonaws.com\"},\"Action\": \"sts:AssumeRole\"}]}" | jq . > Trust-Policy.json ROLE_ARN=$(aws iam create-role \ - --role-name $ROLE_NAME\ + --role-name $ROLE_NAME \ + --region $AWS_REGION \ --assume-role-policy-document file://Trust-Policy.json | jq ".Role.Arn" | tr -d '"') # Create Policy for Lambda Role to Read and Update Secrets echo "{\"Version\": \"2012-10-17\",\"Statement\": [{\"Sid\": \"VisualEditor0\",\"Effect\": \"Allow\",\"Action\": [\"secretsmanager:GetSecretValue\",\"secretsmanager:UpdateSecret\"],\"Resource\": \"${SECRET_ARN}\"}]}" | jq . > Policy.json -POLICY_ARN=$(aws iam create-policy --policy-name $POLICY_NAME --policy-document file://Policy.json | jq ".Policy.Arn" | tr -d '"') +POLICY_ARN=$(aws iam create-policy --policy-name $POLICY_NAME --region $AWS_REGION --policy-document file://Policy.json | jq ".Policy.Arn" | tr -d '"') # Attach policy to role -aws iam attach-role-policy --role-name $ROLE_NAME --policy-arn $POLICY_ARN +aws iam attach-role-policy --role-name $ROLE_NAME --policy-arn $POLICY_ARN --region $AWS_REGION ############################################ @@ -61,6 +64,7 @@ sudo zip -yr ./openssh-layer.zip . > /dev/null LAYER_ARN=$(aws lambda publish-layer-version \ --layer-name $LAYER_NAME \ --zip-file fileb://openssh-layer.zip \ + --region $AWS_REGION \ --query 'LayerVersionArn' \ --output text) cd .. @@ -79,6 +83,7 @@ cd .. aws lambda create-function \ --function-name $FUNCTION_NAME \ --runtime nodejs18.x \ + --region $AWS_REGION \ --handler index.handler \ --zip-file fileb://lambda.zip \ --layers $LAYER_ARN \ diff --git a/private-ca/get-certificate.sh b/private-ca/get-certificate.sh index c5cda01..d0d02ea 100755 --- a/private-ca/get-certificate.sh +++ b/private-ca/get-certificate.sh @@ -18,7 +18,7 @@ SESSION_TOKEN=$(echo $TEMP_CREDS | jq -r ".Credentials.SessionToken") python -m venv env && source env/bin/activate pip install boto3 -output=$(python auth-header.py $ACCESS_KEY_ID $SECRET_ACCESS_KEY $SESSION_TOKEN $AWS_REGION) +output=$(python aws-auth-header.py $ACCESS_KEY_ID $SECRET_ACCESS_KEY $SESSION_TOKEN $AWS_REGION) auth_header=$(echo $output | jq -r ".Authorization") date=$(echo $output | jq -r ".Date") From abcbce91d49bccde512736227a95db7fee2a021e Mon Sep 17 00:00:00 2001 From: sudo-rgorai Date: Mon, 26 Jun 2023 11:12:43 +0530 Subject: [PATCH 07/36] Use exact pattern matching for deleting temporary files --- private-ca/deploy-resources.sh | 12 ++++++++---- private-ca/get-certificate.sh | 2 +- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/private-ca/deploy-resources.sh b/private-ca/deploy-resources.sh index 4971fbb..44557ab 100755 --- a/private-ca/deploy-resources.sh +++ b/private-ca/deploy-resources.sh @@ -33,7 +33,8 @@ SECRET_ARN=$(aws secretsmanager create-secret \ --secret-string file://secret.json \ --region $AWS_REGION | jq ".ARN" | tr -d '"') - +# Clean up +rm host_ca host_ca.pub user_ca user_ca.pub key.pem public.pem root.pem root.crt ############################################ ################### Role ################### @@ -53,6 +54,8 @@ POLICY_ARN=$(aws iam create-policy --policy-name $POLICY_NAME --region $AWS_REGI # Attach policy to role aws iam attach-role-policy --role-name $ROLE_NAME --policy-arn $POLICY_ARN --region $AWS_REGION +# Clean up +rm Trust-Policy.json Policy.json ############################################ ################### Layer ################## @@ -69,6 +72,8 @@ LAYER_ARN=$(aws lambda publish-layer-version \ --output text) cd .. +# Clean up +sudo rm -r openssh-layer/ ############################################ ################## Lambda ################## @@ -89,7 +94,6 @@ aws lambda create-function \ --layers $LAYER_ARN \ --role $ROLE_ARN -########################################### - # Clean up -sudo rm -r openssh-layer/ host_ca user_ca *.pub *.pem *.json *.zip \ No newline at end of file +rm lambda.zip +########################################### diff --git a/private-ca/get-certificate.sh b/private-ca/get-certificate.sh index d0d02ea..d42249d 100755 --- a/private-ca/get-certificate.sh +++ b/private-ca/get-certificate.sh @@ -43,4 +43,4 @@ echo ${response_body} # Clean up deactivate -sudo rm -r env *.json \ No newline at end of file +sudo rm -r env/ event.json response.json \ No newline at end of file From af7756d764caf26179b0e87c4bf9f93551b4b786 Mon Sep 17 00:00:00 2001 From: sudo-rgorai Date: Mon, 26 Jun 2023 11:31:37 +0530 Subject: [PATCH 08/36] Fix filenames in deploy-resources.sh --- private-ca/deploy-resources.sh | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/private-ca/deploy-resources.sh b/private-ca/deploy-resources.sh index 44557ab..fd90e62 100755 --- a/private-ca/deploy-resources.sh +++ b/private-ca/deploy-resources.sh @@ -1,6 +1,6 @@ #!/bin/bash -SECRET_NAME=${1:-"privateCASecret"} +SECRET_NAME=${1:-"privateCA"} ROLE_NAME=${2:-"privateCALambdaRole"} POLICY_NAME=${3:-"AccessPrivateCASecretsPolicy"} LAYER_NAME=${4:-"openssh"} @@ -14,7 +14,7 @@ ssh-keygen -t rsa -b 4096 -f host_ca -C host_ca -N "" ssh-keygen -t rsa -b 4096 -f user_ca -C user_ca -N "" openssl genrsa -out key.pem 2048 -openssl rsa -in key.pem -outform PEM -pubout -out public.PEM +openssl rsa -in key.pem -outform PEM -pubout -out public.pem openssl req -new -x509 -key key.pem -out root.crt -days 365 -subj "/C=US/ST=California/L=YourCity/O=Fundwave/OU=Fundwave/CN=FundwaveCA" HOST_CA_PRIVATE_KEY=$(cat host_ca | base64 -w 0) @@ -23,7 +23,7 @@ USER_CA_PRIVATE_KEY=$(cat user_ca | base64 -w 0) USER_CA_PUBLIC_KEY=$(cat user_ca.pub | base64 -w 0) ROOT_SSL_PRIVATE_KEY=$(cat key.pem | base64 -w 0) ROOT_SSL_PUBLIC_KEY=$(cat public.pem | base64 -w 0) -ROOT_SSL_CERT=$(cat root.pem | base64 -w 0) +ROOT_SSL_CERT=$(cat root.crt | base64 -w 0) echo "{\"host_ca\": \"${HOST_CA_PRIVATE_KEY}\", \"host_ca.pub\": \"${HOST_CA_PUBLIC_KEY}\", \"user_ca\": \"${USER_CA_PRIVATE_KEY}\",\"user_ca.pub\": \"${USER_CA_PUBLIC_KEY}\",\"root_ssl_private_key\": \"${ROOT_SSL_PRIVATE_KEY}\",\"root_ssl_public_key\": \"${ROOT_SSL_PUBLIC_KEY}\", \"rootX509cert\": \"${ROOT_SSL_CERT}\"}" | jq . > secret.json @@ -34,10 +34,11 @@ SECRET_ARN=$(aws secretsmanager create-secret \ --region $AWS_REGION | jq ".ARN" | tr -d '"') # Clean up -rm host_ca host_ca.pub user_ca user_ca.pub key.pem public.pem root.pem root.crt +rm host_ca host_ca.pub user_ca user_ca.pub key.pem public.pem root.crt ############################################ ################### Role ################### + # Create role for lambda echo "{\"Version\": \"2012-10-17\",\"Statement\": [{\"Sid\": \"AllowLambdaAssumeRole\",\"Effect\": \"Allow\",\"Principal\": {\"Service\": \"lambda.amazonaws.com\"},\"Action\": \"sts:AssumeRole\"}]}" | jq . > Trust-Policy.json From 2e1b9b7921642e95798a459a77f0bd9a80f4cdfd Mon Sep 17 00:00:00 2001 From: sudo-rgorai Date: Mon, 26 Jun 2023 12:17:37 +0530 Subject: [PATCH 09/36] Change CA action from get to generate --- private-ca/README.md | 6 +++--- private-ca/deploy-resources.sh | 2 +- private-ca/get-certificate.sh | 4 ++-- private-ca/lambda/index.js | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/private-ca/README.md b/private-ca/README.md index 0b5022a..d8bf422 100644 --- a/private-ca/README.md +++ b/private-ca/README.md @@ -1,6 +1,6 @@ # Private Certificate Authority (CA) for SSH and SSL Certificates -This project provides a private Certificate Authority (CA) implementation for generating both SSH and SSL certificates. It allows you to issue certificates for SSH hosts and users, as well as root and client SSL certificates for secure communication. +This project provides a private Certificate Authority (CA) implementation for generating both SSH and SSL certificates. It allows you to issue certificates for SSH hosts and users, as well as client SSL certificates for secure communication. ## Installation @@ -30,8 +30,8 @@ Certificates can be generated by running: You can pass the following params to modify the payload: - `CA_ACTION` - - getHostSSHCert - - getClientSSHCert + - generateHostSSHCert + - generateClientSSHCert - generateClientX509Cert - `CERT_PUBKEY` - Public Key of the client to be signed for generating the certificate - `CERT_VALIDITY` - Validity of the generated certificate in days diff --git a/private-ca/deploy-resources.sh b/private-ca/deploy-resources.sh index fd90e62..f30e119 100755 --- a/private-ca/deploy-resources.sh +++ b/private-ca/deploy-resources.sh @@ -96,5 +96,5 @@ aws lambda create-function \ --role $ROLE_ARN # Clean up -rm lambda.zip +sudo rm -r lambda/node_modules/ lambda.zip ########################################### diff --git a/private-ca/get-certificate.sh b/private-ca/get-certificate.sh index d42249d..bfc5bc2 100755 --- a/private-ca/get-certificate.sh +++ b/private-ca/get-certificate.sh @@ -18,7 +18,7 @@ SESSION_TOKEN=$(echo $TEMP_CREDS | jq -r ".Credentials.SessionToken") python -m venv env && source env/bin/activate pip install boto3 -output=$(python aws-auth-header.py $ACCESS_KEY_ID $SECRET_ACCESS_KEY $SESSION_TOKEN $AWS_REGION) +output=$(python aws-auth-header.py $ACCESS_KEY_ID $SECRET_ACCESS_KEY $SESSION_TOKEN $AWS_STS_REGION) auth_header=$(echo $output | jq -r ".Authorization") date=$(echo $output | jq -r ".Date") @@ -43,4 +43,4 @@ echo ${response_body} # Clean up deactivate -sudo rm -r env/ event.json response.json \ No newline at end of file +sudo rm -r env/ event.json \ No newline at end of file diff --git a/private-ca/lambda/index.js b/private-ca/lambda/index.js index 6b4e9de..13642dc 100644 --- a/private-ca/lambda/index.js +++ b/private-ca/lambda/index.js @@ -15,14 +15,14 @@ export const handler = async (event) => { // action let res = {}; switch(event.action) { - case "getHostSSHCert": + case "generateHostSSHCert": const hostSSHCert = await signHostSSHCertificate(callerIdentity, secret, event.certValidity, event.certPubkey); res = { statusCode: 200, body: JSON.stringify(hostSSHCert) } return res; - case "getClientSSHCert": + case "generateClientSSHCert": const clientSSHCert = await signClientSSHCertificate(callerIdentity, secret, event.certValidity, event.certPubkey); console.log(clientSSHCert) res = { From 8039e5852bf99c14b85299605a78dfdef6852289 Mon Sep 17 00:00:00 2001 From: sudo-rgorai Date: Mon, 26 Jun 2023 12:41:53 +0530 Subject: [PATCH 10/36] Delete secret.json after use --- .gitignore | 2 +- private-ca/README.md | 2 +- private-ca/deploy-resources.sh | 2 +- private-ca/{get-certificate.sh => generate-certificate.sh} | 0 4 files changed, 3 insertions(+), 3 deletions(-) rename private-ca/{get-certificate.sh => generate-certificate.sh} (100%) diff --git a/.gitignore b/.gitignore index 9444a47..3f998bd 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,2 @@ -lambda/node_modules/ +private-ca/lambda/node_modules/ .talismanrc \ No newline at end of file diff --git a/private-ca/README.md b/private-ca/README.md index d8bf422..6029ee0 100644 --- a/private-ca/README.md +++ b/private-ca/README.md @@ -24,7 +24,7 @@ This creates the following resources on AWS: Certificates can be generated by running: ```bash - ./get-certificate.sh + ./generate-certificate.sh ``` You can pass the following params to modify the payload: diff --git a/private-ca/deploy-resources.sh b/private-ca/deploy-resources.sh index f30e119..2c3174f 100755 --- a/private-ca/deploy-resources.sh +++ b/private-ca/deploy-resources.sh @@ -34,7 +34,7 @@ SECRET_ARN=$(aws secretsmanager create-secret \ --region $AWS_REGION | jq ".ARN" | tr -d '"') # Clean up -rm host_ca host_ca.pub user_ca user_ca.pub key.pem public.pem root.crt +rm host_ca host_ca.pub user_ca user_ca.pub key.pem public.pem root.crt secret.json ############################################ ################### Role ################### diff --git a/private-ca/get-certificate.sh b/private-ca/generate-certificate.sh similarity index 100% rename from private-ca/get-certificate.sh rename to private-ca/generate-certificate.sh From ba4deb7719acc792295003407d669e8ab30da8f5 Mon Sep 17 00:00:00 2001 From: Rahul Gorai Date: Mon, 26 Jun 2023 07:17:41 +0000 Subject: [PATCH 11/36] Delete package-lock.json --- private-ca/lambda/package-lock.json | 596 ---------------------------- 1 file changed, 596 deletions(-) delete mode 100644 private-ca/lambda/package-lock.json diff --git a/private-ca/lambda/package-lock.json b/private-ca/lambda/package-lock.json deleted file mode 100644 index 611abaf..0000000 --- a/private-ca/lambda/package-lock.json +++ /dev/null @@ -1,596 +0,0 @@ -{ - "name": "private-ca", - "version": "1.0.0", - "lockfileVersion": 2, - "requires": true, - "packages": { - "": { - "name": "private-ca", - "version": "1.0.0", - "license": "ISC", - "dependencies": { - "aws-sdk": "^2.1398.0", - "node-forge": "^1.3.1" - } - }, - "node_modules/available-typed-arrays": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz", - "integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/aws-sdk": { - "version": "2.1398.0", - "resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.1398.0.tgz", - "integrity": "sha512-jiHAhKPPKDHZdwsbY9FD/WfX9RfQ7+7eKvXyXr7BYf0JIShok4TWan/iLdNWCtU0BhXYl+bfG9W0pG4rwJ9Ivg==", - "dependencies": { - "buffer": "4.9.2", - "events": "1.1.1", - "ieee754": "1.1.13", - "jmespath": "0.16.0", - "querystring": "0.2.0", - "sax": "1.2.1", - "url": "0.10.3", - "util": "^0.12.4", - "uuid": "8.0.0", - "xml2js": "0.5.0" - }, - "engines": { - "node": ">= 10.0.0" - } - }, - "node_modules/base64-js": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", - "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/buffer": { - "version": "4.9.2", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.2.tgz", - "integrity": "sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg==", - "dependencies": { - "base64-js": "^1.0.2", - "ieee754": "^1.1.4", - "isarray": "^1.0.0" - } - }, - "node_modules/call-bind": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", - "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", - "dependencies": { - "function-bind": "^1.1.1", - "get-intrinsic": "^1.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/events": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz", - "integrity": "sha512-kEcvvCBByWXGnZy6JUlgAp2gBIUjfCAV6P6TgT1/aaQKcmuAEC4OZTV1I4EWQLz2gxZw76atuVyvHhTxvi0Flw==", - "engines": { - "node": ">=0.4.x" - } - }, - "node_modules/for-each": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", - "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", - "dependencies": { - "is-callable": "^1.1.3" - } - }, - "node_modules/function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" - }, - "node_modules/get-intrinsic": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.1.tgz", - "integrity": "sha512-2DcsyfABl+gVHEfCOaTrWgyt+tb6MSEGmKq+kI5HwLbIYgjgmMcV8KQ41uaKz1xxUcn9tJtgFbQUEVcEbd0FYw==", - "dependencies": { - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-proto": "^1.0.1", - "has-symbols": "^1.0.3" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/gopd": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", - "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", - "dependencies": { - "get-intrinsic": "^1.1.3" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "dependencies": { - "function-bind": "^1.1.1" - }, - "engines": { - "node": ">= 0.4.0" - } - }, - "node_modules/has-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz", - "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-symbols": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", - "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-tostringtag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", - "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", - "dependencies": { - "has-symbols": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/ieee754": { - "version": "1.1.13", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz", - "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==" - }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" - }, - "node_modules/is-arguments": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz", - "integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==", - "dependencies": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-callable": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", - "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-generator-function": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.10.tgz", - "integrity": "sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==", - "dependencies": { - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-typed-array": { - "version": "1.1.10", - "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.10.tgz", - "integrity": "sha512-PJqgEHiWZvMpaFZ3uTc8kHPM4+4ADTlDniuQL7cU/UDA0Ql7F70yGfHph3cLNe+c9toaigv+DFzTJKhc2CtO6A==", - "dependencies": { - "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.2", - "for-each": "^0.3.3", - "gopd": "^1.0.1", - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==" - }, - "node_modules/jmespath": { - "version": "0.16.0", - "resolved": "https://registry.npmjs.org/jmespath/-/jmespath-0.16.0.tgz", - "integrity": "sha512-9FzQjJ7MATs1tSpnco1K6ayiYE3figslrXA72G2HQ/n76RzvYlofyi5QM+iX4YRs/pu3yzxlVQSST23+dMDknw==", - "engines": { - "node": ">= 0.6.0" - } - }, - "node_modules/node-forge": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz", - "integrity": "sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==", - "engines": { - "node": ">= 6.13.0" - } - }, - "node_modules/punycode": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", - "integrity": "sha512-RofWgt/7fL5wP1Y7fxE7/EmTLzQVnB0ycyibJ0OOHIlJqTNzglYFxVwETOcIoJqJmpDXJ9xImDv+Fq34F/d4Dw==" - }, - "node_modules/querystring": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", - "integrity": "sha512-X/xY82scca2tau62i9mDyU9K+I+djTMUsvwf7xnUX5GLvVzgJybOJf4Y6o9Zx3oJK/LSXg5tTZBjwzqVPaPO2g==", - "deprecated": "The querystring API is considered Legacy. new code should use the URLSearchParams API instead.", - "engines": { - "node": ">=0.4.x" - } - }, - "node_modules/sax": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.1.tgz", - "integrity": "sha512-8I2a3LovHTOpm7NV5yOyO8IHqgVsfK4+UuySrXU8YXkSRX7k6hCV9b3HrkKCr3nMpgj+0bmocaJJWpvp1oc7ZA==" - }, - "node_modules/url": { - "version": "0.10.3", - "resolved": "https://registry.npmjs.org/url/-/url-0.10.3.tgz", - "integrity": "sha512-hzSUW2q06EqL1gKM/a+obYHLIO6ct2hwPuviqTTOcfFVc61UbfJ2Q32+uGL/HCPxKqrdGB5QUwIe7UqlDgwsOQ==", - "dependencies": { - "punycode": "1.3.2", - "querystring": "0.2.0" - } - }, - "node_modules/util": { - "version": "0.12.5", - "resolved": "https://registry.npmjs.org/util/-/util-0.12.5.tgz", - "integrity": "sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==", - "dependencies": { - "inherits": "^2.0.3", - "is-arguments": "^1.0.4", - "is-generator-function": "^1.0.7", - "is-typed-array": "^1.1.3", - "which-typed-array": "^1.1.2" - } - }, - "node_modules/uuid": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.0.0.tgz", - "integrity": "sha512-jOXGuXZAWdsTH7eZLtyXMqUb9EcWMGZNbL9YcGBJl4MH4nrxHmZJhEHvyLFrkxo+28uLb/NYRcStH48fnD0Vzw==", - "bin": { - "uuid": "dist/bin/uuid" - } - }, - "node_modules/which-typed-array": { - "version": "1.1.9", - "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.9.tgz", - "integrity": "sha512-w9c4xkx6mPidwp7180ckYWfMmvxpjlZuIudNtDf4N/tTAUB8VJbX25qZoAsrtGuYNnGw3pa0AXgbGKRB8/EceA==", - "dependencies": { - "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.2", - "for-each": "^0.3.3", - "gopd": "^1.0.1", - "has-tostringtag": "^1.0.0", - "is-typed-array": "^1.1.10" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/xml2js": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.5.0.tgz", - "integrity": "sha512-drPFnkQJik/O+uPKpqSgr22mpuFHqKdbS835iAQrUC73L2F5WkboIRd63ai/2Yg6I1jzifPFKH2NTK+cfglkIA==", - "dependencies": { - "sax": ">=0.6.0", - "xmlbuilder": "~11.0.0" - }, - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/xmlbuilder": { - "version": "11.0.1", - "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz", - "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==", - "engines": { - "node": ">=4.0" - } - } - }, - "dependencies": { - "available-typed-arrays": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz", - "integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==" - }, - "aws-sdk": { - "version": "2.1398.0", - "resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.1398.0.tgz", - "integrity": "sha512-jiHAhKPPKDHZdwsbY9FD/WfX9RfQ7+7eKvXyXr7BYf0JIShok4TWan/iLdNWCtU0BhXYl+bfG9W0pG4rwJ9Ivg==", - "requires": { - "buffer": "4.9.2", - "events": "1.1.1", - "ieee754": "1.1.13", - "jmespath": "0.16.0", - "querystring": "0.2.0", - "sax": "1.2.1", - "url": "0.10.3", - "util": "^0.12.4", - "uuid": "8.0.0", - "xml2js": "0.5.0" - } - }, - "base64-js": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", - "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==" - }, - "buffer": { - "version": "4.9.2", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.2.tgz", - "integrity": "sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg==", - "requires": { - "base64-js": "^1.0.2", - "ieee754": "^1.1.4", - "isarray": "^1.0.0" - } - }, - "call-bind": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", - "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", - "requires": { - "function-bind": "^1.1.1", - "get-intrinsic": "^1.0.2" - } - }, - "events": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz", - "integrity": "sha512-kEcvvCBByWXGnZy6JUlgAp2gBIUjfCAV6P6TgT1/aaQKcmuAEC4OZTV1I4EWQLz2gxZw76atuVyvHhTxvi0Flw==" - }, - "for-each": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", - "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", - "requires": { - "is-callable": "^1.1.3" - } - }, - "function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" - }, - "get-intrinsic": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.1.tgz", - "integrity": "sha512-2DcsyfABl+gVHEfCOaTrWgyt+tb6MSEGmKq+kI5HwLbIYgjgmMcV8KQ41uaKz1xxUcn9tJtgFbQUEVcEbd0FYw==", - "requires": { - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-proto": "^1.0.1", - "has-symbols": "^1.0.3" - } - }, - "gopd": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", - "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", - "requires": { - "get-intrinsic": "^1.1.3" - } - }, - "has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "requires": { - "function-bind": "^1.1.1" - } - }, - "has-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz", - "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==" - }, - "has-symbols": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", - "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==" - }, - "has-tostringtag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", - "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", - "requires": { - "has-symbols": "^1.0.2" - } - }, - "ieee754": { - "version": "1.1.13", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz", - "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==" - }, - "inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" - }, - "is-arguments": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz", - "integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==", - "requires": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" - } - }, - "is-callable": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", - "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==" - }, - "is-generator-function": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.10.tgz", - "integrity": "sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==", - "requires": { - "has-tostringtag": "^1.0.0" - } - }, - "is-typed-array": { - "version": "1.1.10", - "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.10.tgz", - "integrity": "sha512-PJqgEHiWZvMpaFZ3uTc8kHPM4+4ADTlDniuQL7cU/UDA0Ql7F70yGfHph3cLNe+c9toaigv+DFzTJKhc2CtO6A==", - "requires": { - "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.2", - "for-each": "^0.3.3", - "gopd": "^1.0.1", - "has-tostringtag": "^1.0.0" - } - }, - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==" - }, - "jmespath": { - "version": "0.16.0", - "resolved": "https://registry.npmjs.org/jmespath/-/jmespath-0.16.0.tgz", - "integrity": "sha512-9FzQjJ7MATs1tSpnco1K6ayiYE3figslrXA72G2HQ/n76RzvYlofyi5QM+iX4YRs/pu3yzxlVQSST23+dMDknw==" - }, - "node-forge": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz", - "integrity": "sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==" - }, - "punycode": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", - "integrity": "sha512-RofWgt/7fL5wP1Y7fxE7/EmTLzQVnB0ycyibJ0OOHIlJqTNzglYFxVwETOcIoJqJmpDXJ9xImDv+Fq34F/d4Dw==" - }, - "querystring": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", - "integrity": "sha512-X/xY82scca2tau62i9mDyU9K+I+djTMUsvwf7xnUX5GLvVzgJybOJf4Y6o9Zx3oJK/LSXg5tTZBjwzqVPaPO2g==" - }, - "sax": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.1.tgz", - "integrity": "sha512-8I2a3LovHTOpm7NV5yOyO8IHqgVsfK4+UuySrXU8YXkSRX7k6hCV9b3HrkKCr3nMpgj+0bmocaJJWpvp1oc7ZA==" - }, - "url": { - "version": "0.10.3", - "resolved": "https://registry.npmjs.org/url/-/url-0.10.3.tgz", - "integrity": "sha512-hzSUW2q06EqL1gKM/a+obYHLIO6ct2hwPuviqTTOcfFVc61UbfJ2Q32+uGL/HCPxKqrdGB5QUwIe7UqlDgwsOQ==", - "requires": { - "punycode": "1.3.2", - "querystring": "0.2.0" - } - }, - "util": { - "version": "0.12.5", - "resolved": "https://registry.npmjs.org/util/-/util-0.12.5.tgz", - "integrity": "sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==", - "requires": { - "inherits": "^2.0.3", - "is-arguments": "^1.0.4", - "is-generator-function": "^1.0.7", - "is-typed-array": "^1.1.3", - "which-typed-array": "^1.1.2" - } - }, - "uuid": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.0.0.tgz", - "integrity": "sha512-jOXGuXZAWdsTH7eZLtyXMqUb9EcWMGZNbL9YcGBJl4MH4nrxHmZJhEHvyLFrkxo+28uLb/NYRcStH48fnD0Vzw==" - }, - "which-typed-array": { - "version": "1.1.9", - "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.9.tgz", - "integrity": "sha512-w9c4xkx6mPidwp7180ckYWfMmvxpjlZuIudNtDf4N/tTAUB8VJbX25qZoAsrtGuYNnGw3pa0AXgbGKRB8/EceA==", - "requires": { - "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.2", - "for-each": "^0.3.3", - "gopd": "^1.0.1", - "has-tostringtag": "^1.0.0", - "is-typed-array": "^1.1.10" - } - }, - "xml2js": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.5.0.tgz", - "integrity": "sha512-drPFnkQJik/O+uPKpqSgr22mpuFHqKdbS835iAQrUC73L2F5WkboIRd63ai/2Yg6I1jzifPFKH2NTK+cfglkIA==", - "requires": { - "sax": ">=0.6.0", - "xmlbuilder": "~11.0.0" - } - }, - "xmlbuilder": { - "version": "11.0.1", - "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz", - "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==" - } - } -} From e27dff09dfbda277cf2d9aa7b674e92edbb8e4f4 Mon Sep 17 00:00:00 2001 From: sudo-rgorai Date: Mon, 26 Jun 2023 14:50:23 +0530 Subject: [PATCH 12/36] Fix ssh client cert validity --- .gitignore | 1 + private-ca/lambda/generate-client-ssh-cert.js | 4 ++-- private-ca/lambda/index.js | 20 +++++++------------ 3 files changed, 10 insertions(+), 15 deletions(-) diff --git a/.gitignore b/.gitignore index 3f998bd..a39e264 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ private-ca/lambda/node_modules/ +private-ca/lambda/package-lock.json .talismanrc \ No newline at end of file diff --git a/private-ca/lambda/generate-client-ssh-cert.js b/private-ca/lambda/generate-client-ssh-cert.js index f6e523a..56cb02c 100644 --- a/private-ca/lambda/generate-client-ssh-cert.js +++ b/private-ca/lambda/generate-client-ssh-cert.js @@ -15,13 +15,13 @@ export const signClientSSHCertificate = async (callerIdentity, secret, certValid const certificatePath = "/tmp/" + publicKeyName + "-cert.pub"; const user_ca = Buffer.from(secret.user_ca, 'base64').toString('utf-8'); fs.writeFileSync(caKeyPath, user_ca); - fs.writeFileSync(publicKeyPath, certValidity); + fs.writeFileSync(publicKeyPath, certPubkey); let { stdout, stderr } = await exec(`chmod 600 ${caKeyPath}`); console.log('stdout:', stdout); console.log('stderr:', stderr); - ({ stdout, stderr } = await exec(`ssh-keygen -s ${caKeyPath} -I host_${roleName} -n ${roleName} -V +${certPubkey}d ${publicKeyPath}`)); + ({ stdout, stderr } = await exec(`ssh-keygen -s ${caKeyPath} -I host_${roleName} -n ${roleName} -V +${certValidity}d ${publicKeyPath}`)); console.log('stdout:', stdout); console.log('stderr:', stderr); diff --git a/private-ca/lambda/index.js b/private-ca/lambda/index.js index 13642dc..ddb03dd 100644 --- a/private-ca/lambda/index.js +++ b/private-ca/lambda/index.js @@ -10,35 +10,29 @@ export const handler = async (event) => { const callerIdentity = await getCallerIdentity(event); // secret - const secret = await getSecret(event.awsSecretsRegion, 'privateCA'); + const secret = await getSecret(event.awsSecretsRegion, 'private_CA_Secret'); // action - let res = {}; switch(event.action) { case "generateHostSSHCert": const hostSSHCert = await signHostSSHCertificate(callerIdentity, secret, event.certValidity, event.certPubkey); - res = { + return { statusCode: 200, body: JSON.stringify(hostSSHCert) - } - return res; + }; case "generateClientSSHCert": const clientSSHCert = await signClientSSHCertificate(callerIdentity, secret, event.certValidity, event.certPubkey); - console.log(clientSSHCert) - res = { + return { statusCode: 200, body: JSON.stringify(clientSSHCert) - } - return res; + }; case "generateClientX509Cert": - const res = await generateClientX509Cert(callerIdentity, secret, event); - return res; + return await generateClientX509Cert(callerIdentity, secret, event); default: console.log("Invalid Action") - res = { + return { statusCode: 400, body: JSON.stringify('Invalid Action') }; - return res; } }; From c30758fb001702217b5f64f42c880b7707b017e2 Mon Sep 17 00:00:00 2001 From: sudo-rgorai Date: Mon, 26 Jun 2023 15:31:29 +0530 Subject: [PATCH 13/36] Use pubkey file instead of string for generating certificate --- private-ca/generate-certificate.sh | 12 ++++++------ private-ca/lambda/generate-client-ssh-cert.js | 1 + private-ca/lambda/generate-client-x509-cert.js | 2 +- private-ca/lambda/generate-host-ssh-cert.js | 1 + 4 files changed, 9 insertions(+), 7 deletions(-) diff --git a/private-ca/generate-certificate.sh b/private-ca/generate-certificate.sh index bfc5bc2..d7ec6cd 100755 --- a/private-ca/generate-certificate.sh +++ b/private-ca/generate-certificate.sh @@ -1,12 +1,14 @@ #!/bin/bash CA_ACTION=${1} -CERT_PUBKEY=${2} +CERT_PUBKEY_FILE=${2} CERT_VALIDITY=${3:-"1"} AWS_STS_REGION=${4:-"ap-south-1"} AWS_SECRETS_REGION=${5:-"ap-south-1"} CA_LAMBDA_FUNCTION_NAME=${6:-"privateCA"} +CERT_PUBKEY=$(cat ${CERT_PUBKEY_FILE} | base64 -w 0) + # Temporary Credentials TEMP_CREDS=$(aws sts get-session-token) @@ -36,11 +38,9 @@ echo "{\"auth\": { aws lambda invoke --function-name ${CA_LAMBDA_FUNCTION_NAME} --cli-binary-format raw-in-base64-out --payload file://event.json response.json -response_body=$(cat response.json | jq -r ".body" | tr -d '"' | sed 's/\\r\\n/ \ -/g') - -echo ${response_body} +response_body=$(cat response.json | jq -r ".body" | tr -d '"') +echo ${response_body} > certificate # Clean up deactivate -sudo rm -r env/ event.json \ No newline at end of file +sudo rm -r env/ event.json response.json \ No newline at end of file diff --git a/private-ca/lambda/generate-client-ssh-cert.js b/private-ca/lambda/generate-client-ssh-cert.js index 56cb02c..12cd9c0 100644 --- a/private-ca/lambda/generate-client-ssh-cert.js +++ b/private-ca/lambda/generate-client-ssh-cert.js @@ -14,6 +14,7 @@ export const signClientSSHCertificate = async (callerIdentity, secret, certValid const publicKeyPath = "/tmp/" + publicKeyName + ".pub"; const certificatePath = "/tmp/" + publicKeyName + "-cert.pub"; const user_ca = Buffer.from(secret.user_ca, 'base64').toString('utf-8'); + certPubkey = Buffer.from(certPubkey, 'base64').toString('utf-8'); fs.writeFileSync(caKeyPath, user_ca); fs.writeFileSync(publicKeyPath, certPubkey); diff --git a/private-ca/lambda/generate-client-x509-cert.js b/private-ca/lambda/generate-client-x509-cert.js index 9631408..374c87a 100644 --- a/private-ca/lambda/generate-client-x509-cert.js +++ b/private-ca/lambda/generate-client-x509-cert.js @@ -27,7 +27,7 @@ export const generateClientX509Cert = async (callerIdentity, secret, event) => { // openssl genrsa -out key.pem 2048 // openssl rsa -in key.pem -outform PEM -pubout -out public.pem - const clientPublicKey = pki.publicKeyFromPem(event.certPubkey); + const clientPublicKey = pki.publicKeyFromPem(Buffer.from(event.certPubkey, 'base64').toString('utf-8')); // Create a client certificate signing request (CSR) const clientCertReq = pki.createCertificationRequest(); diff --git a/private-ca/lambda/generate-host-ssh-cert.js b/private-ca/lambda/generate-host-ssh-cert.js index 5e5bb37..861027a 100644 --- a/private-ca/lambda/generate-host-ssh-cert.js +++ b/private-ca/lambda/generate-host-ssh-cert.js @@ -14,6 +14,7 @@ export const signHostSSHCertificate = async (callerIdentity, secret, certValidit const publicKeyPath = "/tmp/" + publicKeyName + ".pub"; const certificatePath = "/tmp/" + publicKeyName + "-cert.pub"; const host_ca = Buffer.from(secret.host_ca, 'base64').toString('utf-8'); + certPubkey = Buffer.from(certPubkey, 'base64').toString('utf-8'); fs.writeFileSync(caKeyPath, host_ca); fs.writeFileSync(publicKeyPath, certPubkey); From 99afb54547e4ee18535ef777ea8c454ca14f712b Mon Sep 17 00:00:00 2001 From: sudo-rgorai Date: Mon, 26 Jun 2023 15:47:41 +0530 Subject: [PATCH 14/36] Do not use response.json --- private-ca/generate-certificate.sh | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/private-ca/generate-certificate.sh b/private-ca/generate-certificate.sh index d7ec6cd..64d8cfb 100755 --- a/private-ca/generate-certificate.sh +++ b/private-ca/generate-certificate.sh @@ -24,7 +24,7 @@ output=$(python aws-auth-header.py $ACCESS_KEY_ID $SECRET_ACCESS_KEY $SESSION_TO auth_header=$(echo $output | jq -r ".Authorization") date=$(echo $output | jq -r ".Date") -echo "{\"auth\": { +EVENT_JSON=$(echo "{\"auth\": { \"amzDate\": \"${date}\", \"authorizationHeader\": \"${auth_header}\", \"sessionToken\": \"${SESSION_TOKEN}\" @@ -34,13 +34,13 @@ echo "{\"auth\": { \"action\": \"${CA_ACTION}\", \"awsSTSRegion\": \"${AWS_STS_REGION}\", \"awsSecretsRegion\": \"${AWS_SECRETS_REGION}\" - }" > event.json + }") -aws lambda invoke --function-name ${CA_LAMBDA_FUNCTION_NAME} --cli-binary-format raw-in-base64-out --payload file://event.json response.json +aws lambda invoke --function-name ${CA_LAMBDA_FUNCTION_NAME} --cli-binary-format raw-in-base64-out --payload "$EVENT_JSON" response.json response_body=$(cat response.json | jq -r ".body" | tr -d '"') echo ${response_body} > certificate # Clean up deactivate -sudo rm -r env/ event.json response.json \ No newline at end of file +sudo rm -r env/ response.json \ No newline at end of file From eb67f933b26502a46be45646c5a7a32df85892ad Mon Sep 17 00:00:00 2001 From: sudo-rgorai Date: Tue, 4 Jul 2023 11:43:47 +0530 Subject: [PATCH 15/36] Base64 encode response certificate --- private-ca/generate-certificate.sh | 2 +- private-ca/lambda/generate-client-ssh-cert.js | 2 +- private-ca/lambda/generate-client-x509-cert.js | 2 +- private-ca/lambda/index.js | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/private-ca/generate-certificate.sh b/private-ca/generate-certificate.sh index 64d8cfb..d421752 100755 --- a/private-ca/generate-certificate.sh +++ b/private-ca/generate-certificate.sh @@ -39,7 +39,7 @@ EVENT_JSON=$(echo "{\"auth\": { aws lambda invoke --function-name ${CA_LAMBDA_FUNCTION_NAME} --cli-binary-format raw-in-base64-out --payload "$EVENT_JSON" response.json response_body=$(cat response.json | jq -r ".body" | tr -d '"') -echo ${response_body} > certificate +echo ${response_body} | base64 -d > certificate # Clean up deactivate diff --git a/private-ca/lambda/generate-client-ssh-cert.js b/private-ca/lambda/generate-client-ssh-cert.js index 12cd9c0..91fbe9d 100644 --- a/private-ca/lambda/generate-client-ssh-cert.js +++ b/private-ca/lambda/generate-client-ssh-cert.js @@ -22,7 +22,7 @@ export const signClientSSHCertificate = async (callerIdentity, secret, certValid console.log('stdout:', stdout); console.log('stderr:', stderr); - ({ stdout, stderr } = await exec(`ssh-keygen -s ${caKeyPath} -I host_${roleName} -n ${roleName} -V +${certValidity}d ${publicKeyPath}`)); + ({ stdout, stderr } = await exec(`ssh-keygen -s ${caKeyPath} -I client_${roleName} -n ${roleName} -V +${certValidity}d ${publicKeyPath}`)); console.log('stdout:', stdout); console.log('stderr:', stderr); diff --git a/private-ca/lambda/generate-client-x509-cert.js b/private-ca/lambda/generate-client-x509-cert.js index 374c87a..b1a3699 100644 --- a/private-ca/lambda/generate-client-x509-cert.js +++ b/private-ca/lambda/generate-client-x509-cert.js @@ -63,6 +63,6 @@ export const generateClientX509Cert = async (callerIdentity, secret, event) => { const clientCertPem = pki.certificateToPem(clientCert); return { statusCode: 200, - body: clientCertPem + body: Buffer.from(clientCertPem).toString('base64') }; } \ No newline at end of file diff --git a/private-ca/lambda/index.js b/private-ca/lambda/index.js index ddb03dd..2aba806 100644 --- a/private-ca/lambda/index.js +++ b/private-ca/lambda/index.js @@ -18,13 +18,13 @@ export const handler = async (event) => { const hostSSHCert = await signHostSSHCertificate(callerIdentity, secret, event.certValidity, event.certPubkey); return { statusCode: 200, - body: JSON.stringify(hostSSHCert) + body: JSON.stringify(Buffer.from(hostSSHCert).toString('base64')) }; case "generateClientSSHCert": const clientSSHCert = await signClientSSHCertificate(callerIdentity, secret, event.certValidity, event.certPubkey); return { statusCode: 200, - body: JSON.stringify(clientSSHCert) + body: JSON.stringify(Buffer.from(clientSSHCert).toString('base64')) }; case "generateClientX509Cert": return await generateClientX509Cert(callerIdentity, secret, event); From 99a8b9be0cc740d46075dc20b83bd9d2047145b4 Mon Sep 17 00:00:00 2001 From: sudo-rgorai Date: Tue, 4 Jul 2023 16:18:08 +0530 Subject: [PATCH 16/36] Invoke function using lambda URL instead of AWS CLI --- private-ca/deploy-resources.sh | 18 +++++++++++++++--- private-ca/generate-certificate.sh | 28 ++++++++-------------------- private-ca/lambda/index.js | 2 ++ 3 files changed, 25 insertions(+), 23 deletions(-) diff --git a/private-ca/deploy-resources.sh b/private-ca/deploy-resources.sh index 2c3174f..d138335 100755 --- a/private-ca/deploy-resources.sh +++ b/private-ca/deploy-resources.sh @@ -2,7 +2,7 @@ SECRET_NAME=${1:-"privateCA"} ROLE_NAME=${2:-"privateCALambdaRole"} -POLICY_NAME=${3:-"AccessPrivateCASecretsPolicy"} +POLICY_NAME=${3:-"PrivateCAPolicy"} LAYER_NAME=${4:-"openssh"} FUNCTION_NAME=${5:-"privateCA"} AWS_REGION=${6:-"ap-south-1"} @@ -48,7 +48,7 @@ ROLE_ARN=$(aws iam create-role \ --assume-role-policy-document file://Trust-Policy.json | jq ".Role.Arn" | tr -d '"') # Create Policy for Lambda Role to Read and Update Secrets -echo "{\"Version\": \"2012-10-17\",\"Statement\": [{\"Sid\": \"VisualEditor0\",\"Effect\": \"Allow\",\"Action\": [\"secretsmanager:GetSecretValue\",\"secretsmanager:UpdateSecret\"],\"Resource\": \"${SECRET_ARN}\"}]}" | jq . > Policy.json +echo "{\"Version\": \"2012-10-17\",\"Statement\": [{\"Sid\": \"VisualEditor0\",\"Effect\": \"Allow\",\"Action\": [\"secretsmanager:GetSecretValue\",\"secretsmanager:UpdateSecret\"],\"Resource\": \"${SECRET_ARN}\"}, {\"Action\": [\"logs:CreateLogGroup\",\"logs:CreateLogStream\",\"logs:PutLogEvents\"],\"Effect\": \"Allow\",\"Resource\": \"arn:aws:logs:*:*:*\"}]}" | jq . > Policy.json POLICY_ARN=$(aws iam create-policy --policy-name $POLICY_NAME --region $AWS_REGION --policy-document file://Policy.json | jq ".Policy.Arn" | tr -d '"') @@ -95,6 +95,18 @@ aws lambda create-function \ --layers $LAYER_ARN \ --role $ROLE_ARN +aws lambda add-permission \ + --function-name $FUNCTION_NAME \ + --action lambda:InvokeFunctionUrl \ + --principal "*" \ + --function-url-auth-type "NONE" \ + --statement-id url + +FUNCTION_URL=$(aws lambda create-function-url-config --function-name "privateCA" --auth-type "NONE" | jq -r ".FunctionUrl") + +echo "CA deployed at URL:" +echo "${FUNCTION_URL}" + # Clean up -sudo rm -r lambda/node_modules/ lambda.zip +sudo rm -r lambda/node_modules/ lambda/package-lock.json lambda.zip ########################################### diff --git a/private-ca/generate-certificate.sh b/private-ca/generate-certificate.sh index d421752..950a430 100755 --- a/private-ca/generate-certificate.sh +++ b/private-ca/generate-certificate.sh @@ -2,10 +2,11 @@ CA_ACTION=${1} CERT_PUBKEY_FILE=${2} -CERT_VALIDITY=${3:-"1"} -AWS_STS_REGION=${4:-"ap-south-1"} -AWS_SECRETS_REGION=${5:-"ap-south-1"} -CA_LAMBDA_FUNCTION_NAME=${6:-"privateCA"} +CA_LAMBDA_URL=${3} +CERT_VALIDITY=${4:-"1"} +AWS_STS_REGION=${5:-"ap-south-1"} +AWS_SECRETS_REGION=${6:-"ap-south-1"} +CA_LAMBDA_FUNCTION_NAME=${7:-"privateCA"} CERT_PUBKEY=$(cat ${CERT_PUBKEY_FILE} | base64 -w 0) @@ -24,23 +25,10 @@ output=$(python aws-auth-header.py $ACCESS_KEY_ID $SECRET_ACCESS_KEY $SESSION_TO auth_header=$(echo $output | jq -r ".Authorization") date=$(echo $output | jq -r ".Date") -EVENT_JSON=$(echo "{\"auth\": { - \"amzDate\": \"${date}\", - \"authorizationHeader\": \"${auth_header}\", - \"sessionToken\": \"${SESSION_TOKEN}\" - }, - \"certValidity\": \"${CERT_VALIDITY}\", - \"certPubkey\": \"${CERT_PUBKEY}\", - \"action\": \"${CA_ACTION}\", - \"awsSTSRegion\": \"${AWS_STS_REGION}\", - \"awsSecretsRegion\": \"${AWS_SECRETS_REGION}\" - }") +EVENT_JSON=$(echo "{\"auth\":{\"amzDate\":\"${date}\",\"authorizationHeader\":\"${auth_header}\",\"sessionToken\":\"${SESSION_TOKEN}\"},\"certValidity\":\"${CERT_VALIDITY}\",\"certPubkey\":\"${CERT_PUBKEY}\",\"action\":\"${CA_ACTION}\",\"awsSTSRegion\":\"${AWS_STS_REGION}\",\"awsSecretsRegion\":\"${AWS_SECRETS_REGION}\"}") - -aws lambda invoke --function-name ${CA_LAMBDA_FUNCTION_NAME} --cli-binary-format raw-in-base64-out --payload "$EVENT_JSON" response.json -response_body=$(cat response.json | jq -r ".body" | tr -d '"') -echo ${response_body} | base64 -d > certificate +curl "${CA_LAMBDA_URL}" -H 'content-type: application/json' -d "$EVENT_JSON" | tr -d '"' | base64 -d > certificate # Clean up deactivate -sudo rm -r env/ response.json \ No newline at end of file +sudo rm -r env/ \ No newline at end of file diff --git a/private-ca/lambda/index.js b/private-ca/lambda/index.js index 2aba806..f4c4647 100644 --- a/private-ca/lambda/index.js +++ b/private-ca/lambda/index.js @@ -6,6 +6,8 @@ import { getSecret } from './secret-manager-utils.js'; export const handler = async (event) => { + event=JSON.parse(event.body); + // auth const callerIdentity = await getCallerIdentity(event); From b9c2c4878c8298a681ea4414a32b6e0166232324 Mon Sep 17 00:00:00 2001 From: sudo-rgorai Date: Thu, 6 Jul 2023 11:57:48 +0530 Subject: [PATCH 17/36] Add -h help tag and handle invalid action --- private-ca/aws-auth-header.py | 3 +-- private-ca/generate-certificate.sh | 31 +++++++++++++++++++++++++++++- 2 files changed, 31 insertions(+), 3 deletions(-) diff --git a/private-ca/aws-auth-header.py b/private-ca/aws-auth-header.py index 2794c6e..8e61d98 100644 --- a/private-ca/aws-auth-header.py +++ b/private-ca/aws-auth-header.py @@ -17,8 +17,7 @@ 'Host': sts_host, 'X-Amz-Date': datetime.now().strftime('%Y%m%dT%H%M%SZ') } - request = AWSRequest(method="POST", url="/", data=request_parameters, - headers=request_headers) + request = AWSRequest(method="POST", url="/", data=request_parameters, headers=request_headers) boto_creds = Credentials(access_key_id, secret_access_key,token=session_token) auth = SigV4Auth(boto_creds, "sts", aws_region) auth.add_auth(request) diff --git a/private-ca/generate-certificate.sh b/private-ca/generate-certificate.sh index 950a430..699feec 100755 --- a/private-ca/generate-certificate.sh +++ b/private-ca/generate-certificate.sh @@ -1,5 +1,20 @@ #!/bin/bash +while getopts ":h" option; do + case $option in + h) + echo "Usage: ./generate-certificate.sh [ACTION] [PUBLIC KEY FILE] [LAMBDA URL]" + echo "Possible actions:" + echo " generateHostSSHCert: Generates SSH Certificate for Host" + echo " generateClientSSHCert: Generates SSH Certificate for Client" + echo " generateClientX509Cert: Generates X.509 Certificate for Client" + exit;; + *) + echo "Error: Invalid option" + exit;; + esac +done + CA_ACTION=${1} CERT_PUBKEY_FILE=${2} CA_LAMBDA_URL=${3} @@ -8,6 +23,20 @@ AWS_STS_REGION=${5:-"ap-south-1"} AWS_SECRETS_REGION=${6:-"ap-south-1"} CA_LAMBDA_FUNCTION_NAME=${7:-"privateCA"} +if [[ + $CA_ACTION != "generateHostSSHCert" && + $CA_ACTION != "generateClientSSHCert" && + $CA_ACTION != "generateClientX509Cert" + ]] +then + echo "Invalid Action" + echo "Possible actions include:" + echo " generateHostSSHCert: Generates SSH Certificate for Host" + echo " generateClientSSHCert: Generates SSH Certificate for Client" + echo " generateClientX509Cert: Generates X.509 Certificate for Client" + exit; +fi + CERT_PUBKEY=$(cat ${CERT_PUBKEY_FILE} | base64 -w 0) # Temporary Credentials @@ -31,4 +60,4 @@ curl "${CA_LAMBDA_URL}" -H 'content-type: application/json' -d "$EVENT_JSON" | t # Clean up deactivate -sudo rm -r env/ \ No newline at end of file +rm -r env/ \ No newline at end of file From d38a12c212af0839324b7bb6ddbdb33864d2f13b Mon Sep 17 00:00:00 2001 From: sudo-rgorai Date: Thu, 6 Jul 2023 12:52:54 +0530 Subject: [PATCH 18/36] Certificate validity should be decided by CA not by the subject --- private-ca/README.md | 1 - private-ca/generate-certificate.sh | 9 ++++----- private-ca/lambda/generate-client-ssh-cert.js | 4 ++-- private-ca/lambda/generate-client-x509-cert.js | 2 +- private-ca/lambda/generate-host-ssh-cert.js | 4 ++-- private-ca/lambda/index.js | 4 ++-- 6 files changed, 11 insertions(+), 13 deletions(-) diff --git a/private-ca/README.md b/private-ca/README.md index 6029ee0..7d11cad 100644 --- a/private-ca/README.md +++ b/private-ca/README.md @@ -34,7 +34,6 @@ You can pass the following params to modify the payload: - generateClientSSHCert - generateClientX509Cert - `CERT_PUBKEY` - Public Key of the client to be signed for generating the certificate -- `CERT_VALIDITY` - Validity of the generated certificate in days - `AWS_STS_REGION` - AWS region for generating sts token - `AWS_SECRETS_REGION` - AWS region of the secret - `CA_LAMBDA_FUNCTION_NAME` - Name of the CA lambda function \ No newline at end of file diff --git a/private-ca/generate-certificate.sh b/private-ca/generate-certificate.sh index 699feec..f3ffe8d 100755 --- a/private-ca/generate-certificate.sh +++ b/private-ca/generate-certificate.sh @@ -18,10 +18,9 @@ done CA_ACTION=${1} CERT_PUBKEY_FILE=${2} CA_LAMBDA_URL=${3} -CERT_VALIDITY=${4:-"1"} -AWS_STS_REGION=${5:-"ap-south-1"} -AWS_SECRETS_REGION=${6:-"ap-south-1"} -CA_LAMBDA_FUNCTION_NAME=${7:-"privateCA"} +AWS_STS_REGION=${4:-"ap-south-1"} +AWS_SECRETS_REGION=${5:-"ap-south-1"} +CA_LAMBDA_FUNCTION_NAME=${6:-"privateCA"} if [[ $CA_ACTION != "generateHostSSHCert" && @@ -54,7 +53,7 @@ output=$(python aws-auth-header.py $ACCESS_KEY_ID $SECRET_ACCESS_KEY $SESSION_TO auth_header=$(echo $output | jq -r ".Authorization") date=$(echo $output | jq -r ".Date") -EVENT_JSON=$(echo "{\"auth\":{\"amzDate\":\"${date}\",\"authorizationHeader\":\"${auth_header}\",\"sessionToken\":\"${SESSION_TOKEN}\"},\"certValidity\":\"${CERT_VALIDITY}\",\"certPubkey\":\"${CERT_PUBKEY}\",\"action\":\"${CA_ACTION}\",\"awsSTSRegion\":\"${AWS_STS_REGION}\",\"awsSecretsRegion\":\"${AWS_SECRETS_REGION}\"}") +EVENT_JSON=$(echo "{\"auth\":{\"amzDate\":\"${date}\",\"authorizationHeader\":\"${auth_header}\",\"sessionToken\":\"${SESSION_TOKEN}\"},\"certPubkey\":\"${CERT_PUBKEY}\",\"action\":\"${CA_ACTION}\",\"awsSTSRegion\":\"${AWS_STS_REGION}\",\"awsSecretsRegion\":\"${AWS_SECRETS_REGION}\"}") curl "${CA_LAMBDA_URL}" -H 'content-type: application/json' -d "$EVENT_JSON" | tr -d '"' | base64 -d > certificate diff --git a/private-ca/lambda/generate-client-ssh-cert.js b/private-ca/lambda/generate-client-ssh-cert.js index 91fbe9d..111261f 100644 --- a/private-ca/lambda/generate-client-ssh-cert.js +++ b/private-ca/lambda/generate-client-ssh-cert.js @@ -4,7 +4,7 @@ import util from 'util'; const exec = util.promisify(child_process.exec); -export const signClientSSHCertificate = async (callerIdentity, secret, certValidity, certPubkey) => { +export const signClientSSHCertificate = async (callerIdentity, secret, certPubkey) => { const arn = callerIdentity.GetCallerIdentityResponse.GetCallerIdentityResult.Arn; const roleName = arn.match(/\/([^/]+)$/)?.[1]; @@ -22,7 +22,7 @@ export const signClientSSHCertificate = async (callerIdentity, secret, certValid console.log('stdout:', stdout); console.log('stderr:', stderr); - ({ stdout, stderr } = await exec(`ssh-keygen -s ${caKeyPath} -I client_${roleName} -n ${roleName} -V +${certValidity}d ${publicKeyPath}`)); + ({ stdout, stderr } = await exec(`ssh-keygen -s ${caKeyPath} -I client_${roleName} -n ${roleName} -V +1d ${publicKeyPath}`)); console.log('stdout:', stdout); console.log('stderr:', stderr); diff --git a/private-ca/lambda/generate-client-x509-cert.js b/private-ca/lambda/generate-client-x509-cert.js index b1a3699..d63a9c0 100644 --- a/private-ca/lambda/generate-client-x509-cert.js +++ b/private-ca/lambda/generate-client-x509-cert.js @@ -47,7 +47,7 @@ export const generateClientX509Cert = async (callerIdentity, secret, event) => { const startDate = new Date(); // Valid from the current date and time const endDate = new Date(); - endDate.setDate(startDate.getDate() + event.certValidity); + endDate.setDate(startDate.getDate() + 1); clientCert.validity.notBefore = startDate; clientCert.validity.notAfter = endDate; diff --git a/private-ca/lambda/generate-host-ssh-cert.js b/private-ca/lambda/generate-host-ssh-cert.js index 861027a..353982c 100644 --- a/private-ca/lambda/generate-host-ssh-cert.js +++ b/private-ca/lambda/generate-host-ssh-cert.js @@ -4,7 +4,7 @@ import util from 'util'; const exec = util.promisify(child_process.exec); -export const signHostSSHCertificate = async (callerIdentity, secret, certValidity, certPubkey) => { +export const signHostSSHCertificate = async (callerIdentity, secret, certPubkey) => { const arn = callerIdentity.GetCallerIdentityResponse.GetCallerIdentityResult.Arn; const roleName = arn.match(/\/([^/]+)$/)?.[1]; @@ -22,7 +22,7 @@ export const signHostSSHCertificate = async (callerIdentity, secret, certValidit console.log('stdout:', stdout); console.log('stderr:', stderr); - ({ stdout, stderr } = await exec(`ssh-keygen -s ${caKeyPath} -I host_${roleName} -h -n ${roleName} -V +${certValidity}d ${publicKeyPath}`)); + ({ stdout, stderr } = await exec(`ssh-keygen -s ${caKeyPath} -I host_${roleName} -h -n ${roleName} -V +$1d ${publicKeyPath}`)); console.log('stdout:', stdout); console.log('stderr:', stderr); diff --git a/private-ca/lambda/index.js b/private-ca/lambda/index.js index f4c4647..3226005 100644 --- a/private-ca/lambda/index.js +++ b/private-ca/lambda/index.js @@ -17,13 +17,13 @@ export const handler = async (event) => { // action switch(event.action) { case "generateHostSSHCert": - const hostSSHCert = await signHostSSHCertificate(callerIdentity, secret, event.certValidity, event.certPubkey); + const hostSSHCert = await signHostSSHCertificate(callerIdentity, secret, event.certPubkey); return { statusCode: 200, body: JSON.stringify(Buffer.from(hostSSHCert).toString('base64')) }; case "generateClientSSHCert": - const clientSSHCert = await signClientSSHCertificate(callerIdentity, secret, event.certValidity, event.certPubkey); + const clientSSHCert = await signClientSSHCertificate(callerIdentity, secret, event.certPubkey); return { statusCode: 200, body: JSON.stringify(Buffer.from(clientSSHCert).toString('base64')) From 1b7802e62cc6d065cd5ec4331a13bde73828fe74 Mon Sep 17 00:00:00 2001 From: sudo-rgorai Date: Thu, 6 Jul 2023 15:36:29 +0530 Subject: [PATCH 19/36] Check for existing certificates and their expiration --- private-ca/generate-certificate.sh | 99 +++++++++++++++++++++++++----- 1 file changed, 82 insertions(+), 17 deletions(-) diff --git a/private-ca/generate-certificate.sh b/private-ca/generate-certificate.sh index f3ffe8d..5d8994f 100755 --- a/private-ca/generate-certificate.sh +++ b/private-ca/generate-certificate.sh @@ -1,5 +1,15 @@ #!/bin/bash +CA_ACTION=${1} +CA_LAMBDA_URL=${2} +USER_SSH_DIR=${3:-"/home/$USER/.ssh/"} +SYSTEM_SSH_DIR=${4:-"/etc/ssh"} +SYSTEM_SSL_DIR=${5:-"/etc/ssl"} +AWS_STS_REGION=${6:-"ap-south-1"} +AWS_SECRETS_REGION=${7:-"ap-south-1"} +CA_LAMBDA_FUNCTION_NAME=${8:-"privateCA"} + +# Check for options while getopts ":h" option; do case $option in h) @@ -9,25 +19,74 @@ while getopts ":h" option; do echo " generateClientSSHCert: Generates SSH Certificate for Client" echo " generateClientX509Cert: Generates X.509 Certificate for Client" exit;; - *) + *) echo "Error: Invalid option" exit;; esac done -CA_ACTION=${1} -CERT_PUBKEY_FILE=${2} -CA_LAMBDA_URL=${3} -AWS_STS_REGION=${4:-"ap-south-1"} -AWS_SECRETS_REGION=${5:-"ap-south-1"} -CA_LAMBDA_FUNCTION_NAME=${6:-"privateCA"} - -if [[ - $CA_ACTION != "generateHostSSHCert" && - $CA_ACTION != "generateClientSSHCert" && - $CA_ACTION != "generateClientX509Cert" - ]] -then +# Check for CA Action +if [[ $CA_ACTION = "generateClientSSHCert" ]]; then + if [ test -f ${USER_SSH_DIR}/id_rsa-cert.pub ]; then + # Client SSH Certificate already exists + current_timestamp=$(date +%s) + certificate_expiration_timestamp=$(ssh-keygen -Lf ${USER_SSH_DIR}/id_rsa-cert.pub | awk '/Valid:/{print $NF}') + certificate_expiration_timestamp_seconds=$(date -d "$certificate_expiration_timestamp" +%s) + + if (( certificate_expiration_timestamp_seconds > current_timestamp )); then + # Certificate is valid + echo "A valid certificate was found at ${USER_SSH_DIR}/id_rsa-cert.pub." + echo "Aborting..." + exit;; + else + # Certificate expired + rm ${USER_SSH_DIR}/id_rsa-cert.pub + fi + fi + test -f ${USER_SSH_DIR}/id_rsa.pub || ssh-keygen -t rsa -b 4096 -f ${USER_SSH_DIR}/id_rsa -C host_ca -N "" + CERT_PUBKEY=$(cat ${USER_SSH_DIR}/id_rsa.pub | base64 -w 0) + +elif [[ $CA_ACTION = "generateHostSSHCert" ]]; then + if [ test -f ${SYSTEM_SSH_DIR}/id_rsa-cert.pub ]; then + # Host SSH Certificate already exists + current_timestamp=$(date +%s) + certificate_expiration_timestamp=$(ssh-keygen -Lf ${SYSTEM_SSH_DIR}/id_rsa-cert.pub | awk '/Valid:/{print $NF}') + certificate_expiration_timestamp_seconds=$(date -d "$certificate_expiration_timestamp" +%s) + + if (( certificate_expiration_timestamp_seconds > current_timestamp )); then + # Certificate is valid + echo "A valid certificate was found at ${SYSTEM_SSH_DIR}/id_rsa-cert.pub." + echo "Aborting..." + exit;; + else + # Certificate expired + rm ${SYSTEM_SSH_DIR}/id_rsa-cert.pub + fi + fi + test -f ${SYSTEM_SSH_DIR}/id_rsa.pub || ssh-keygen -t rsa -b 4096 -f ${SYSTEM_SSH_DIR}/id_rsa -C host_ca -N "" + CERT_PUBKEY=$(cat ${SYSTEM_SSH_DIR}/id_rsa.pub | base64 -w 0) + +elif [[ $CA_ACTION = "generateClientX509Cert" ]]; then + test -d ${SYSTEM_SSL_DIR}/privateCA || mkdir -p ${SYSTEM_SSL_DIR}/privateCA + if [ test -f ${SYSTEM_SSL_DIR}/privateCA/public.crt ]; then + # X.509 Certificate already exists + + if (( openssl x509 -checkend 1 -noout -in file.pem )); then + # Certificate is valid + echo "A valid certificate was found at ${SYSTEM_SSL_DIR}/privateCA/public.crt." + echo "Aborting..." + exit;; + else + # Certificate expired + rm ${SYSTEM_SSL_DIR}/privateCA/public.crt + fi + fi + if [ ! test -f ${SYSTEM_SSL_DIR}/privateCA/public.pem ]; then + openssl genrsa -out ${SYSTEM_SSL_DIR}/privateCA/key.pem 2048 + openssl rsa -in ${SYSTEM_SSL_DIR}/privateCA/key.pem -outform PEM -pubout -out ${SYSTEM_SSL_DIR}/privateCA/public.pem + fi + CERT_PUBKEY=$(cat ${SYSTEM_SSL_DIR}/privateCA/public.pem | base64 -w 0) +else echo "Invalid Action" echo "Possible actions include:" echo " generateHostSSHCert: Generates SSH Certificate for Host" @@ -36,8 +95,6 @@ then exit; fi -CERT_PUBKEY=$(cat ${CERT_PUBKEY_FILE} | base64 -w 0) - # Temporary Credentials TEMP_CREDS=$(aws sts get-session-token) @@ -55,7 +112,15 @@ date=$(echo $output | jq -r ".Date") EVENT_JSON=$(echo "{\"auth\":{\"amzDate\":\"${date}\",\"authorizationHeader\":\"${auth_header}\",\"sessionToken\":\"${SESSION_TOKEN}\"},\"certPubkey\":\"${CERT_PUBKEY}\",\"action\":\"${CA_ACTION}\",\"awsSTSRegion\":\"${AWS_STS_REGION}\",\"awsSecretsRegion\":\"${AWS_SECRETS_REGION}\"}") -curl "${CA_LAMBDA_URL}" -H 'content-type: application/json' -d "$EVENT_JSON" | tr -d '"' | base64 -d > certificate +CERTIFICATE=$(curl "${CA_LAMBDA_URL}" -H 'content-type: application/json' -d "$EVENT_JSON" | tr -d '"' | base64 -d) + +if [[ $CA_ACTION = "generateClientSSHCert" ]]; then + echo $CERTIFICATE > ${USER_SSH_DIR}/id_rsa-cert.pub +elif [[ $CA_ACTION = "generateHostSSHCert" ]]; then + echo $CERTIFICATE > ${SYSTEM_SSH_DIR}/id_rsa-cert.pub +elif [[ $CA_ACTION = "generateClientX509Cert" ]]; then + echo $CERTIFICATE > ${SYSTEM_SSL_DIR}/privateCA/public.crt +fi # Clean up deactivate From 4c70c4458e8750698b0d2491c89e4e0824480995 Mon Sep 17 00:00:00 2001 From: sudo-rgorai Date: Fri, 7 Jul 2023 13:55:24 +0530 Subject: [PATCH 20/36] Add certificate expiration buffer of 5 minutes --- private-ca/deploy-resources.sh | 2 +- private-ca/generate-certificate.sh | 63 +++++++++++---------- private-ca/lambda/generate-host-ssh-cert.js | 2 +- private-ca/lambda/index.js | 2 +- 4 files changed, 37 insertions(+), 32 deletions(-) diff --git a/private-ca/deploy-resources.sh b/private-ca/deploy-resources.sh index d138335..df7b14e 100755 --- a/private-ca/deploy-resources.sh +++ b/private-ca/deploy-resources.sh @@ -108,5 +108,5 @@ echo "CA deployed at URL:" echo "${FUNCTION_URL}" # Clean up -sudo rm -r lambda/node_modules/ lambda/package-lock.json lambda.zip +rm -r lambda/node_modules/ lambda/package-lock.json lambda.zip ########################################### diff --git a/private-ca/generate-certificate.sh b/private-ca/generate-certificate.sh index 5d8994f..45822c2 100755 --- a/private-ca/generate-certificate.sh +++ b/private-ca/generate-certificate.sh @@ -2,7 +2,7 @@ CA_ACTION=${1} CA_LAMBDA_URL=${2} -USER_SSH_DIR=${3:-"/home/$USER/.ssh/"} +USER_SSH_DIR=${3:-"/home/$USER/.ssh"} SYSTEM_SSH_DIR=${4:-"/etc/ssh"} SYSTEM_SSL_DIR=${5:-"/etc/ssl"} AWS_STS_REGION=${6:-"ap-south-1"} @@ -27,19 +27,19 @@ done # Check for CA Action if [[ $CA_ACTION = "generateClientSSHCert" ]]; then - if [ test -f ${USER_SSH_DIR}/id_rsa-cert.pub ]; then + if test -f ${USER_SSH_DIR}/id_rsa-cert.pub; then # Client SSH Certificate already exists current_timestamp=$(date +%s) certificate_expiration_timestamp=$(ssh-keygen -Lf ${USER_SSH_DIR}/id_rsa-cert.pub | awk '/Valid:/{print $NF}') certificate_expiration_timestamp_seconds=$(date -d "$certificate_expiration_timestamp" +%s) - if (( certificate_expiration_timestamp_seconds > current_timestamp )); then - # Certificate is valid + if (( certificate_expiration_timestamp_seconds > current_timestamp + 300 )); then + # Certificate is valid for atleast 300 seconds (5 minutes) echo "A valid certificate was found at ${USER_SSH_DIR}/id_rsa-cert.pub." echo "Aborting..." - exit;; + exit; else - # Certificate expired + # Certificate expired or about to expire rm ${USER_SSH_DIR}/id_rsa-cert.pub fi fi @@ -47,43 +47,43 @@ if [[ $CA_ACTION = "generateClientSSHCert" ]]; then CERT_PUBKEY=$(cat ${USER_SSH_DIR}/id_rsa.pub | base64 -w 0) elif [[ $CA_ACTION = "generateHostSSHCert" ]]; then - if [ test -f ${SYSTEM_SSH_DIR}/id_rsa-cert.pub ]; then + if test -f ${SYSTEM_SSH_DIR}/ssh_host_rsa_key-cert.pub; then # Host SSH Certificate already exists current_timestamp=$(date +%s) - certificate_expiration_timestamp=$(ssh-keygen -Lf ${SYSTEM_SSH_DIR}/id_rsa-cert.pub | awk '/Valid:/{print $NF}') + certificate_expiration_timestamp=$(ssh-keygen -Lf ${SYSTEM_SSH_DIR}/ssh_host_rsa_key-cert.pub | awk '/Valid:/{print $NF}') certificate_expiration_timestamp_seconds=$(date -d "$certificate_expiration_timestamp" +%s) - if (( certificate_expiration_timestamp_seconds > current_timestamp )); then - # Certificate is valid - echo "A valid certificate was found at ${SYSTEM_SSH_DIR}/id_rsa-cert.pub." + if (( certificate_expiration_timestamp_seconds > current_timestamp + 300)); then + # Certificate is valid for atleast 300 seconds (5 minutes) + echo "A valid certificate was found at ${SYSTEM_SSH_DIR}/ssh_host_rsa_key-cert.pub." echo "Aborting..." - exit;; + exit; else - # Certificate expired - rm ${SYSTEM_SSH_DIR}/id_rsa-cert.pub + # Certificate expired or about to expire + rm ${SYSTEM_SSH_DIR}/ssh_host_rsa_key-cert.pub fi fi - test -f ${SYSTEM_SSH_DIR}/id_rsa.pub || ssh-keygen -t rsa -b 4096 -f ${SYSTEM_SSH_DIR}/id_rsa -C host_ca -N "" - CERT_PUBKEY=$(cat ${SYSTEM_SSH_DIR}/id_rsa.pub | base64 -w 0) + test -f ${SYSTEM_SSH_DIR}/ssh_host_rsa_key.pub || ssh-keygen -t rsa -b 4096 -f ${SYSTEM_SSH_DIR}/ssh_host_rsa_key -C host_ca -N "" + CERT_PUBKEY=$(cat ${SYSTEM_SSH_DIR}/ssh_host_rsa_key.pub | base64 -w 0) elif [[ $CA_ACTION = "generateClientX509Cert" ]]; then - test -d ${SYSTEM_SSL_DIR}/privateCA || mkdir -p ${SYSTEM_SSL_DIR}/privateCA - if [ test -f ${SYSTEM_SSL_DIR}/privateCA/public.crt ]; then + test -d ${SYSTEM_SSL_DIR}/privateCA || sudo mkdir -p ${SYSTEM_SSL_DIR}/privateCA + if test -f ${SYSTEM_SSL_DIR}/privateCA/public.crt; then # X.509 Certificate already exists - if (( openssl x509 -checkend 1 -noout -in file.pem )); then - # Certificate is valid + if ( sudo openssl x509 -checkend 300 -noout -in ${SYSTEM_SSL_DIR}/privateCA/public.crt ); then + # Certificate is valid for atleast 300 seconds (5 minutes) echo "A valid certificate was found at ${SYSTEM_SSL_DIR}/privateCA/public.crt." echo "Aborting..." - exit;; + exit; else - # Certificate expired - rm ${SYSTEM_SSL_DIR}/privateCA/public.crt + # Certificate expired or about to expire + sudo rm ${SYSTEM_SSL_DIR}/privateCA/public.crt fi fi - if [ ! test -f ${SYSTEM_SSL_DIR}/privateCA/public.pem ]; then - openssl genrsa -out ${SYSTEM_SSL_DIR}/privateCA/key.pem 2048 - openssl rsa -in ${SYSTEM_SSL_DIR}/privateCA/key.pem -outform PEM -pubout -out ${SYSTEM_SSL_DIR}/privateCA/public.pem + if ! test -f ${SYSTEM_SSL_DIR}/privateCA/public.pem; then + sudo openssl genrsa -out ${SYSTEM_SSL_DIR}/privateCA/key.pem 2048 + sudo openssl rsa -in ${SYSTEM_SSL_DIR}/privateCA/key.pem -outform PEM -pubout -out ${SYSTEM_SSL_DIR}/privateCA/public.pem fi CERT_PUBKEY=$(cat ${SYSTEM_SSL_DIR}/privateCA/public.pem | base64 -w 0) else @@ -112,14 +112,19 @@ date=$(echo $output | jq -r ".Date") EVENT_JSON=$(echo "{\"auth\":{\"amzDate\":\"${date}\",\"authorizationHeader\":\"${auth_header}\",\"sessionToken\":\"${SESSION_TOKEN}\"},\"certPubkey\":\"${CERT_PUBKEY}\",\"action\":\"${CA_ACTION}\",\"awsSTSRegion\":\"${AWS_STS_REGION}\",\"awsSecretsRegion\":\"${AWS_SECRETS_REGION}\"}") -CERTIFICATE=$(curl "${CA_LAMBDA_URL}" -H 'content-type: application/json' -d "$EVENT_JSON" | tr -d '"' | base64 -d) +ENCODED_CERTIFICATE=$(curl "${CA_LAMBDA_URL}" -H 'content-type: application/json' -d "$EVENT_JSON" | tr -d '"') +CERTIFICATE=$(echo $ENCODED_CERTIFICATE | base64 -d) + if [[ $CA_ACTION = "generateClientSSHCert" ]]; then echo $CERTIFICATE > ${USER_SSH_DIR}/id_rsa-cert.pub + echo "Certificate written to ${USER_SSH_DIR}/id_rsa-cert.pub" elif [[ $CA_ACTION = "generateHostSSHCert" ]]; then - echo $CERTIFICATE > ${SYSTEM_SSH_DIR}/id_rsa-cert.pub + sudo sh -c "echo $CERTIFICATE > ${SYSTEM_SSH_DIR}/ssh_host_rsa_key-cert.pub" + echo "Certificate written to ${SYSTEM_SSH_DIR}/ssh_host_rsa_key-cert.pub" elif [[ $CA_ACTION = "generateClientX509Cert" ]]; then - echo $CERTIFICATE > ${SYSTEM_SSL_DIR}/privateCA/public.crt + sudo sh -c "echo '$CERTIFICATE' > ${SYSTEM_SSL_DIR}/privateCA/public.crt" + echo "Certificate written to ${SYSTEM_SSL_DIR}/privateCA/public.crt" fi # Clean up diff --git a/private-ca/lambda/generate-host-ssh-cert.js b/private-ca/lambda/generate-host-ssh-cert.js index 353982c..8d3dd10 100644 --- a/private-ca/lambda/generate-host-ssh-cert.js +++ b/private-ca/lambda/generate-host-ssh-cert.js @@ -22,7 +22,7 @@ export const signHostSSHCertificate = async (callerIdentity, secret, certPubkey) console.log('stdout:', stdout); console.log('stderr:', stderr); - ({ stdout, stderr } = await exec(`ssh-keygen -s ${caKeyPath} -I host_${roleName} -h -n ${roleName} -V +$1d ${publicKeyPath}`)); + ({ stdout, stderr } = await exec(`ssh-keygen -s ${caKeyPath} -I host_${roleName} -h -n ${roleName} -V +1d ${publicKeyPath}`)); console.log('stdout:', stdout); console.log('stderr:', stderr); diff --git a/private-ca/lambda/index.js b/private-ca/lambda/index.js index 3226005..b75910e 100644 --- a/private-ca/lambda/index.js +++ b/private-ca/lambda/index.js @@ -12,7 +12,7 @@ export const handler = async (event) => { const callerIdentity = await getCallerIdentity(event); // secret - const secret = await getSecret(event.awsSecretsRegion, 'private_CA_Secret'); + const secret = await getSecret(event.awsSecretsRegion, 'privateCA'); // action switch(event.action) { From 6efcdb35184ada940c08c6672d6922a325242fe5 Mon Sep 17 00:00:00 2001 From: sudo-rgorai Date: Tue, 11 Jul 2023 11:13:01 +0530 Subject: [PATCH 21/36] Use awscurl instead of curl to generate certs --- private-ca/deploy-resources.sh | 2 +- private-ca/generate-certificate.sh | 24 +++++++++++++++++------- 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/private-ca/deploy-resources.sh b/private-ca/deploy-resources.sh index df7b14e..ae9e085 100755 --- a/private-ca/deploy-resources.sh +++ b/private-ca/deploy-resources.sh @@ -99,7 +99,7 @@ aws lambda add-permission \ --function-name $FUNCTION_NAME \ --action lambda:InvokeFunctionUrl \ --principal "*" \ - --function-url-auth-type "NONE" \ + --function-url-auth-type "AWS_IAM" \ --statement-id url FUNCTION_URL=$(aws lambda create-function-url-config --function-name "privateCA" --auth-type "NONE" | jq -r ".FunctionUrl") diff --git a/private-ca/generate-certificate.sh b/private-ca/generate-certificate.sh index 45822c2..d4661ce 100755 --- a/private-ca/generate-certificate.sh +++ b/private-ca/generate-certificate.sh @@ -7,7 +7,8 @@ SYSTEM_SSH_DIR=${4:-"/etc/ssh"} SYSTEM_SSL_DIR=${5:-"/etc/ssl"} AWS_STS_REGION=${6:-"ap-south-1"} AWS_SECRETS_REGION=${7:-"ap-south-1"} -CA_LAMBDA_FUNCTION_NAME=${8:-"privateCA"} +AWS_LAMBDA_REGION=${8:-"ap-south-1"} +CA_LAMBDA_FUNCTION_NAME=${9:-"privateCA"} # Check for options while getopts ":h" option; do @@ -44,14 +45,14 @@ if [[ $CA_ACTION = "generateClientSSHCert" ]]; then fi fi test -f ${USER_SSH_DIR}/id_rsa.pub || ssh-keygen -t rsa -b 4096 -f ${USER_SSH_DIR}/id_rsa -C host_ca -N "" - CERT_PUBKEY=$(cat ${USER_SSH_DIR}/id_rsa.pub | base64 -w 0) + CERT_PUBKEY=$(cat ${USER_SSH_DIR}/id_rsa.pub | base64 | tr -d \\n) elif [[ $CA_ACTION = "generateHostSSHCert" ]]; then if test -f ${SYSTEM_SSH_DIR}/ssh_host_rsa_key-cert.pub; then # Host SSH Certificate already exists current_timestamp=$(date +%s) certificate_expiration_timestamp=$(ssh-keygen -Lf ${SYSTEM_SSH_DIR}/ssh_host_rsa_key-cert.pub | awk '/Valid:/{print $NF}') - certificate_expiration_timestamp_seconds=$(date -d "$certificate_expiration_timestamp" +%s) + certificate_expiration_timestamp_seconds=$(echo "$certificate_expiration_timestamp" | awk -F: '{ print ($1 * 3600) + ($2 * 60) + $3 }') if (( certificate_expiration_timestamp_seconds > current_timestamp + 300)); then # Certificate is valid for atleast 300 seconds (5 minutes) @@ -64,7 +65,7 @@ elif [[ $CA_ACTION = "generateHostSSHCert" ]]; then fi fi test -f ${SYSTEM_SSH_DIR}/ssh_host_rsa_key.pub || ssh-keygen -t rsa -b 4096 -f ${SYSTEM_SSH_DIR}/ssh_host_rsa_key -C host_ca -N "" - CERT_PUBKEY=$(cat ${SYSTEM_SSH_DIR}/ssh_host_rsa_key.pub | base64 -w 0) + CERT_PUBKEY=$(cat ${SYSTEM_SSH_DIR}/ssh_host_rsa_key.pub | base64 | tr -d \\n) elif [[ $CA_ACTION = "generateClientX509Cert" ]]; then test -d ${SYSTEM_SSL_DIR}/privateCA || sudo mkdir -p ${SYSTEM_SSL_DIR}/privateCA @@ -85,7 +86,7 @@ elif [[ $CA_ACTION = "generateClientX509Cert" ]]; then sudo openssl genrsa -out ${SYSTEM_SSL_DIR}/privateCA/key.pem 2048 sudo openssl rsa -in ${SYSTEM_SSL_DIR}/privateCA/key.pem -outform PEM -pubout -out ${SYSTEM_SSL_DIR}/privateCA/public.pem fi - CERT_PUBKEY=$(cat ${SYSTEM_SSL_DIR}/privateCA/public.pem | base64 -w 0) + CERT_PUBKEY=$(cat ${SYSTEM_SSL_DIR}/privateCA/public.pem | base64 | tr -d \\n) else echo "Invalid Action" echo "Possible actions include:" @@ -104,7 +105,7 @@ SESSION_TOKEN=$(echo $TEMP_CREDS | jq -r ".Credentials.SessionToken") # Auth Headers python -m venv env && source env/bin/activate -pip install boto3 +pip install boto3 awscurl output=$(python aws-auth-header.py $ACCESS_KEY_ID $SECRET_ACCESS_KEY $SESSION_TOKEN $AWS_STS_REGION) auth_header=$(echo $output | jq -r ".Authorization") @@ -112,7 +113,16 @@ date=$(echo $output | jq -r ".Date") EVENT_JSON=$(echo "{\"auth\":{\"amzDate\":\"${date}\",\"authorizationHeader\":\"${auth_header}\",\"sessionToken\":\"${SESSION_TOKEN}\"},\"certPubkey\":\"${CERT_PUBKEY}\",\"action\":\"${CA_ACTION}\",\"awsSTSRegion\":\"${AWS_STS_REGION}\",\"awsSecretsRegion\":\"${AWS_SECRETS_REGION}\"}") -ENCODED_CERTIFICATE=$(curl "${CA_LAMBDA_URL}" -H 'content-type: application/json' -d "$EVENT_JSON" | tr -d '"') +ENCODED_CERTIFICATE=$( + awscurl -X POST "${CA_LAMBDA_URL}" \ + -H 'Content-Type: application/json' \ + -d "${EVENT_JSON}" \ + --region "${AWS_LAMBDA_REGION}" \ + --service 'lambda' + --access_key "${ACCESS_KEY_ID}" \ + --secret_key "${SECRET_ACCESS_KEY}" \ + --security_token "${SESSION_TOKEN}" \ + ) CERTIFICATE=$(echo $ENCODED_CERTIFICATE | base64 -d) From ff72a42d04178f8fc850a8135b0e18c09d7546b5 Mon Sep 17 00:00:00 2001 From: sudo-rgorai Date: Tue, 11 Jul 2023 15:01:25 +0530 Subject: [PATCH 22/36] Do not use awscurl --- private-ca/deploy-resources.sh | 2 +- private-ca/generate-certificate.sh | 16 +++------------- 2 files changed, 4 insertions(+), 14 deletions(-) diff --git a/private-ca/deploy-resources.sh b/private-ca/deploy-resources.sh index ae9e085..df7b14e 100755 --- a/private-ca/deploy-resources.sh +++ b/private-ca/deploy-resources.sh @@ -99,7 +99,7 @@ aws lambda add-permission \ --function-name $FUNCTION_NAME \ --action lambda:InvokeFunctionUrl \ --principal "*" \ - --function-url-auth-type "AWS_IAM" \ + --function-url-auth-type "NONE" \ --statement-id url FUNCTION_URL=$(aws lambda create-function-url-config --function-name "privateCA" --auth-type "NONE" | jq -r ".FunctionUrl") diff --git a/private-ca/generate-certificate.sh b/private-ca/generate-certificate.sh index d4661ce..92c1556 100755 --- a/private-ca/generate-certificate.sh +++ b/private-ca/generate-certificate.sh @@ -7,8 +7,7 @@ SYSTEM_SSH_DIR=${4:-"/etc/ssh"} SYSTEM_SSL_DIR=${5:-"/etc/ssl"} AWS_STS_REGION=${6:-"ap-south-1"} AWS_SECRETS_REGION=${7:-"ap-south-1"} -AWS_LAMBDA_REGION=${8:-"ap-south-1"} -CA_LAMBDA_FUNCTION_NAME=${9:-"privateCA"} +CA_LAMBDA_FUNCTION_NAME=${8:-"privateCA"} # Check for options while getopts ":h" option; do @@ -105,7 +104,7 @@ SESSION_TOKEN=$(echo $TEMP_CREDS | jq -r ".Credentials.SessionToken") # Auth Headers python -m venv env && source env/bin/activate -pip install boto3 awscurl +pip install boto3 output=$(python aws-auth-header.py $ACCESS_KEY_ID $SECRET_ACCESS_KEY $SESSION_TOKEN $AWS_STS_REGION) auth_header=$(echo $output | jq -r ".Authorization") @@ -113,16 +112,7 @@ date=$(echo $output | jq -r ".Date") EVENT_JSON=$(echo "{\"auth\":{\"amzDate\":\"${date}\",\"authorizationHeader\":\"${auth_header}\",\"sessionToken\":\"${SESSION_TOKEN}\"},\"certPubkey\":\"${CERT_PUBKEY}\",\"action\":\"${CA_ACTION}\",\"awsSTSRegion\":\"${AWS_STS_REGION}\",\"awsSecretsRegion\":\"${AWS_SECRETS_REGION}\"}") -ENCODED_CERTIFICATE=$( - awscurl -X POST "${CA_LAMBDA_URL}" \ - -H 'Content-Type: application/json' \ - -d "${EVENT_JSON}" \ - --region "${AWS_LAMBDA_REGION}" \ - --service 'lambda' - --access_key "${ACCESS_KEY_ID}" \ - --secret_key "${SECRET_ACCESS_KEY}" \ - --security_token "${SESSION_TOKEN}" \ - ) +ENCODED_CERTIFICATE=$(curl "${CA_LAMBDA_URL}" -H 'content-type: application/json' -d "$EVENT_JSON" | tr -d '"') CERTIFICATE=$(echo $ENCODED_CERTIFICATE | base64 -d) From c854587a537418ed42d098c01ca17dbbfc6067f8 Mon Sep 17 00:00:00 2001 From: Mohit Garg Date: Tue, 11 Jul 2023 16:08:47 +0530 Subject: [PATCH 23/36] reorganize directories --- .gitignore | 4 +- .talismanrc | 3 + private-ca/{ => client}/aws-auth-header.py | 2 +- .../client/generate-certificate-aws-cli.sh | 69 ++++ .../generate-certificate-curl.sh} | 4 +- ...esources.sh => deploy-server-on-lambda.sh} | 4 +- .../generate-client-ssh-cert.js | 0 .../generate-client-x509-cert.js | 0 .../generate-host-ssh-cert.js | 0 .../{lambda => server}/get-caller-identity.js | 0 .../index.js => server/index_lambda.js} | 0 private-ca/server/package-lock.json | 358 ++++++++++++++++++ private-ca/{lambda => server}/package.json | 2 +- .../secret-manager-utils.js | 2 + 14 files changed, 439 insertions(+), 9 deletions(-) create mode 100644 .talismanrc rename private-ca/{ => client}/aws-auth-header.py (98%) create mode 100755 private-ca/client/generate-certificate-aws-cli.sh rename private-ca/{generate-certificate.sh => client/generate-certificate-curl.sh} (98%) rename private-ca/{deploy-resources.sh => deploy-server-on-lambda.sh} (93%) rename private-ca/{lambda => server}/generate-client-ssh-cert.js (100%) rename private-ca/{lambda => server}/generate-client-x509-cert.js (100%) rename private-ca/{lambda => server}/generate-host-ssh-cert.js (100%) rename private-ca/{lambda => server}/get-caller-identity.js (100%) rename private-ca/{lambda/index.js => server/index_lambda.js} (100%) create mode 100644 private-ca/server/package-lock.json rename private-ca/{lambda => server}/package.json (93%) rename private-ca/{lambda => server}/secret-manager-utils.js (98%) diff --git a/.gitignore b/.gitignore index a39e264..0979a6c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1 @@ -private-ca/lambda/node_modules/ -private-ca/lambda/package-lock.json -.talismanrc \ No newline at end of file +**/node_modules/** diff --git a/.talismanrc b/.talismanrc new file mode 100644 index 0000000..9631174 --- /dev/null +++ b/.talismanrc @@ -0,0 +1,3 @@ +threshold: medium +scopeconfig: + - scope: node diff --git a/private-ca/aws-auth-header.py b/private-ca/client/aws-auth-header.py similarity index 98% rename from private-ca/aws-auth-header.py rename to private-ca/client/aws-auth-header.py index 8e61d98..ad0632e 100644 --- a/private-ca/aws-auth-header.py +++ b/private-ca/client/aws-auth-header.py @@ -26,4 +26,4 @@ date = request.headers["X-Amz-Date"] response = f'{{"Authorization": "{authorization}", "Date": "{date}"}}' - print(response) + print(response) \ No newline at end of file diff --git a/private-ca/client/generate-certificate-aws-cli.sh b/private-ca/client/generate-certificate-aws-cli.sh new file mode 100755 index 0000000..2a2a72d --- /dev/null +++ b/private-ca/client/generate-certificate-aws-cli.sh @@ -0,0 +1,69 @@ +#!/bin/bash + +CA_LAMBDA_FUNCTION_NAME="privateCA" + +# Edit values here +###################################################### +# CA_ACTION="getHostSSHCert" +# CA_ACTION="getClientSSHCert" +CA_ACTION="generateRootX509Cert" +# CA_ACTION="generateClientX509Cert" + +# # Get client SSL certificate +# SSL_ATTRS_VALIDITY="" +# SSL_CLIENT_PUBKEY_PEM="" + +# # Get host SSH certificate +# SSH_ATTRS_VALIDITY="" +# SSH_HOST_RSA_PUBKEY="" + +# # Get client SSH certificate +# SSH_ATTRS_VALIDITY="" +# SSH_CLIENT_RSA_PUBKEY="" +###################################################### + +# Temporary Credentials +TEMP_CREDS=$(aws sts get-session-token) + +ACCESS_KEY_ID=$(echo $TEMP_CREDS | jq -r ".Credentials.AccessKeyId") +SECRET_ACCESS_KEY=$(echo $TEMP_CREDS | jq -r ".Credentials.SecretAccessKey") +SESSION_TOKEN=$(echo $TEMP_CREDS | jq -r ".Credentials.SessionToken") + +PYTHON_EXEC=$(which python || which python3) +LAMBDA_REGION=${AWS_REGION:-'ap-south-1'} + +# Auth Headers +$PYTHON_EXEC -m venv env && source env/bin/activate +pip install boto3 + +output=$($PYTHON_EXEC auth-header.py $ACCESS_KEY_ID $SECRET_ACCESS_KEY $SESSION_TOKEN) +auth_header=$(echo $output | jq -r ".Authorization") +date=$(echo $output | jq -r ".Date") + +echo "{\"auth\": { + \"amzDate\": \"${date}\", + \"authorizationHeader\": \"${auth_header}\", + \"sessionToken\": \"${SESSION_TOKEN}\" + }, + \"sslAttrs\": { + \"validityPeriod\": \"${SSL_ATTRS_VALIDITY}\", + \"clientPublicKeyPem\": \"${SSL_CLIENT_PUBKEY_PEM}\" + }, + \"sshAttrs\": { + \"validity\": \"${SSH_ATTRS_VALIDITY}\", + \"sshHostRSAKey\": \"${SSH_HOST_RSA_PUBKEY}\", + \"sshClientRSAKey\": \"${SSH_CLIENT_RSA_PUBKEY}\" + }, + \"action\": \"${CA_ACTION}\" + }" > event.json + + +aws lambda invoke --function-name ${CA_LAMBDA_FUNCTION_NAME} --cli-binary-format raw-in-base64-out --payload file://event.json response.json --region $LAMBDA_REGION +response_body=$(cat response.json | jq -r ".body" | tr -d '"' | sed 's/\\r\\n/ \ +/g') + +echo ${response_body} + +# Clean up +deactivate +sudo rm -r env *.json diff --git a/private-ca/generate-certificate.sh b/private-ca/client/generate-certificate-curl.sh similarity index 98% rename from private-ca/generate-certificate.sh rename to private-ca/client/generate-certificate-curl.sh index 92c1556..f961471 100755 --- a/private-ca/generate-certificate.sh +++ b/private-ca/client/generate-certificate-curl.sh @@ -7,7 +7,7 @@ SYSTEM_SSH_DIR=${4:-"/etc/ssh"} SYSTEM_SSL_DIR=${5:-"/etc/ssl"} AWS_STS_REGION=${6:-"ap-south-1"} AWS_SECRETS_REGION=${7:-"ap-south-1"} -CA_LAMBDA_FUNCTION_NAME=${8:-"privateCA"} +PYTHON_EXEC=$(which python || which python3) # Check for options while getopts ":h" option; do @@ -103,7 +103,7 @@ SECRET_ACCESS_KEY=$(echo $TEMP_CREDS | jq -r ".Credentials.SecretAccessKey") SESSION_TOKEN=$(echo $TEMP_CREDS | jq -r ".Credentials.SessionToken") # Auth Headers -python -m venv env && source env/bin/activate +$PYTHON_EXEC -m venv env && source env/bin/activate pip install boto3 output=$(python aws-auth-header.py $ACCESS_KEY_ID $SECRET_ACCESS_KEY $SESSION_TOKEN $AWS_STS_REGION) diff --git a/private-ca/deploy-resources.sh b/private-ca/deploy-server-on-lambda.sh similarity index 93% rename from private-ca/deploy-resources.sh rename to private-ca/deploy-server-on-lambda.sh index df7b14e..22ab321 100755 --- a/private-ca/deploy-resources.sh +++ b/private-ca/deploy-server-on-lambda.sh @@ -48,7 +48,7 @@ ROLE_ARN=$(aws iam create-role \ --assume-role-policy-document file://Trust-Policy.json | jq ".Role.Arn" | tr -d '"') # Create Policy for Lambda Role to Read and Update Secrets -echo "{\"Version\": \"2012-10-17\",\"Statement\": [{\"Sid\": \"VisualEditor0\",\"Effect\": \"Allow\",\"Action\": [\"secretsmanager:GetSecretValue\",\"secretsmanager:UpdateSecret\"],\"Resource\": \"${SECRET_ARN}\"}, {\"Action\": [\"logs:CreateLogGroup\",\"logs:CreateLogStream\",\"logs:PutLogEvents\"],\"Effect\": \"Allow\",\"Resource\": \"arn:aws:logs:*:*:*\"}]}" | jq . > Policy.json +echo "{\"Version\": \"2012-10-17\",\"Statement\": [{\"Sid\": \"VisualEditor0\",\"Effect\": \"Allow\",\"Action\": [\"secretsmanager:GetSecretValue\"],\"Resource\": \"${SECRET_ARN}\"}, {\"Action\": [\"logs:CreateLogGroup\",\"logs:CreateLogStream\",\"logs:PutLogEvents\"],\"Effect\": \"Allow\",\"Resource\": \"arn:aws:logs:*:*:*\"}]}" | jq . > Policy.json POLICY_ARN=$(aws iam create-policy --policy-name $POLICY_NAME --region $AWS_REGION --policy-document file://Policy.json | jq ".Policy.Arn" | tr -d '"') @@ -90,7 +90,7 @@ aws lambda create-function \ --function-name $FUNCTION_NAME \ --runtime nodejs18.x \ --region $AWS_REGION \ - --handler index.handler \ + --handler index_lambda.handler \ --zip-file fileb://lambda.zip \ --layers $LAYER_ARN \ --role $ROLE_ARN diff --git a/private-ca/lambda/generate-client-ssh-cert.js b/private-ca/server/generate-client-ssh-cert.js similarity index 100% rename from private-ca/lambda/generate-client-ssh-cert.js rename to private-ca/server/generate-client-ssh-cert.js diff --git a/private-ca/lambda/generate-client-x509-cert.js b/private-ca/server/generate-client-x509-cert.js similarity index 100% rename from private-ca/lambda/generate-client-x509-cert.js rename to private-ca/server/generate-client-x509-cert.js diff --git a/private-ca/lambda/generate-host-ssh-cert.js b/private-ca/server/generate-host-ssh-cert.js similarity index 100% rename from private-ca/lambda/generate-host-ssh-cert.js rename to private-ca/server/generate-host-ssh-cert.js diff --git a/private-ca/lambda/get-caller-identity.js b/private-ca/server/get-caller-identity.js similarity index 100% rename from private-ca/lambda/get-caller-identity.js rename to private-ca/server/get-caller-identity.js diff --git a/private-ca/lambda/index.js b/private-ca/server/index_lambda.js similarity index 100% rename from private-ca/lambda/index.js rename to private-ca/server/index_lambda.js diff --git a/private-ca/server/package-lock.json b/private-ca/server/package-lock.json new file mode 100644 index 0000000..f8efc1c --- /dev/null +++ b/private-ca/server/package-lock.json @@ -0,0 +1,358 @@ +{ + "name": "private-ca", + "version": "0.0.1", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "private-ca", + "version": "0.0.1", + "license": "ISC", + "dependencies": { + "aws-sdk": "^2.1398.0", + "node-forge": "^1.3.1" + } + }, + "node_modules/available-typed-arrays": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz", + "integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/aws-sdk": { + "version": "2.1414.0", + "resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.1414.0.tgz", + "integrity": "sha512-WhqTWiTZRUxWITvUG5VMPYGdCLNAm4zOTDIiotbErR9x+uDExk2CAGbXE8HH11+tD8PhZVXyukymSiG+7rJMMg==", + "dependencies": { + "buffer": "4.9.2", + "events": "1.1.1", + "ieee754": "1.1.13", + "jmespath": "0.16.0", + "querystring": "0.2.0", + "sax": "1.2.1", + "url": "0.10.3", + "util": "^0.12.4", + "uuid": "8.0.0", + "xml2js": "0.5.0" + }, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/buffer": { + "version": "4.9.2", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.2.tgz", + "integrity": "sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg==", + "dependencies": { + "base64-js": "^1.0.2", + "ieee754": "^1.1.4", + "isarray": "^1.0.0" + } + }, + "node_modules/call-bind": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", + "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "dependencies": { + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/events": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz", + "integrity": "sha512-kEcvvCBByWXGnZy6JUlgAp2gBIUjfCAV6P6TgT1/aaQKcmuAEC4OZTV1I4EWQLz2gxZw76atuVyvHhTxvi0Flw==", + "engines": { + "node": ">=0.4.x" + } + }, + "node_modules/for-each": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", + "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", + "dependencies": { + "is-callable": "^1.1.3" + } + }, + "node_modules/function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" + }, + "node_modules/get-intrinsic": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.1.tgz", + "integrity": "sha512-2DcsyfABl+gVHEfCOaTrWgyt+tb6MSEGmKq+kI5HwLbIYgjgmMcV8KQ41uaKz1xxUcn9tJtgFbQUEVcEbd0FYw==", + "dependencies": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-proto": "^1.0.1", + "has-symbols": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gopd": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", + "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", + "dependencies": { + "get-intrinsic": "^1.1.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dependencies": { + "function-bind": "^1.1.1" + }, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/has-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz", + "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", + "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", + "dependencies": { + "has-symbols": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/ieee754": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz", + "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==" + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "node_modules/is-arguments": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz", + "integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==", + "dependencies": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-callable": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-generator-function": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.10.tgz", + "integrity": "sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==", + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-typed-array": { + "version": "1.1.10", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.10.tgz", + "integrity": "sha512-PJqgEHiWZvMpaFZ3uTc8kHPM4+4ADTlDniuQL7cU/UDA0Ql7F70yGfHph3cLNe+c9toaigv+DFzTJKhc2CtO6A==", + "dependencies": { + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==" + }, + "node_modules/jmespath": { + "version": "0.16.0", + "resolved": "https://registry.npmjs.org/jmespath/-/jmespath-0.16.0.tgz", + "integrity": "sha512-9FzQjJ7MATs1tSpnco1K6ayiYE3figslrXA72G2HQ/n76RzvYlofyi5QM+iX4YRs/pu3yzxlVQSST23+dMDknw==", + "engines": { + "node": ">= 0.6.0" + } + }, + "node_modules/node-forge": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz", + "integrity": "sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==", + "engines": { + "node": ">= 6.13.0" + } + }, + "node_modules/punycode": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", + "integrity": "sha512-RofWgt/7fL5wP1Y7fxE7/EmTLzQVnB0ycyibJ0OOHIlJqTNzglYFxVwETOcIoJqJmpDXJ9xImDv+Fq34F/d4Dw==" + }, + "node_modules/querystring": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", + "integrity": "sha512-X/xY82scca2tau62i9mDyU9K+I+djTMUsvwf7xnUX5GLvVzgJybOJf4Y6o9Zx3oJK/LSXg5tTZBjwzqVPaPO2g==", + "deprecated": "The querystring API is considered Legacy. new code should use the URLSearchParams API instead.", + "engines": { + "node": ">=0.4.x" + } + }, + "node_modules/sax": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.1.tgz", + "integrity": "sha512-8I2a3LovHTOpm7NV5yOyO8IHqgVsfK4+UuySrXU8YXkSRX7k6hCV9b3HrkKCr3nMpgj+0bmocaJJWpvp1oc7ZA==" + }, + "node_modules/url": { + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/url/-/url-0.10.3.tgz", + "integrity": "sha512-hzSUW2q06EqL1gKM/a+obYHLIO6ct2hwPuviqTTOcfFVc61UbfJ2Q32+uGL/HCPxKqrdGB5QUwIe7UqlDgwsOQ==", + "dependencies": { + "punycode": "1.3.2", + "querystring": "0.2.0" + } + }, + "node_modules/util": { + "version": "0.12.5", + "resolved": "https://registry.npmjs.org/util/-/util-0.12.5.tgz", + "integrity": "sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==", + "dependencies": { + "inherits": "^2.0.3", + "is-arguments": "^1.0.4", + "is-generator-function": "^1.0.7", + "is-typed-array": "^1.1.3", + "which-typed-array": "^1.1.2" + } + }, + "node_modules/uuid": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.0.0.tgz", + "integrity": "sha512-jOXGuXZAWdsTH7eZLtyXMqUb9EcWMGZNbL9YcGBJl4MH4nrxHmZJhEHvyLFrkxo+28uLb/NYRcStH48fnD0Vzw==", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/which-typed-array": { + "version": "1.1.10", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.10.tgz", + "integrity": "sha512-uxoA5vLUfRPdjCuJ1h5LlYdmTLbYfums398v3WLkM+i/Wltl2/XyZpQWKbN++ck5L64SR/grOHqtXCUKmlZPNA==", + "dependencies": { + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-tostringtag": "^1.0.0", + "is-typed-array": "^1.1.10" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/xml2js": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.5.0.tgz", + "integrity": "sha512-drPFnkQJik/O+uPKpqSgr22mpuFHqKdbS835iAQrUC73L2F5WkboIRd63ai/2Yg6I1jzifPFKH2NTK+cfglkIA==", + "dependencies": { + "sax": ">=0.6.0", + "xmlbuilder": "~11.0.0" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/xmlbuilder": { + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz", + "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==", + "engines": { + "node": ">=4.0" + } + } + } +} diff --git a/private-ca/lambda/package.json b/private-ca/server/package.json similarity index 93% rename from private-ca/lambda/package.json rename to private-ca/server/package.json index ecb3f6b..fa1d9a5 100644 --- a/private-ca/lambda/package.json +++ b/private-ca/server/package.json @@ -1,6 +1,6 @@ { "name": "private-ca", - "version": "1.0.0", + "version": "0.0.1", "description": "", "main": "index.js", "type": "module", diff --git a/private-ca/lambda/secret-manager-utils.js b/private-ca/server/secret-manager-utils.js similarity index 98% rename from private-ca/lambda/secret-manager-utils.js rename to private-ca/server/secret-manager-utils.js index c645c29..1a622fd 100644 --- a/private-ca/lambda/secret-manager-utils.js +++ b/private-ca/server/secret-manager-utils.js @@ -7,6 +7,7 @@ export const getSecret = async (secretRegion, secretId) => { return secret; }; +/** export const updateSecret = async (secretRegion, secretId, key, value) => { var secretsmanager = new aws.SecretsManager({ region: secretRegion }); let secret = await getSecret(secretId); @@ -18,3 +19,4 @@ export const updateSecret = async (secretRegion, secretId, key, value) => { const updateRes = await secretsmanager.updateSecret(params).promise(); return updateRes; } +**/ From d67a78041523cd8b1886fc2863dd5264b223e896 Mon Sep 17 00:00:00 2001 From: Mohit Garg Date: Tue, 11 Jul 2023 16:36:13 +0530 Subject: [PATCH 24/36] add awsprofile, let server decide it's own secret region --- private-ca/README.md | 1 - .../client/generate-certificate-curl.sh | 10 +++-- private-ca/deploy-server-on-lambda.sh | 37 +++++++++++-------- private-ca/server/index_lambda.js | 5 ++- 4 files changed, 31 insertions(+), 22 deletions(-) diff --git a/private-ca/README.md b/private-ca/README.md index 7d11cad..6446eb5 100644 --- a/private-ca/README.md +++ b/private-ca/README.md @@ -35,5 +35,4 @@ You can pass the following params to modify the payload: - generateClientX509Cert - `CERT_PUBKEY` - Public Key of the client to be signed for generating the certificate - `AWS_STS_REGION` - AWS region for generating sts token -- `AWS_SECRETS_REGION` - AWS region of the secret - `CA_LAMBDA_FUNCTION_NAME` - Name of the CA lambda function \ No newline at end of file diff --git a/private-ca/client/generate-certificate-curl.sh b/private-ca/client/generate-certificate-curl.sh index f961471..76e9339 100755 --- a/private-ca/client/generate-certificate-curl.sh +++ b/private-ca/client/generate-certificate-curl.sh @@ -5,8 +5,10 @@ CA_LAMBDA_URL=${2} USER_SSH_DIR=${3:-"/home/$USER/.ssh"} SYSTEM_SSH_DIR=${4:-"/etc/ssh"} SYSTEM_SSL_DIR=${5:-"/etc/ssl"} -AWS_STS_REGION=${6:-"ap-south-1"} -AWS_SECRETS_REGION=${7:-"ap-south-1"} +AWS_STS_REGION=${6:-"ap-southeast-1"} +AWS_SCRTS_REGION=${7:-"ap-southeast-1"} +AWS_PROFILE=${8:-"default"} + PYTHON_EXEC=$(which python || which python3) # Check for options @@ -96,7 +98,7 @@ else fi # Temporary Credentials -TEMP_CREDS=$(aws sts get-session-token) +TEMP_CREDS=$(aws sts get-session-token --region $AWS_STS_REGION --profile $AWS_PROFILE) ACCESS_KEY_ID=$(echo $TEMP_CREDS | jq -r ".Credentials.AccessKeyId") SECRET_ACCESS_KEY=$(echo $TEMP_CREDS | jq -r ".Credentials.SecretAccessKey") @@ -110,7 +112,7 @@ output=$(python aws-auth-header.py $ACCESS_KEY_ID $SECRET_ACCESS_KEY $SESSION_TO auth_header=$(echo $output | jq -r ".Authorization") date=$(echo $output | jq -r ".Date") -EVENT_JSON=$(echo "{\"auth\":{\"amzDate\":\"${date}\",\"authorizationHeader\":\"${auth_header}\",\"sessionToken\":\"${SESSION_TOKEN}\"},\"certPubkey\":\"${CERT_PUBKEY}\",\"action\":\"${CA_ACTION}\",\"awsSTSRegion\":\"${AWS_STS_REGION}\",\"awsSecretsRegion\":\"${AWS_SECRETS_REGION}\"}") +EVENT_JSON=$(echo "{\"auth\":{\"amzDate\":\"${date}\",\"authorizationHeader\":\"${auth_header}\",\"sessionToken\":\"${SESSION_TOKEN}\"},\"certPubkey\":\"${CERT_PUBKEY}\",\"action\":\"${CA_ACTION}\",\"awsSTSRegion\":\"${AWS_STS_REGION}\"}") ENCODED_CERTIFICATE=$(curl "${CA_LAMBDA_URL}" -H 'content-type: application/json' -d "$EVENT_JSON" | tr -d '"') CERTIFICATE=$(echo $ENCODED_CERTIFICATE | base64 -d) diff --git a/private-ca/deploy-server-on-lambda.sh b/private-ca/deploy-server-on-lambda.sh index 22ab321..55962e7 100755 --- a/private-ca/deploy-server-on-lambda.sh +++ b/private-ca/deploy-server-on-lambda.sh @@ -5,8 +5,8 @@ ROLE_NAME=${2:-"privateCALambdaRole"} POLICY_NAME=${3:-"PrivateCAPolicy"} LAYER_NAME=${4:-"openssh"} FUNCTION_NAME=${5:-"privateCA"} -AWS_REGION=${6:-"ap-south-1"} - +AWS_REGION=${6:-"ap-southeast-1"} +AWS_PROFILE=${7:-"default"} ################## Secret ################## # Generate Keys @@ -17,13 +17,13 @@ openssl genrsa -out key.pem 2048 openssl rsa -in key.pem -outform PEM -pubout -out public.pem openssl req -new -x509 -key key.pem -out root.crt -days 365 -subj "/C=US/ST=California/L=YourCity/O=Fundwave/OU=Fundwave/CN=FundwaveCA" -HOST_CA_PRIVATE_KEY=$(cat host_ca | base64 -w 0) -HOST_CA_PUBLIC_KEY=$(cat host_ca.pub | base64 -w 0) -USER_CA_PRIVATE_KEY=$(cat user_ca | base64 -w 0) -USER_CA_PUBLIC_KEY=$(cat user_ca.pub | base64 -w 0) -ROOT_SSL_PRIVATE_KEY=$(cat key.pem | base64 -w 0) -ROOT_SSL_PUBLIC_KEY=$(cat public.pem | base64 -w 0) -ROOT_SSL_CERT=$(cat root.crt | base64 -w 0) +HOST_CA_PRIVATE_KEY=$(cat host_ca | base64 | tr -d \\n) +HOST_CA_PUBLIC_KEY=$(cat host_ca.pub | base64 | tr -d \\n) +USER_CA_PRIVATE_KEY=$(cat user_ca | base64 | tr -d \\n) +USER_CA_PUBLIC_KEY=$(cat user_ca.pub | base64 | tr -d \\n) +ROOT_SSL_PRIVATE_KEY=$(cat key.pem | base64 | tr -d \\n) +ROOT_SSL_PUBLIC_KEY=$(cat public.pem | base64 | tr -d \\n) +ROOT_SSL_CERT=$(cat root.crt | base64 | tr -d \\n) echo "{\"host_ca\": \"${HOST_CA_PRIVATE_KEY}\", \"host_ca.pub\": \"${HOST_CA_PUBLIC_KEY}\", \"user_ca\": \"${USER_CA_PRIVATE_KEY}\",\"user_ca.pub\": \"${USER_CA_PUBLIC_KEY}\",\"root_ssl_private_key\": \"${ROOT_SSL_PRIVATE_KEY}\",\"root_ssl_public_key\": \"${ROOT_SSL_PUBLIC_KEY}\", \"rootX509cert\": \"${ROOT_SSL_CERT}\"}" | jq . > secret.json @@ -31,7 +31,9 @@ echo "{\"host_ca\": \"${HOST_CA_PRIVATE_KEY}\", \"host_ca.pub\": \"${HOST_CA_PUB SECRET_ARN=$(aws secretsmanager create-secret \ --name $SECRET_NAME \ --secret-string file://secret.json \ - --region $AWS_REGION | jq ".ARN" | tr -d '"') + --region $AWS_REGION \ + --profile $AWS_PROFILE \ + | jq ".ARN" | tr -d '"') # Clean up rm host_ca host_ca.pub user_ca user_ca.pub key.pem public.pem root.crt secret.json @@ -45,15 +47,16 @@ echo "{\"Version\": \"2012-10-17\",\"Statement\": [{\"Sid\": \"AllowLambdaAssume ROLE_ARN=$(aws iam create-role \ --role-name $ROLE_NAME \ --region $AWS_REGION \ + --profile $AWS_PROFILE \ --assume-role-policy-document file://Trust-Policy.json | jq ".Role.Arn" | tr -d '"') # Create Policy for Lambda Role to Read and Update Secrets echo "{\"Version\": \"2012-10-17\",\"Statement\": [{\"Sid\": \"VisualEditor0\",\"Effect\": \"Allow\",\"Action\": [\"secretsmanager:GetSecretValue\"],\"Resource\": \"${SECRET_ARN}\"}, {\"Action\": [\"logs:CreateLogGroup\",\"logs:CreateLogStream\",\"logs:PutLogEvents\"],\"Effect\": \"Allow\",\"Resource\": \"arn:aws:logs:*:*:*\"}]}" | jq . > Policy.json -POLICY_ARN=$(aws iam create-policy --policy-name $POLICY_NAME --region $AWS_REGION --policy-document file://Policy.json | jq ".Policy.Arn" | tr -d '"') +POLICY_ARN=$(aws iam create-policy --policy-name $POLICY_NAME --region $AWS_REGION --profile $AWS_PROFILE --policy-document file://Policy.json | jq ".Policy.Arn" | tr -d '"') # Attach policy to role -aws iam attach-role-policy --role-name $ROLE_NAME --policy-arn $POLICY_ARN --region $AWS_REGION +aws iam attach-role-policy --role-name $ROLE_NAME --policy-arn $POLICY_ARN --region $AWS_REGION --profile $AWS_PROFILE # Clean up rm Trust-Policy.json Policy.json @@ -69,6 +72,7 @@ LAYER_ARN=$(aws lambda publish-layer-version \ --layer-name $LAYER_NAME \ --zip-file fileb://openssh-layer.zip \ --region $AWS_REGION \ + --profile $AWS_PROFILE \ --query 'LayerVersionArn' \ --output text) cd .. @@ -90,6 +94,7 @@ aws lambda create-function \ --function-name $FUNCTION_NAME \ --runtime nodejs18.x \ --region $AWS_REGION \ + --profile $AWS_PROFILE \ --handler index_lambda.handler \ --zip-file fileb://lambda.zip \ --layers $LAYER_ARN \ @@ -100,13 +105,15 @@ aws lambda add-permission \ --action lambda:InvokeFunctionUrl \ --principal "*" \ --function-url-auth-type "NONE" \ - --statement-id url + --statement-id url \ + --region $AWS_REGION \ + --profile $AWS_PROFILE -FUNCTION_URL=$(aws lambda create-function-url-config --function-name "privateCA" --auth-type "NONE" | jq -r ".FunctionUrl") +FUNCTION_URL=$(aws lambda create-function-url-config --function-name "privateCA" --auth-type "NONE" --region $AWS_REGION --profile $AWS_PROFILE | jq -r ".FunctionUrl") echo "CA deployed at URL:" echo "${FUNCTION_URL}" # Clean up rm -r lambda/node_modules/ lambda/package-lock.json lambda.zip -########################################### +########################################### \ No newline at end of file diff --git a/private-ca/server/index_lambda.js b/private-ca/server/index_lambda.js index b75910e..60b5af5 100644 --- a/private-ca/server/index_lambda.js +++ b/private-ca/server/index_lambda.js @@ -3,7 +3,8 @@ import { signClientSSHCertificate } from './generate-client-ssh-cert.js'; import { getCallerIdentity } from './get-caller-identity.js'; import { generateClientX509Cert } from './generate-client-x509-cert.js'; import { getSecret } from './secret-manager-utils.js'; - +const AWS_SCRTS_REGION = process.env.AWS_SCRTS_REGION; + export const handler = async (event) => { event=JSON.parse(event.body); @@ -12,7 +13,7 @@ export const handler = async (event) => { const callerIdentity = await getCallerIdentity(event); // secret - const secret = await getSecret(event.awsSecretsRegion, 'privateCA'); + const secret = await getSecret(AWS_SCRTS_REGION, 'privateCA'); // action switch(event.action) { From e073fbcbcdbed23fc60f6dc30fc2b7e642f4f4b8 Mon Sep 17 00:00:00 2001 From: sudo-rgorai Date: Wed, 12 Jul 2023 12:08:40 +0530 Subject: [PATCH 25/36] Add audience header --- private-ca/client/aws-auth-header.py | 3 +- private-ca/deploy-server-on-lambda.sh | 4 +- private-ca/server/get-caller-identity.js | 5 +- private-ca/server/package-lock.json | 358 ----------------------- 4 files changed, 7 insertions(+), 363 deletions(-) delete mode 100644 private-ca/server/package-lock.json diff --git a/private-ca/client/aws-auth-header.py b/private-ca/client/aws-auth-header.py index ad0632e..446aca7 100644 --- a/private-ca/client/aws-auth-header.py +++ b/private-ca/client/aws-auth-header.py @@ -15,7 +15,8 @@ request_parameters = 'Action=GetCallerIdentity&Version=2011-06-15' request_headers = { 'Host': sts_host, - 'X-Amz-Date': datetime.now().strftime('%Y%m%dT%H%M%SZ') + 'X-Amz-Date': datetime.now().strftime('%Y%m%dT%H%M%SZ'), + 'Aud': 'FundwaveCA' } request = AWSRequest(method="POST", url="/", data=request_parameters, headers=request_headers) boto_creds = Credentials(access_key_id, secret_access_key,token=session_token) diff --git a/private-ca/deploy-server-on-lambda.sh b/private-ca/deploy-server-on-lambda.sh index 55962e7..23b87dc 100755 --- a/private-ca/deploy-server-on-lambda.sh +++ b/private-ca/deploy-server-on-lambda.sh @@ -84,7 +84,7 @@ sudo rm -r openssh-layer/ ################## Lambda ################## # Create lambda function -cd lambda +cd server npm i zip -r ./lambda.zip . mv lambda.zip ../ @@ -115,5 +115,5 @@ echo "CA deployed at URL:" echo "${FUNCTION_URL}" # Clean up -rm -r lambda/node_modules/ lambda/package-lock.json lambda.zip +rm -r server/node_modules/ server/package-lock.json lambda.zip ########################################### \ No newline at end of file diff --git a/private-ca/server/get-caller-identity.js b/private-ca/server/get-caller-identity.js index 5d918ab..18aa180 100644 --- a/private-ca/server/get-caller-identity.js +++ b/private-ca/server/get-caller-identity.js @@ -14,9 +14,10 @@ export const getCallerIdentity = (event) => { 'Content-Type': 'application/x-www-form-urlencoded', 'X-Amz-Date': auth.amzDate, 'Authorization': auth.authorizationHeader, - 'X-Amz-Security-Token': auth.sessionToken + 'X-Amz-Security-Token': auth.sessionToken, + 'Aud': 'FundwaveCA' }; - + const options = { hostname: host, path: path, diff --git a/private-ca/server/package-lock.json b/private-ca/server/package-lock.json deleted file mode 100644 index f8efc1c..0000000 --- a/private-ca/server/package-lock.json +++ /dev/null @@ -1,358 +0,0 @@ -{ - "name": "private-ca", - "version": "0.0.1", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "private-ca", - "version": "0.0.1", - "license": "ISC", - "dependencies": { - "aws-sdk": "^2.1398.0", - "node-forge": "^1.3.1" - } - }, - "node_modules/available-typed-arrays": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz", - "integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/aws-sdk": { - "version": "2.1414.0", - "resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.1414.0.tgz", - "integrity": "sha512-WhqTWiTZRUxWITvUG5VMPYGdCLNAm4zOTDIiotbErR9x+uDExk2CAGbXE8HH11+tD8PhZVXyukymSiG+7rJMMg==", - "dependencies": { - "buffer": "4.9.2", - "events": "1.1.1", - "ieee754": "1.1.13", - "jmespath": "0.16.0", - "querystring": "0.2.0", - "sax": "1.2.1", - "url": "0.10.3", - "util": "^0.12.4", - "uuid": "8.0.0", - "xml2js": "0.5.0" - }, - "engines": { - "node": ">= 10.0.0" - } - }, - "node_modules/base64-js": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", - "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/buffer": { - "version": "4.9.2", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.2.tgz", - "integrity": "sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg==", - "dependencies": { - "base64-js": "^1.0.2", - "ieee754": "^1.1.4", - "isarray": "^1.0.0" - } - }, - "node_modules/call-bind": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", - "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", - "dependencies": { - "function-bind": "^1.1.1", - "get-intrinsic": "^1.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/events": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz", - "integrity": "sha512-kEcvvCBByWXGnZy6JUlgAp2gBIUjfCAV6P6TgT1/aaQKcmuAEC4OZTV1I4EWQLz2gxZw76atuVyvHhTxvi0Flw==", - "engines": { - "node": ">=0.4.x" - } - }, - "node_modules/for-each": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", - "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", - "dependencies": { - "is-callable": "^1.1.3" - } - }, - "node_modules/function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" - }, - "node_modules/get-intrinsic": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.1.tgz", - "integrity": "sha512-2DcsyfABl+gVHEfCOaTrWgyt+tb6MSEGmKq+kI5HwLbIYgjgmMcV8KQ41uaKz1xxUcn9tJtgFbQUEVcEbd0FYw==", - "dependencies": { - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-proto": "^1.0.1", - "has-symbols": "^1.0.3" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/gopd": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", - "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", - "dependencies": { - "get-intrinsic": "^1.1.3" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "dependencies": { - "function-bind": "^1.1.1" - }, - "engines": { - "node": ">= 0.4.0" - } - }, - "node_modules/has-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz", - "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-symbols": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", - "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-tostringtag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", - "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", - "dependencies": { - "has-symbols": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/ieee754": { - "version": "1.1.13", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz", - "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==" - }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" - }, - "node_modules/is-arguments": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz", - "integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==", - "dependencies": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-callable": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", - "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-generator-function": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.10.tgz", - "integrity": "sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==", - "dependencies": { - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-typed-array": { - "version": "1.1.10", - "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.10.tgz", - "integrity": "sha512-PJqgEHiWZvMpaFZ3uTc8kHPM4+4ADTlDniuQL7cU/UDA0Ql7F70yGfHph3cLNe+c9toaigv+DFzTJKhc2CtO6A==", - "dependencies": { - "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.2", - "for-each": "^0.3.3", - "gopd": "^1.0.1", - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==" - }, - "node_modules/jmespath": { - "version": "0.16.0", - "resolved": "https://registry.npmjs.org/jmespath/-/jmespath-0.16.0.tgz", - "integrity": "sha512-9FzQjJ7MATs1tSpnco1K6ayiYE3figslrXA72G2HQ/n76RzvYlofyi5QM+iX4YRs/pu3yzxlVQSST23+dMDknw==", - "engines": { - "node": ">= 0.6.0" - } - }, - "node_modules/node-forge": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz", - "integrity": "sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==", - "engines": { - "node": ">= 6.13.0" - } - }, - "node_modules/punycode": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", - "integrity": "sha512-RofWgt/7fL5wP1Y7fxE7/EmTLzQVnB0ycyibJ0OOHIlJqTNzglYFxVwETOcIoJqJmpDXJ9xImDv+Fq34F/d4Dw==" - }, - "node_modules/querystring": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", - "integrity": "sha512-X/xY82scca2tau62i9mDyU9K+I+djTMUsvwf7xnUX5GLvVzgJybOJf4Y6o9Zx3oJK/LSXg5tTZBjwzqVPaPO2g==", - "deprecated": "The querystring API is considered Legacy. new code should use the URLSearchParams API instead.", - "engines": { - "node": ">=0.4.x" - } - }, - "node_modules/sax": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.1.tgz", - "integrity": "sha512-8I2a3LovHTOpm7NV5yOyO8IHqgVsfK4+UuySrXU8YXkSRX7k6hCV9b3HrkKCr3nMpgj+0bmocaJJWpvp1oc7ZA==" - }, - "node_modules/url": { - "version": "0.10.3", - "resolved": "https://registry.npmjs.org/url/-/url-0.10.3.tgz", - "integrity": "sha512-hzSUW2q06EqL1gKM/a+obYHLIO6ct2hwPuviqTTOcfFVc61UbfJ2Q32+uGL/HCPxKqrdGB5QUwIe7UqlDgwsOQ==", - "dependencies": { - "punycode": "1.3.2", - "querystring": "0.2.0" - } - }, - "node_modules/util": { - "version": "0.12.5", - "resolved": "https://registry.npmjs.org/util/-/util-0.12.5.tgz", - "integrity": "sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==", - "dependencies": { - "inherits": "^2.0.3", - "is-arguments": "^1.0.4", - "is-generator-function": "^1.0.7", - "is-typed-array": "^1.1.3", - "which-typed-array": "^1.1.2" - } - }, - "node_modules/uuid": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.0.0.tgz", - "integrity": "sha512-jOXGuXZAWdsTH7eZLtyXMqUb9EcWMGZNbL9YcGBJl4MH4nrxHmZJhEHvyLFrkxo+28uLb/NYRcStH48fnD0Vzw==", - "bin": { - "uuid": "dist/bin/uuid" - } - }, - "node_modules/which-typed-array": { - "version": "1.1.10", - "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.10.tgz", - "integrity": "sha512-uxoA5vLUfRPdjCuJ1h5LlYdmTLbYfums398v3WLkM+i/Wltl2/XyZpQWKbN++ck5L64SR/grOHqtXCUKmlZPNA==", - "dependencies": { - "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.2", - "for-each": "^0.3.3", - "gopd": "^1.0.1", - "has-tostringtag": "^1.0.0", - "is-typed-array": "^1.1.10" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/xml2js": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.5.0.tgz", - "integrity": "sha512-drPFnkQJik/O+uPKpqSgr22mpuFHqKdbS835iAQrUC73L2F5WkboIRd63ai/2Yg6I1jzifPFKH2NTK+cfglkIA==", - "dependencies": { - "sax": ">=0.6.0", - "xmlbuilder": "~11.0.0" - }, - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/xmlbuilder": { - "version": "11.0.1", - "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz", - "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==", - "engines": { - "node": ">=4.0" - } - } - } -} From b4f21c54ebbc800d63dce65dd3d7a99ddc3acb75 Mon Sep 17 00:00:00 2001 From: sudo-rgorai Date: Wed, 12 Jul 2023 12:32:27 +0530 Subject: [PATCH 26/36] Use docker container --- private-ca/README.md | 16 +++-- private-ca/client/Docker/Dockerfile | 32 ++++++++++ private-ca/client/Docker/README.md | 63 +++++++++++++++++++ .../client/{ => Docker}/aws-auth-header.py | 0 .../{ => Docker}/generate-certificate-curl.sh | 3 +- 5 files changed, 106 insertions(+), 8 deletions(-) create mode 100644 private-ca/client/Docker/Dockerfile create mode 100644 private-ca/client/Docker/README.md rename private-ca/client/{ => Docker}/aws-auth-header.py (100%) rename private-ca/client/{ => Docker}/generate-certificate-curl.sh (98%) diff --git a/private-ca/README.md b/private-ca/README.md index 6446eb5..e11ac4c 100644 --- a/private-ca/README.md +++ b/private-ca/README.md @@ -8,13 +8,13 @@ This project provides a private Certificate Authority (CA) implementation for ge Deploy the resources by running: ```bash - ./deploy-resources.sh + ./deploy-server-on-lambda.sh ``` This creates the following resources on AWS: - Secret to store the keys for signing certificates - A role for the lambda function -- A policy to be attached to the role giving read/update access to created secret +- A policy to be attached to the role giving read access to created secret - An openSSH layer to facilitate SSH operations - The lambda function to act as a privateCA @@ -24,7 +24,8 @@ This creates the following resources on AWS: Certificates can be generated by running: ```bash - ./generate-certificate.sh + cd client/Docker + ./generate-certificate-curl.sh ``` You can pass the following params to modify the payload: @@ -33,6 +34,9 @@ You can pass the following params to modify the payload: - generateHostSSHCert - generateClientSSHCert - generateClientX509Cert -- `CERT_PUBKEY` - Public Key of the client to be signed for generating the certificate -- `AWS_STS_REGION` - AWS region for generating sts token -- `CA_LAMBDA_FUNCTION_NAME` - Name of the CA lambda function \ No newline at end of file +- `CA_LAMBDA_URL`: The URL of the AWS Lambda function hosting the Private CA. +- `USER_SSH_DIR`: The path to the directory where the user's SSH keys will be stored. Defaults to "/home/$USER/.ssh". +- `SYSTEM_SSH_DIR`: The path to the system's SSH directory. Defaults to "/etc/ssh". +- `SYSTEM_SSL_DIR`: The path to the system's SSL directory. Defaults to "/etc/ssl". +- `AWS_STS_REGION`: The AWS region for the STS (Security Token Service). Defaults to "ap-south-1". +- `AWS_PROFILE`: The AWS profile for running aws commands. Defaults to "default" \ No newline at end of file diff --git a/private-ca/client/Docker/Dockerfile b/private-ca/client/Docker/Dockerfile new file mode 100644 index 0000000..fb825d1 --- /dev/null +++ b/private-ca/client/Docker/Dockerfile @@ -0,0 +1,32 @@ +FROM ubuntu:latest + +ENV CA_ACTION="" +ENV CA_LAMBDA_URL="" +ENV USER_SSH_DIR="" +ENV SYSTEM_SSH_DIR="" +ENV SYSTEM_SSL_DIR="" +ENV AWS_STS_REGION="" +ENV AWS_PROFILE="" + +RUN apt-get update && apt-get install -y \ + python3 \ + python3-pip \ + python3-venv \ + openssl \ + openssh-client \ + jq + +RUN curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip" +RUN unzip awscliv2.zip +RUN sudo ./aws/install + +COPY . /app +WORKDIR /app + +RUN chmod +x generate-certificate-curl.sh + +ENTRYPOINT ["/bin/bash", "-c", "./generate-certificate.sh"] + +CMD [" ${CA_ACTION}" , "${CA_LAMBDA_URL}", "${USER_SSH_DIR}", "${SYSTEM_SSH_DIR}", "${SYSTEM_SSL_DIR}", "${AWS_STS_REGION}", "${AWS_PROFILE}"] + +RUN sleep infinity \ No newline at end of file diff --git a/private-ca/client/Docker/README.md b/private-ca/client/Docker/README.md new file mode 100644 index 0000000..cdc5d1f --- /dev/null +++ b/private-ca/client/Docker/README.md @@ -0,0 +1,63 @@ +# Certificate Generator Docker Container + +This Docker container runs a script that generates SSH and X.509 certificates using a Private Certificate Authority (CA) hosted on AWS Lambda. The script provides options to generate SSH certificates for hosts and clients, as well as X.509 certificates for clients. + +## Prerequisites + +- Docker: Install Docker on your system. Refer to the [Docker documentation](https://docs.docker.com/get-docker/) for installation instructions. + +## Usage + +1. Clone this repository or download the Dockerfile and the `generate-certificate.sh` script. + +2. Build the Docker image using the following command: + + ```shell + docker build -t generate-certificate . + ``` + +3. Run the Docker container with the desired parameters. The container requires specific environment variables to be set: + + - `CA_ACTION`: Specify the action to perform. Possible values are: + - `generateHostSSHCert`: Generates an SSH certificate for the host. + - `generateClientSSHCert`: Generates an SSH certificate for a client. + - `generateClientX509Cert`: Generates an X.509 certificate for a client. + + - `CA_LAMBDA_URL`: The URL of the AWS Lambda function hosting the Private CA. + + Optional environment variables: + - `USER_SSH_DIR`: The path to the directory where the user's SSH keys will be stored. Defaults to "/home/$USER/.ssh". + - `SYSTEM_SSH_DIR`: The path to the system's SSH directory. Defaults to "/etc/ssh". + - `SYSTEM_SSL_DIR`: The path to the system's SSL directory. Defaults to "/etc/ssl". + - `AWS_STS_REGION`: The AWS region for the STS (Security Token Service). Defaults to "ap-south-1". + - `AWS_PROFILE`: The AWS profile for running aws commands. Defaults to "default" + + ```shell + docker run -it --rm \ + -v /path/to/ssh/directory:/home/$USER/.ssh \ + -v /path/to/system/ssh/directory:/etc/ssh \ + -v /path/to/system/ssl/directory:/etc/ssl \ + -e CA_ACTION= \ + -e CA_LAMBDA_URL= \ + -e USER_SSH_DIR= \ + -e SYSTEM_SSH_DIR= \ + -e SYSTEM_SSL_DIR= \ + -e AWS_STS_REGION= \ + -e AWS_PROFILE= \ + generate-certificate + ``` + +4. The script will generate the necessary certificates based on the provided action and store them in the specified directories. + +## Script Explanation + +The `generate-certificate.sh` script performs the following tasks: + +- Parses command-line arguments and checks for the specified CA action. +- Checks if the required SSH or X.509 certificate already exists and is valid. If not, generates new certificates. +- Retrieves temporary AWS credentials using STS (Security Token Service). +- Generates authentication headers required for making authenticated AWS API requests. +- Invokes the AWS Lambda function to generate the certificate based on the specified action. +- Stores the generated certificate in the appropriate directory. + +Make sure to adjust the script parameters and environment variables according to your specific requirements. diff --git a/private-ca/client/aws-auth-header.py b/private-ca/client/Docker/aws-auth-header.py similarity index 100% rename from private-ca/client/aws-auth-header.py rename to private-ca/client/Docker/aws-auth-header.py diff --git a/private-ca/client/generate-certificate-curl.sh b/private-ca/client/Docker/generate-certificate-curl.sh similarity index 98% rename from private-ca/client/generate-certificate-curl.sh rename to private-ca/client/Docker/generate-certificate-curl.sh index 76e9339..e03cc57 100755 --- a/private-ca/client/generate-certificate-curl.sh +++ b/private-ca/client/Docker/generate-certificate-curl.sh @@ -6,8 +6,7 @@ USER_SSH_DIR=${3:-"/home/$USER/.ssh"} SYSTEM_SSH_DIR=${4:-"/etc/ssh"} SYSTEM_SSL_DIR=${5:-"/etc/ssl"} AWS_STS_REGION=${6:-"ap-southeast-1"} -AWS_SCRTS_REGION=${7:-"ap-southeast-1"} -AWS_PROFILE=${8:-"default"} +AWS_PROFILE=${7:-"default"} PYTHON_EXEC=$(which python || which python3) From b078b610f40c4f70f2b99116aad474224de3d046 Mon Sep 17 00:00:00 2001 From: sudo-rgorai Date: Thu, 13 Jul 2023 18:24:20 +0530 Subject: [PATCH 27/36] Fix server deployment issues --- private-ca/client/Docker/Dockerfile | 36 +- .../Docker/generate-certificate-curl.sh | 85 ++- .../client/generate-certificate-curl.sh | 155 +++++ private-ca/server/index_lambda.js | 4 +- private-ca/server/package-lock.json | 596 ++++++++++++++++++ private-ca/update-server-on-lambda.sh | 15 + 6 files changed, 830 insertions(+), 61 deletions(-) create mode 100755 private-ca/client/generate-certificate-curl.sh create mode 100644 private-ca/server/package-lock.json create mode 100755 private-ca/update-server-on-lambda.sh diff --git a/private-ca/client/Docker/Dockerfile b/private-ca/client/Docker/Dockerfile index fb825d1..671e9fb 100644 --- a/private-ca/client/Docker/Dockerfile +++ b/private-ca/client/Docker/Dockerfile @@ -1,32 +1,20 @@ -FROM ubuntu:latest +FROM ubuntu:20.04 -ENV CA_ACTION="" -ENV CA_LAMBDA_URL="" -ENV USER_SSH_DIR="" -ENV SYSTEM_SSH_DIR="" -ENV SYSTEM_SSL_DIR="" -ENV AWS_STS_REGION="" -ENV AWS_PROFILE="" +ENV CA_ACTION=${CA_ACTION} \ + CA_LAMBDA_URL=${CA_LAMBDA_URL} \ + USER_SSH_DIR=${USER_SSH_DIR} \ + SYSTEM_SSH_DIR=${SYSTEM_SSH_DIR} \ + SYSTEM_SSL_DIR=${SYSTEM_SSL_DIR} \ + AWS_STS_REGION=${AWS_STS_REGION} \ + AWS_PROFILE=${AWS_PROFILE} -RUN apt-get update && apt-get install -y \ - python3 \ - python3-pip \ - python3-venv \ - openssl \ - openssh-client \ - jq - -RUN curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip" -RUN unzip awscliv2.zip -RUN sudo ./aws/install +RUN DEBIAN_FRONTEND="noninteractive" apt-get update && apt-get install -y tzdata keyboard-configuration +RUN apt-get -y install curl unzip groff less jq pip python3 python3-venv openssh-client openssl +RUN pip install boto3 COPY . /app WORKDIR /app RUN chmod +x generate-certificate-curl.sh -ENTRYPOINT ["/bin/bash", "-c", "./generate-certificate.sh"] - -CMD [" ${CA_ACTION}" , "${CA_LAMBDA_URL}", "${USER_SSH_DIR}", "${SYSTEM_SSH_DIR}", "${SYSTEM_SSL_DIR}", "${AWS_STS_REGION}", "${AWS_PROFILE}"] - -RUN sleep infinity \ No newline at end of file +ENTRYPOINT ["/bin/bash", "-c", "./generate-certificate-curl.sh", " ${CA_ACTION}" , "${CA_LAMBDA_URL}", "${USER_SSH_DIR}", "${SYSTEM_SSH_DIR}", "${SYSTEM_SSL_DIR}", "${AWS_STS_REGION}", "${AWS_PROFILE}"] \ No newline at end of file diff --git a/private-ca/client/Docker/generate-certificate-curl.sh b/private-ca/client/Docker/generate-certificate-curl.sh index e03cc57..c4ddf62 100755 --- a/private-ca/client/Docker/generate-certificate-curl.sh +++ b/private-ca/client/Docker/generate-certificate-curl.sh @@ -1,7 +1,7 @@ #!/bin/bash -CA_ACTION=${1} -CA_LAMBDA_URL=${2} +CA_ACTION=${1:-$CA_ACTION} +CA_LAMBDA_URL=${2:-$CA_LAMBDA_URL} USER_SSH_DIR=${3:-"/home/$USER/.ssh"} SYSTEM_SSH_DIR=${4:-"/etc/ssh"} SYSTEM_SSL_DIR=${5:-"/etc/ssl"} @@ -30,17 +30,16 @@ done if [[ $CA_ACTION = "generateClientSSHCert" ]]; then if test -f ${USER_SSH_DIR}/id_rsa-cert.pub; then # Client SSH Certificate already exists - current_timestamp=$(date +%s) + current_timestamp=$(date -u +"%Y-%m-%dT%H:%M:%S") certificate_expiration_timestamp=$(ssh-keygen -Lf ${USER_SSH_DIR}/id_rsa-cert.pub | awk '/Valid:/{print $NF}') - certificate_expiration_timestamp_seconds=$(date -d "$certificate_expiration_timestamp" +%s) - if (( certificate_expiration_timestamp_seconds > current_timestamp + 300 )); then - # Certificate is valid for atleast 300 seconds (5 minutes) + if [[ $certificate_expiration_timestamp > $current_timestamp ]]; then + # Certificate is valid echo "A valid certificate was found at ${USER_SSH_DIR}/id_rsa-cert.pub." echo "Aborting..." exit; else - # Certificate expired or about to expire + # Certificate expired rm ${USER_SSH_DIR}/id_rsa-cert.pub fi fi @@ -50,17 +49,16 @@ if [[ $CA_ACTION = "generateClientSSHCert" ]]; then elif [[ $CA_ACTION = "generateHostSSHCert" ]]; then if test -f ${SYSTEM_SSH_DIR}/ssh_host_rsa_key-cert.pub; then # Host SSH Certificate already exists - current_timestamp=$(date +%s) + current_timestamp=$(date -u +"%Y-%m-%dT%H:%M:%S") certificate_expiration_timestamp=$(ssh-keygen -Lf ${SYSTEM_SSH_DIR}/ssh_host_rsa_key-cert.pub | awk '/Valid:/{print $NF}') - certificate_expiration_timestamp_seconds=$(echo "$certificate_expiration_timestamp" | awk -F: '{ print ($1 * 3600) + ($2 * 60) + $3 }') - if (( certificate_expiration_timestamp_seconds > current_timestamp + 300)); then - # Certificate is valid for atleast 300 seconds (5 minutes) + if [[ $certificate_expiration_timestamp > $current_timestamp ]]; then + # Certificate is valid echo "A valid certificate was found at ${SYSTEM_SSH_DIR}/ssh_host_rsa_key-cert.pub." echo "Aborting..." exit; else - # Certificate expired or about to expire + # Certificate expired rm ${SYSTEM_SSH_DIR}/ssh_host_rsa_key-cert.pub fi fi @@ -68,23 +66,23 @@ elif [[ $CA_ACTION = "generateHostSSHCert" ]]; then CERT_PUBKEY=$(cat ${SYSTEM_SSH_DIR}/ssh_host_rsa_key.pub | base64 | tr -d \\n) elif [[ $CA_ACTION = "generateClientX509Cert" ]]; then - test -d ${SYSTEM_SSL_DIR}/privateCA || sudo mkdir -p ${SYSTEM_SSL_DIR}/privateCA + test -d ${SYSTEM_SSL_DIR}/privateCA || mkdir -p ${SYSTEM_SSL_DIR}/privateCA if test -f ${SYSTEM_SSL_DIR}/privateCA/public.crt; then # X.509 Certificate already exists - if ( sudo openssl x509 -checkend 300 -noout -in ${SYSTEM_SSL_DIR}/privateCA/public.crt ); then + if ( openssl x509 -checkend 300 -noout -in ${SYSTEM_SSL_DIR}/privateCA/public.crt ); then # Certificate is valid for atleast 300 seconds (5 minutes) echo "A valid certificate was found at ${SYSTEM_SSL_DIR}/privateCA/public.crt." echo "Aborting..." exit; else # Certificate expired or about to expire - sudo rm ${SYSTEM_SSL_DIR}/privateCA/public.crt + rm ${SYSTEM_SSL_DIR}/privateCA/public.crt fi fi if ! test -f ${SYSTEM_SSL_DIR}/privateCA/public.pem; then - sudo openssl genrsa -out ${SYSTEM_SSL_DIR}/privateCA/key.pem 2048 - sudo openssl rsa -in ${SYSTEM_SSL_DIR}/privateCA/key.pem -outform PEM -pubout -out ${SYSTEM_SSL_DIR}/privateCA/public.pem + openssl genrsa -out ${SYSTEM_SSL_DIR}/privateCA/key.pem 2048 + openssl rsa -in ${SYSTEM_SSL_DIR}/privateCA/key.pem -outform PEM -pubout -out ${SYSTEM_SSL_DIR}/privateCA/public.pem fi CERT_PUBKEY=$(cat ${SYSTEM_SSL_DIR}/privateCA/public.pem | base64 | tr -d \\n) else @@ -97,37 +95,54 @@ else fi # Temporary Credentials -TEMP_CREDS=$(aws sts get-session-token --region $AWS_STS_REGION --profile $AWS_PROFILE) +INSTANCE_ROLE_NAME=$(curl http://169.254.169.254/latest/meta-data/iam/security-credentials/) +TEMP_CREDS=$(curl http://169.254.169.254/latest/meta-data/iam/security-credentials/$INSTANCE_ROLE_NAME) -ACCESS_KEY_ID=$(echo $TEMP_CREDS | jq -r ".Credentials.AccessKeyId") -SECRET_ACCESS_KEY=$(echo $TEMP_CREDS | jq -r ".Credentials.SecretAccessKey") -SESSION_TOKEN=$(echo $TEMP_CREDS | jq -r ".Credentials.SessionToken") +ACCESS_KEY_ID=$(echo $TEMP_CREDS | jq -r ".AccessKeyId") +SECRET_ACCESS_KEY=$(echo $TEMP_CREDS | jq -r ".SecretAccessKey") +SESSION_TOKEN=$(echo $TEMP_CREDS | jq -r ".Token") # Auth Headers -$PYTHON_EXEC -m venv env && source env/bin/activate -pip install boto3 - -output=$(python aws-auth-header.py $ACCESS_KEY_ID $SECRET_ACCESS_KEY $SESSION_TOKEN $AWS_STS_REGION) +output=$($PYTHON_EXEC aws-auth-header.py $ACCESS_KEY_ID $SECRET_ACCESS_KEY $SESSION_TOKEN $AWS_STS_REGION) auth_header=$(echo $output | jq -r ".Authorization") date=$(echo $output | jq -r ".Date") EVENT_JSON=$(echo "{\"auth\":{\"amzDate\":\"${date}\",\"authorizationHeader\":\"${auth_header}\",\"sessionToken\":\"${SESSION_TOKEN}\"},\"certPubkey\":\"${CERT_PUBKEY}\",\"action\":\"${CA_ACTION}\",\"awsSTSRegion\":\"${AWS_STS_REGION}\"}") -ENCODED_CERTIFICATE=$(curl "${CA_LAMBDA_URL}" -H 'content-type: application/json' -d "$EVENT_JSON" | tr -d '"') -CERTIFICATE=$(echo $ENCODED_CERTIFICATE | base64 -d) - - if [[ $CA_ACTION = "generateClientSSHCert" ]]; then + LAMBDA_RESPONSE=$(curl "${CA_LAMBDA_URL}" -H 'content-type: application/json' -d "$EVENT_JSON") + ENCODED_CERTIFICATE=$(echo $LAMBDA_RESPONSE | jq -r ".certificate") + CERTIFICATE=$(echo $ENCODED_CERTIFICATE | base64 -d) + HOST_CA_PUBKEY=$(echo $LAMBDA_RESPONSE | jq -r ".\"host_ca.pub\"" | base64 -d) + echo $CERTIFICATE > ${USER_SSH_DIR}/id_rsa-cert.pub echo "Certificate written to ${USER_SSH_DIR}/id_rsa-cert.pub" + + if [[ $(grep -q "@cert-authority" "${USER_SSH_DIR}/known_hosts"; echo $?) -ne 0 ]]; then + echo "@cert-authority * ${HOST_CA_PUBKEY}" >> ${USER_SSH_DIR}/known_hosts + fi + elif [[ $CA_ACTION = "generateHostSSHCert" ]]; then - sudo sh -c "echo $CERTIFICATE > ${SYSTEM_SSH_DIR}/ssh_host_rsa_key-cert.pub" + LAMBDA_RESPONSE=$(curl "${CA_LAMBDA_URL}" -H 'content-type: application/json' -d "$EVENT_JSON") + ENCODED_CERTIFICATE=$(echo $LAMBDA_RESPONSE | jq -r ".certificate") + CERTIFICATE=$(echo $ENCODED_CERTIFICATE | base64 -d) + USER_CA_PUBKEY=$(echo $LAMBDA_RESPONSE | jq -r ".\"user_ca.pub\"" | base64 -d) + + sh -c "echo $CERTIFICATE > ${SYSTEM_SSH_DIR}/ssh_host_rsa_key-cert.pub" echo "Certificate written to ${SYSTEM_SSH_DIR}/ssh_host_rsa_key-cert.pub" + + test -f ${SYSTEM_SSH_DIR}/user_ca.pub || echo $USER_CA_PUBKEY > ${SYSTEM_SSH_DIR}/user_ca.pub + + if [[ $(grep -q "HostCertificate" "${SYSTEM_SSH_DIR}/sshd_config"; echo $?) -ne 0 ]]; then + echo "HostCertificate ${SYSTEM_SSH_DIR}/ssh_host_rsa_key-cert.pub" >> ${SYSTEM_SSH_DIR}/sshd_config + fi + + if [[ $(grep -q "TrustedUserCAKeys" "${SYSTEM_SSH_DIR}/sshd_config"; echo $?) -ne 0 ]]; then + echo "TrustedUserCAKeys ${SYSTEM_SSH_DIR}/user_ca.pub" >> ${SYSTEM_SSH_DIR}/sshd_config + fi elif [[ $CA_ACTION = "generateClientX509Cert" ]]; then - sudo sh -c "echo '$CERTIFICATE' > ${SYSTEM_SSL_DIR}/privateCA/public.crt" + ENCODED_CERTIFICATE=$(curl "${CA_LAMBDA_URL}" -H 'content-type: application/json' -d "$EVENT_JSON" | tr -d '"') + CERTIFICATE=$(echo $ENCODED_CERTIFICATE | base64 -d) + sh -c "echo '$CERTIFICATE' > ${SYSTEM_SSL_DIR}/privateCA/public.crt" echo "Certificate written to ${SYSTEM_SSL_DIR}/privateCA/public.crt" fi - -# Clean up -deactivate -rm -r env/ \ No newline at end of file diff --git a/private-ca/client/generate-certificate-curl.sh b/private-ca/client/generate-certificate-curl.sh new file mode 100755 index 0000000..3c1d32a --- /dev/null +++ b/private-ca/client/generate-certificate-curl.sh @@ -0,0 +1,155 @@ +#!/bin/bash + +CA_ACTION=${1} +CA_LAMBDA_URL=${2} +USER_SSH_DIR=${3:-"/home/$USER/.ssh"} +SYSTEM_SSH_DIR=${4:-"/etc/ssh"} +SYSTEM_SSL_DIR=${5:-"/etc/ssl"} +AWS_STS_REGION=${6:-"ap-southeast-1"} +AWS_PROFILE=${7:-"default"} + +PYTHON_EXEC=$(which python || which python3) + +# Check for options +while getopts ":h" option; do + case $option in + h) + echo "Usage: ./generate-certificate.sh [ACTION] [PUBLIC KEY FILE] [LAMBDA URL]" + echo "Possible actions:" + echo " generateHostSSHCert: Generates SSH Certificate for Host" + echo " generateClientSSHCert: Generates SSH Certificate for Client" + echo " generateClientX509Cert: Generates X.509 Certificate for Client" + exit;; + *) + echo "Error: Invalid option" + exit;; + esac +done + +# Check for CA Action +if [[ $CA_ACTION = "generateClientSSHCert" ]]; then + if test -f ${USER_SSH_DIR}/id_rsa-cert.pub; then + # Client SSH Certificate already exists + current_timestamp=$(date -u +"%Y-%m-%dT%H:%M:%S") + certificate_expiration_timestamp=$(ssh-keygen -Lf ${USER_SSH_DIR}/id_rsa-cert.pub | awk '/Valid:/{print $NF}') + + if [[ $certificate_expiration_timestamp > $current_timestamp ]]; then + # Certificate is valid + echo "A valid certificate was found at ${USER_SSH_DIR}/id_rsa-cert.pub." + echo "Aborting..." + exit; + else + # Certificate expired + rm ${USER_SSH_DIR}/id_rsa-cert.pub + fi + fi + test -f ${USER_SSH_DIR}/id_rsa.pub || ssh-keygen -t rsa -b 4096 -f ${USER_SSH_DIR}/id_rsa -C host_ca -N "" + CERT_PUBKEY=$(cat ${USER_SSH_DIR}/id_rsa.pub | base64 | tr -d \\n) + +elif [[ $CA_ACTION = "generateHostSSHCert" ]]; then + if test -f ${SYSTEM_SSH_DIR}/ssh_host_rsa_key-cert.pub; then + # Host SSH Certificate already exists + current_timestamp=$(date -u +"%Y-%m-%dT%H:%M:%S") + certificate_expiration_timestamp=$(ssh-keygen -Lf ${SYSTEM_SSH_DIR}/ssh_host_rsa_key-cert.pub | awk '/Valid:/{print $NF}') + + if [[ $certificate_expiration_timestamp_seconds > $current_timestamp ]]; then + # Certificate is valid + echo "A valid certificate was found at ${SYSTEM_SSH_DIR}/ssh_host_rsa_key-cert.pub." + echo "Aborting..." + exit; + else + # Certificate expired + rm ${SYSTEM_SSH_DIR}/ssh_host_rsa_key-cert.pub + fi + fi + test -f ${SYSTEM_SSH_DIR}/ssh_host_rsa_key.pub || sudo ssh-keygen -t rsa -b 4096 -f ${SYSTEM_SSH_DIR}/ssh_host_rsa_key -C host_ca -N "" + CERT_PUBKEY=$(cat ${SYSTEM_SSH_DIR}/ssh_host_rsa_key.pub | base64 | tr -d \\n) + +elif [[ $CA_ACTION = "generateClientX509Cert" ]]; then + test -d ${SYSTEM_SSL_DIR}/privateCA || sudo mkdir -p ${SYSTEM_SSL_DIR}/privateCA + if test -f ${SYSTEM_SSL_DIR}/privateCA/public.crt; then + # X.509 Certificate already exists + + if ( sudo openssl x509 -checkend 300 -noout -in ${SYSTEM_SSL_DIR}/privateCA/public.crt ); then + # Certificate is valid for atleast 300 seconds (5 minutes) + echo "A valid certificate was found at ${SYSTEM_SSL_DIR}/privateCA/public.crt." + echo "Aborting..." + exit; + else + # Certificate expired or about to expire + sudo rm ${SYSTEM_SSL_DIR}/privateCA/public.crt + fi + fi + if ! test -f ${SYSTEM_SSL_DIR}/privateCA/public.pem; then + sudo openssl genrsa -out ${SYSTEM_SSL_DIR}/privateCA/key.pem 2048 + sudo openssl rsa -in ${SYSTEM_SSL_DIR}/privateCA/key.pem -outform PEM -pubout -out ${SYSTEM_SSL_DIR}/privateCA/public.pem + fi + CERT_PUBKEY=$(cat ${SYSTEM_SSL_DIR}/privateCA/public.pem | base64 | tr -d \\n) +else + echo "Invalid Action" + echo "Possible actions include:" + echo " generateHostSSHCert: Generates SSH Certificate for Host" + echo " generateClientSSHCert: Generates SSH Certificate for Client" + echo " generateClientX509Cert: Generates X.509 Certificate for Client" + exit; +fi + +# Temporary Credentials +TEMP_CREDS=$(aws sts get-session-token --region $AWS_STS_REGION --profile $AWS_PROFILE) + +ACCESS_KEY_ID=$(echo $TEMP_CREDS | jq -r ".Credentials.AccessKeyId") +SECRET_ACCESS_KEY=$(echo $TEMP_CREDS | jq -r ".Credentials.SecretAccessKey") +SESSION_TOKEN=$(echo $TEMP_CREDS | jq -r ".Credentials.SessionToken") + +# Auth Headers +$PYTHON_EXEC -m venv env && source env/bin/activate +pip install boto3 + +output=$($PYTHON_EXEC aws-auth-header.py $ACCESS_KEY_ID $SECRET_ACCESS_KEY $SESSION_TOKEN $AWS_STS_REGION) +auth_header=$(echo $output | jq -r ".Authorization") +date=$(echo $output | jq -r ".Date") + +EVENT_JSON=$(echo "{\"auth\":{\"amzDate\":\"${date}\",\"authorizationHeader\":\"${auth_header}\",\"sessionToken\":\"${SESSION_TOKEN}\"},\"certPubkey\":\"${CERT_PUBKEY}\",\"action\":\"${CA_ACTION}\",\"awsSTSRegion\":\"${AWS_STS_REGION}\"}") + + +if [[ $CA_ACTION = "generateClientSSHCert" ]]; then + LAMBDA_RESPONSE=$(curl "${CA_LAMBDA_URL}" -H 'content-type: application/json' -d "$EVENT_JSON") + ENCODED_CERTIFICATE=$(echo $LAMBDA_RESPONSE | jq -r ".certificate") + CERTIFICATE=$(echo $ENCODED_CERTIFICATE | base64 -d) + HOST_CA_PUBKEY=$(echo $LAMBDA_RESPONSE | jq -r ".\"host_ca.pub\"" | base64 -d) + + echo $CERTIFICATE > ${USER_SSH_DIR}/id_rsa-cert.pub + echo "Certificate written to ${USER_SSH_DIR}/id_rsa-cert.pub" + + if [[ $(grep -q "@cert-authority" "${USER_SSH_DIR}/known_hosts"; echo $?) -ne 0 ]]; then + echo "@cert-authority * ${HOST_CA_PUBKEY}" >> ${USER_SSH_DIR}/known_hosts + fi + +elif [[ $CA_ACTION = "generateHostSSHCert" ]]; then + LAMBDA_RESPONSE=$(curl "${CA_LAMBDA_URL}" -H 'content-type: application/json' -d "$EVENT_JSON") + ENCODED_CERTIFICATE=$(echo $LAMBDA_RESPONSE | jq -r ".certificate") + CERTIFICATE=$(echo $ENCODED_CERTIFICATE | base64 -d) + USER_CA_PUBKEY=$(echo $LAMBDA_RESPONSE | jq -r ".\"user_ca.pub\"" | base64 -d) + + sudo sh -c "echo $CERTIFICATE > ${SYSTEM_SSH_DIR}/ssh_host_rsa_key-cert.pub" + echo "Certificate written to ${SYSTEM_SSH_DIR}/ssh_host_rsa_key-cert.pub" + + test -f ${SYSTEM_SSH_DIR}/user_ca.pub || echo $USER_CA_PUBKEY > ${SYSTEM_SSH_DIR}/user_ca.pub + + if [[ $(grep -q "HostCertificate" "${SYSTEM_SSH_DIR}/sshd_config"; echo $?) -ne 0 ]]; then + echo "HostCertificate ${SYSTEM_SSH_DIR}/ssh_host_rsa_key-cert.pub" >> ${SYSTEM_SSH_DIR}/sshd_config + fi + + if [[ $(grep -q "TrustedUserCAKeys" "${SYSTEM_SSH_DIR}/sshd_config"; echo $?) -ne 0 ]]; then + echo "TrustedUserCAKeys ${SYSTEM_SSH_DIR}/user_ca.pub" >> ${SYSTEM_SSH_DIR}/sshd_config + fi +elif [[ $CA_ACTION = "generateClientX509Cert" ]]; then + ENCODED_CERTIFICATE=$(curl "${CA_LAMBDA_URL}" -H 'content-type: application/json' -d "$EVENT_JSON" | tr -d '"') + CERTIFICATE=$(echo $ENCODED_CERTIFICATE | base64 -d) + sudo sh -c "echo '$CERTIFICATE' > ${SYSTEM_SSL_DIR}/privateCA/public.crt" + echo "Certificate written to ${SYSTEM_SSL_DIR}/privateCA/public.crt" +fi + +# Clean up +deactivate +rm -r env/ \ No newline at end of file diff --git a/private-ca/server/index_lambda.js b/private-ca/server/index_lambda.js index 60b5af5..80af176 100644 --- a/private-ca/server/index_lambda.js +++ b/private-ca/server/index_lambda.js @@ -21,13 +21,13 @@ export const handler = async (event) => { const hostSSHCert = await signHostSSHCertificate(callerIdentity, secret, event.certPubkey); return { statusCode: 200, - body: JSON.stringify(Buffer.from(hostSSHCert).toString('base64')) + body: "{\"certificate\" : \""+Buffer.from(hostSSHCert).toString('base64')+"\", \"user_ca.pub\": \""+secret["user_ca.pub"]+"\"}" }; case "generateClientSSHCert": const clientSSHCert = await signClientSSHCertificate(callerIdentity, secret, event.certPubkey); return { statusCode: 200, - body: JSON.stringify(Buffer.from(clientSSHCert).toString('base64')) + body: "{\"certificate\" : \""+Buffer.from(clientSSHCert).toString('base64')+"\", \"host_ca.pub\": \""+secret["host_ca.pub"]+"\"}" }; case "generateClientX509Cert": return await generateClientX509Cert(callerIdentity, secret, event); diff --git a/private-ca/server/package-lock.json b/private-ca/server/package-lock.json new file mode 100644 index 0000000..3600019 --- /dev/null +++ b/private-ca/server/package-lock.json @@ -0,0 +1,596 @@ +{ + "name": "private-ca", + "version": "0.0.1", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "name": "private-ca", + "version": "0.0.1", + "license": "ISC", + "dependencies": { + "aws-sdk": "^2.1398.0", + "node-forge": "^1.3.1" + } + }, + "node_modules/available-typed-arrays": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz", + "integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/aws-sdk": { + "version": "2.1414.0", + "resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.1414.0.tgz", + "integrity": "sha512-WhqTWiTZRUxWITvUG5VMPYGdCLNAm4zOTDIiotbErR9x+uDExk2CAGbXE8HH11+tD8PhZVXyukymSiG+7rJMMg==", + "dependencies": { + "buffer": "4.9.2", + "events": "1.1.1", + "ieee754": "1.1.13", + "jmespath": "0.16.0", + "querystring": "0.2.0", + "sax": "1.2.1", + "url": "0.10.3", + "util": "^0.12.4", + "uuid": "8.0.0", + "xml2js": "0.5.0" + }, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/buffer": { + "version": "4.9.2", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.2.tgz", + "integrity": "sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg==", + "dependencies": { + "base64-js": "^1.0.2", + "ieee754": "^1.1.4", + "isarray": "^1.0.0" + } + }, + "node_modules/call-bind": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", + "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "dependencies": { + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/events": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz", + "integrity": "sha512-kEcvvCBByWXGnZy6JUlgAp2gBIUjfCAV6P6TgT1/aaQKcmuAEC4OZTV1I4EWQLz2gxZw76atuVyvHhTxvi0Flw==", + "engines": { + "node": ">=0.4.x" + } + }, + "node_modules/for-each": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", + "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", + "dependencies": { + "is-callable": "^1.1.3" + } + }, + "node_modules/function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" + }, + "node_modules/get-intrinsic": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.1.tgz", + "integrity": "sha512-2DcsyfABl+gVHEfCOaTrWgyt+tb6MSEGmKq+kI5HwLbIYgjgmMcV8KQ41uaKz1xxUcn9tJtgFbQUEVcEbd0FYw==", + "dependencies": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-proto": "^1.0.1", + "has-symbols": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gopd": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", + "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", + "dependencies": { + "get-intrinsic": "^1.1.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dependencies": { + "function-bind": "^1.1.1" + }, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/has-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz", + "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", + "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", + "dependencies": { + "has-symbols": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/ieee754": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz", + "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==" + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "node_modules/is-arguments": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz", + "integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==", + "dependencies": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-callable": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-generator-function": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.10.tgz", + "integrity": "sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==", + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-typed-array": { + "version": "1.1.10", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.10.tgz", + "integrity": "sha512-PJqgEHiWZvMpaFZ3uTc8kHPM4+4ADTlDniuQL7cU/UDA0Ql7F70yGfHph3cLNe+c9toaigv+DFzTJKhc2CtO6A==", + "dependencies": { + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==" + }, + "node_modules/jmespath": { + "version": "0.16.0", + "resolved": "https://registry.npmjs.org/jmespath/-/jmespath-0.16.0.tgz", + "integrity": "sha512-9FzQjJ7MATs1tSpnco1K6ayiYE3figslrXA72G2HQ/n76RzvYlofyi5QM+iX4YRs/pu3yzxlVQSST23+dMDknw==", + "engines": { + "node": ">= 0.6.0" + } + }, + "node_modules/node-forge": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz", + "integrity": "sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==", + "engines": { + "node": ">= 6.13.0" + } + }, + "node_modules/punycode": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", + "integrity": "sha512-RofWgt/7fL5wP1Y7fxE7/EmTLzQVnB0ycyibJ0OOHIlJqTNzglYFxVwETOcIoJqJmpDXJ9xImDv+Fq34F/d4Dw==" + }, + "node_modules/querystring": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", + "integrity": "sha512-X/xY82scca2tau62i9mDyU9K+I+djTMUsvwf7xnUX5GLvVzgJybOJf4Y6o9Zx3oJK/LSXg5tTZBjwzqVPaPO2g==", + "deprecated": "The querystring API is considered Legacy. new code should use the URLSearchParams API instead.", + "engines": { + "node": ">=0.4.x" + } + }, + "node_modules/sax": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.1.tgz", + "integrity": "sha512-8I2a3LovHTOpm7NV5yOyO8IHqgVsfK4+UuySrXU8YXkSRX7k6hCV9b3HrkKCr3nMpgj+0bmocaJJWpvp1oc7ZA==" + }, + "node_modules/url": { + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/url/-/url-0.10.3.tgz", + "integrity": "sha512-hzSUW2q06EqL1gKM/a+obYHLIO6ct2hwPuviqTTOcfFVc61UbfJ2Q32+uGL/HCPxKqrdGB5QUwIe7UqlDgwsOQ==", + "dependencies": { + "punycode": "1.3.2", + "querystring": "0.2.0" + } + }, + "node_modules/util": { + "version": "0.12.5", + "resolved": "https://registry.npmjs.org/util/-/util-0.12.5.tgz", + "integrity": "sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==", + "dependencies": { + "inherits": "^2.0.3", + "is-arguments": "^1.0.4", + "is-generator-function": "^1.0.7", + "is-typed-array": "^1.1.3", + "which-typed-array": "^1.1.2" + } + }, + "node_modules/uuid": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.0.0.tgz", + "integrity": "sha512-jOXGuXZAWdsTH7eZLtyXMqUb9EcWMGZNbL9YcGBJl4MH4nrxHmZJhEHvyLFrkxo+28uLb/NYRcStH48fnD0Vzw==", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/which-typed-array": { + "version": "1.1.10", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.10.tgz", + "integrity": "sha512-uxoA5vLUfRPdjCuJ1h5LlYdmTLbYfums398v3WLkM+i/Wltl2/XyZpQWKbN++ck5L64SR/grOHqtXCUKmlZPNA==", + "dependencies": { + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-tostringtag": "^1.0.0", + "is-typed-array": "^1.1.10" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/xml2js": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.5.0.tgz", + "integrity": "sha512-drPFnkQJik/O+uPKpqSgr22mpuFHqKdbS835iAQrUC73L2F5WkboIRd63ai/2Yg6I1jzifPFKH2NTK+cfglkIA==", + "dependencies": { + "sax": ">=0.6.0", + "xmlbuilder": "~11.0.0" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/xmlbuilder": { + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz", + "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==", + "engines": { + "node": ">=4.0" + } + } + }, + "dependencies": { + "available-typed-arrays": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz", + "integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==" + }, + "aws-sdk": { + "version": "2.1414.0", + "resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.1414.0.tgz", + "integrity": "sha512-WhqTWiTZRUxWITvUG5VMPYGdCLNAm4zOTDIiotbErR9x+uDExk2CAGbXE8HH11+tD8PhZVXyukymSiG+7rJMMg==", + "requires": { + "buffer": "4.9.2", + "events": "1.1.1", + "ieee754": "1.1.13", + "jmespath": "0.16.0", + "querystring": "0.2.0", + "sax": "1.2.1", + "url": "0.10.3", + "util": "^0.12.4", + "uuid": "8.0.0", + "xml2js": "0.5.0" + } + }, + "base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==" + }, + "buffer": { + "version": "4.9.2", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.2.tgz", + "integrity": "sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg==", + "requires": { + "base64-js": "^1.0.2", + "ieee754": "^1.1.4", + "isarray": "^1.0.0" + } + }, + "call-bind": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", + "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "requires": { + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.2" + } + }, + "events": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz", + "integrity": "sha512-kEcvvCBByWXGnZy6JUlgAp2gBIUjfCAV6P6TgT1/aaQKcmuAEC4OZTV1I4EWQLz2gxZw76atuVyvHhTxvi0Flw==" + }, + "for-each": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", + "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", + "requires": { + "is-callable": "^1.1.3" + } + }, + "function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" + }, + "get-intrinsic": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.1.tgz", + "integrity": "sha512-2DcsyfABl+gVHEfCOaTrWgyt+tb6MSEGmKq+kI5HwLbIYgjgmMcV8KQ41uaKz1xxUcn9tJtgFbQUEVcEbd0FYw==", + "requires": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-proto": "^1.0.1", + "has-symbols": "^1.0.3" + } + }, + "gopd": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", + "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", + "requires": { + "get-intrinsic": "^1.1.3" + } + }, + "has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "requires": { + "function-bind": "^1.1.1" + } + }, + "has-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz", + "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==" + }, + "has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==" + }, + "has-tostringtag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", + "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", + "requires": { + "has-symbols": "^1.0.2" + } + }, + "ieee754": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz", + "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==" + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "is-arguments": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz", + "integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==", + "requires": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + } + }, + "is-callable": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==" + }, + "is-generator-function": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.10.tgz", + "integrity": "sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==", + "requires": { + "has-tostringtag": "^1.0.0" + } + }, + "is-typed-array": { + "version": "1.1.10", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.10.tgz", + "integrity": "sha512-PJqgEHiWZvMpaFZ3uTc8kHPM4+4ADTlDniuQL7cU/UDA0Ql7F70yGfHph3cLNe+c9toaigv+DFzTJKhc2CtO6A==", + "requires": { + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-tostringtag": "^1.0.0" + } + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==" + }, + "jmespath": { + "version": "0.16.0", + "resolved": "https://registry.npmjs.org/jmespath/-/jmespath-0.16.0.tgz", + "integrity": "sha512-9FzQjJ7MATs1tSpnco1K6ayiYE3figslrXA72G2HQ/n76RzvYlofyi5QM+iX4YRs/pu3yzxlVQSST23+dMDknw==" + }, + "node-forge": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz", + "integrity": "sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==" + }, + "punycode": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", + "integrity": "sha512-RofWgt/7fL5wP1Y7fxE7/EmTLzQVnB0ycyibJ0OOHIlJqTNzglYFxVwETOcIoJqJmpDXJ9xImDv+Fq34F/d4Dw==" + }, + "querystring": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", + "integrity": "sha512-X/xY82scca2tau62i9mDyU9K+I+djTMUsvwf7xnUX5GLvVzgJybOJf4Y6o9Zx3oJK/LSXg5tTZBjwzqVPaPO2g==" + }, + "sax": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.1.tgz", + "integrity": "sha512-8I2a3LovHTOpm7NV5yOyO8IHqgVsfK4+UuySrXU8YXkSRX7k6hCV9b3HrkKCr3nMpgj+0bmocaJJWpvp1oc7ZA==" + }, + "url": { + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/url/-/url-0.10.3.tgz", + "integrity": "sha512-hzSUW2q06EqL1gKM/a+obYHLIO6ct2hwPuviqTTOcfFVc61UbfJ2Q32+uGL/HCPxKqrdGB5QUwIe7UqlDgwsOQ==", + "requires": { + "punycode": "1.3.2", + "querystring": "0.2.0" + } + }, + "util": { + "version": "0.12.5", + "resolved": "https://registry.npmjs.org/util/-/util-0.12.5.tgz", + "integrity": "sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==", + "requires": { + "inherits": "^2.0.3", + "is-arguments": "^1.0.4", + "is-generator-function": "^1.0.7", + "is-typed-array": "^1.1.3", + "which-typed-array": "^1.1.2" + } + }, + "uuid": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.0.0.tgz", + "integrity": "sha512-jOXGuXZAWdsTH7eZLtyXMqUb9EcWMGZNbL9YcGBJl4MH4nrxHmZJhEHvyLFrkxo+28uLb/NYRcStH48fnD0Vzw==" + }, + "which-typed-array": { + "version": "1.1.10", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.10.tgz", + "integrity": "sha512-uxoA5vLUfRPdjCuJ1h5LlYdmTLbYfums398v3WLkM+i/Wltl2/XyZpQWKbN++ck5L64SR/grOHqtXCUKmlZPNA==", + "requires": { + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-tostringtag": "^1.0.0", + "is-typed-array": "^1.1.10" + } + }, + "xml2js": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.5.0.tgz", + "integrity": "sha512-drPFnkQJik/O+uPKpqSgr22mpuFHqKdbS835iAQrUC73L2F5WkboIRd63ai/2Yg6I1jzifPFKH2NTK+cfglkIA==", + "requires": { + "sax": ">=0.6.0", + "xmlbuilder": "~11.0.0" + } + }, + "xmlbuilder": { + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz", + "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==" + } + } +} diff --git a/private-ca/update-server-on-lambda.sh b/private-ca/update-server-on-lambda.sh new file mode 100755 index 0000000..5f29625 --- /dev/null +++ b/private-ca/update-server-on-lambda.sh @@ -0,0 +1,15 @@ +FUNCTION_NAME=${1:-'privateCA'} +REGION=${2:-'ap-south-1'} +PROFILE=${3:-'default'} + +cd server +npm i +zip -r ./lambda.zip . +mv lambda.zip ../ +cd .. + +aws lambda update-function-code \ + --function-name $FUNCTION_NAME \ + --zip-file fileb://lambda.zip --region $REGION --profile $PROFILE 1>/dev/null 2>/dev/stderr + +rm -r lambda.zip \ No newline at end of file From 6424640485bac3bf6cf9b06636b07520e8c0f676 Mon Sep 17 00:00:00 2001 From: sudo-rgorai Date: Fri, 14 Jul 2023 12:03:38 +0530 Subject: [PATCH 28/36] Add instruction for AWS secrets region environment variable --- private-ca/README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/private-ca/README.md b/private-ca/README.md index e11ac4c..0f85d5a 100644 --- a/private-ca/README.md +++ b/private-ca/README.md @@ -17,7 +17,8 @@ This creates the following resources on AWS: - A policy to be attached to the role giving read access to created secret - An openSSH layer to facilitate SSH operations - The lambda function to act as a privateCA - + +Note: Once the lambda is deployed you will need to manually add an environment variable called `AWS_SCRTS_REGION` to store the region in which AWS secrets for privateCA reside. ## Usage From a128e392afa0461ee8f118ae8c86fa82a57164bd Mon Sep 17 00:00:00 2001 From: sudo-rgorai Date: Fri, 14 Jul 2023 19:06:00 +0530 Subject: [PATCH 29/36] Specify certificate type --- private-ca/server/generate-client-ssh-cert.js | 2 +- private-ca/server/generate-host-ssh-cert.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/private-ca/server/generate-client-ssh-cert.js b/private-ca/server/generate-client-ssh-cert.js index 111261f..b28abac 100644 --- a/private-ca/server/generate-client-ssh-cert.js +++ b/private-ca/server/generate-client-ssh-cert.js @@ -22,7 +22,7 @@ export const signClientSSHCertificate = async (callerIdentity, secret, certPubke console.log('stdout:', stdout); console.log('stderr:', stderr); - ({ stdout, stderr } = await exec(`ssh-keygen -s ${caKeyPath} -I client_${roleName} -n ${roleName} -V +1d ${publicKeyPath}`)); + ({ stdout, stderr } = await exec(`ssh-keygen -s ${caKeyPath} -t rsa-sha2-512 -I client_${roleName} -n ${roleName} -V +1d ${publicKeyPath}`)); console.log('stdout:', stdout); console.log('stderr:', stderr); diff --git a/private-ca/server/generate-host-ssh-cert.js b/private-ca/server/generate-host-ssh-cert.js index 8d3dd10..eedfccd 100644 --- a/private-ca/server/generate-host-ssh-cert.js +++ b/private-ca/server/generate-host-ssh-cert.js @@ -22,7 +22,7 @@ export const signHostSSHCertificate = async (callerIdentity, secret, certPubkey) console.log('stdout:', stdout); console.log('stderr:', stderr); - ({ stdout, stderr } = await exec(`ssh-keygen -s ${caKeyPath} -I host_${roleName} -h -n ${roleName} -V +1d ${publicKeyPath}`)); + ({ stdout, stderr } = await exec(`ssh-keygen -s ${caKeyPath} -t rsa-sha2-512 -I host_${roleName} -h -n ${roleName} -V +1d ${publicKeyPath}`)); console.log('stdout:', stdout); console.log('stderr:', stderr); From d4fdede5a6719c36b9f94e18776179e2abfc6df1 Mon Sep 17 00:00:00 2001 From: Mohit Garg Date: Fri, 4 Aug 2023 15:42:00 +0530 Subject: [PATCH 30/36] private-ca: use variables for validity, cert details --- private-ca/server/generate-client-ssh-cert.js | 19 ++++++---- .../server/generate-client-x509-cert.js | 35 ++++++++++++------- private-ca/update-server-on-lambda.sh | 2 +- 3 files changed, 37 insertions(+), 19 deletions(-) diff --git a/private-ca/server/generate-client-ssh-cert.js b/private-ca/server/generate-client-ssh-cert.js index b28abac..06f5d0d 100644 --- a/private-ca/server/generate-client-ssh-cert.js +++ b/private-ca/server/generate-client-ssh-cert.js @@ -3,17 +3,18 @@ import child_process from 'child_process'; import util from 'util'; const exec = util.promisify(child_process.exec); +const validityInDays = process.env.validityInDays ?? 1; +const caKeyPath = "/tmp/client_ca"; +const publicKeyName = "ssh_client_rsa_key"; +const publicKeyPath = "/tmp/" + publicKeyName + ".pub"; +const certificatePath = "/tmp/" + publicKeyName + "-cert.pub"; export const signClientSSHCertificate = async (callerIdentity, secret, certPubkey) => { const arn = callerIdentity.GetCallerIdentityResponse.GetCallerIdentityResult.Arn; const roleName = arn.match(/\/([^/]+)$/)?.[1]; - - const caKeyPath = "/tmp/client_ca"; - const publicKeyName = "ssh_client_rsa_key"; - const publicKeyPath = "/tmp/" + publicKeyName + ".pub"; - const certificatePath = "/tmp/" + publicKeyName + "-cert.pub"; const user_ca = Buffer.from(secret.user_ca, 'base64').toString('utf-8'); + certPubkey = Buffer.from(certPubkey, 'base64').toString('utf-8'); fs.writeFileSync(caKeyPath, user_ca); fs.writeFileSync(publicKeyPath, certPubkey); @@ -22,10 +23,16 @@ export const signClientSSHCertificate = async (callerIdentity, secret, certPubke console.log('stdout:', stdout); console.log('stderr:', stderr); - ({ stdout, stderr } = await exec(`ssh-keygen -s ${caKeyPath} -t rsa-sha2-512 -I client_${roleName} -n ${roleName} -V +1d ${publicKeyPath}`)); + ( + { stdout, stderr } = await exec( + `ssh-keygen -s ${caKeyPath} -t rsa-sha2-512 -I client_${roleName} -n ${roleName} -V +${validityInDays}d ${publicKeyPath}` + ) + ); + console.log('stdout:', stdout); console.log('stderr:', stderr); const certificate = fs.readFileSync(certificatePath, 'utf8'); return certificate; + }; diff --git a/private-ca/server/generate-client-x509-cert.js b/private-ca/server/generate-client-x509-cert.js index d63a9c0..545291f 100644 --- a/private-ca/server/generate-client-x509-cert.js +++ b/private-ca/server/generate-client-x509-cert.js @@ -1,5 +1,14 @@ import forge from 'node-forge'; import md from 'node-forge'; +import crypto from "crypto"; + +const countryName = process.env.countryName ?? "SG"; +const localityName = process.env.localityName ?? "Singapore"; +const organizationName = process.env.organizationName ?? "Fundwave"; +const organizationalUnitName = process.env.localityName ?? "Fundwave"; + +const validityInDays = process.env.validityInDays ?? 1; +const messageDigestAlg = process.env.messageDigestAlg ?? "sha256"; export const generateClientX509Cert = async (callerIdentity, secret, event) => { @@ -33,23 +42,17 @@ export const generateClientX509Cert = async (callerIdentity, secret, event) => { const clientCertReq = pki.createCertificationRequest(); clientCertReq.publicKey = clientPublicKey; clientCertReq.setSubject([ - { name: 'countryName', value: 'US' }, - { name: 'localityName', value: 'California' }, - { name: 'organizationName', value: 'Fundwave' }, - { name: 'organizationalUnitName', value: 'Fundwave' }, + { name: 'countryName', value: countryName }, + { name: 'localityName', value: localityName }, + { name: 'organizationName', value: organizationName }, + { name: 'organizationalUnitName', value: organizationalUnitName }, { name: 'commonName', value: roleName } ]); // Sign the client certificate request with the root certificate and private key const clientCert = pki.createCertificate(); clientCert.publicKey = clientCertReq.publicKey; - clientCert.serialNumber = '01'; // Set a unique serial number - - const startDate = new Date(); // Valid from the current date and time - const endDate = new Date(); - endDate.setDate(startDate.getDate() + 1); - clientCert.validity.notBefore = startDate; - clientCert.validity.notAfter = endDate; + // clientCert.serialNumber = crypto.randomBytes(8).toString("hex"); // Set a unique serial number upto 20 bytes. https://security.stackexchange.com/questions/35691/what-is-the-difference-between-serial-number-and-thumbprint https://www.hindawi.com/journals/scn/2019/6013846/ clientCert.setSubject(clientCertReq.subject.attributes); clientCert.setIssuer(rootCert.subject.attributes); @@ -57,7 +60,14 @@ export const generateClientX509Cert = async (callerIdentity, secret, event) => { { name: 'basicConstraints', cA: false }, { name: 'keyUsage', digitalSignature: true, nonRepudiation: true, keyEncipherment: true }, ]); - clientCert.sign(rootKey, md.sha256.create()); + + const startDate = new Date(); // Valid from the current date and time + const endDate = new Date(); + endDate.setDate(startDate.getDate() + validityInDays); + clientCert.validity.notBefore = startDate; + clientCert.validity.notAfter = endDate; + + clientCert.sign(rootKey, md[messageDigestAlg].create()); // Convert the signed client certificate to PEM format const clientCertPem = pki.certificateToPem(clientCert); @@ -65,4 +75,5 @@ export const generateClientX509Cert = async (callerIdentity, secret, event) => { statusCode: 200, body: Buffer.from(clientCertPem).toString('base64') }; + } \ No newline at end of file diff --git a/private-ca/update-server-on-lambda.sh b/private-ca/update-server-on-lambda.sh index 5f29625..7bc60ca 100755 --- a/private-ca/update-server-on-lambda.sh +++ b/private-ca/update-server-on-lambda.sh @@ -1,5 +1,5 @@ FUNCTION_NAME=${1:-'privateCA'} -REGION=${2:-'ap-south-1'} +REGION=${2:-'ap-southeast-1'} PROFILE=${3:-'default'} cd server From 27202929c22bd1001ef471e3da4c8c9ab13ed3c7 Mon Sep 17 00:00:00 2001 From: Srinivasan T M <=> Date: Mon, 25 Sep 2023 13:09:53 +0530 Subject: [PATCH 31/36] Added cron job to Docker Container to regenerate certs everyday --- private-ca/client/Docker/Dockerfile | 15 +++++++++------ private-ca/client/Docker/run.sh | 4 ++++ 2 files changed, 13 insertions(+), 6 deletions(-) create mode 100644 private-ca/client/Docker/run.sh diff --git a/private-ca/client/Docker/Dockerfile b/private-ca/client/Docker/Dockerfile index 671e9fb..7a8985b 100644 --- a/private-ca/client/Docker/Dockerfile +++ b/private-ca/client/Docker/Dockerfile @@ -1,4 +1,4 @@ -FROM ubuntu:20.04 +FROM alpine:latest ENV CA_ACTION=${CA_ACTION} \ CA_LAMBDA_URL=${CA_LAMBDA_URL} \ @@ -8,13 +8,16 @@ ENV CA_ACTION=${CA_ACTION} \ AWS_STS_REGION=${AWS_STS_REGION} \ AWS_PROFILE=${AWS_PROFILE} -RUN DEBIAN_FRONTEND="noninteractive" apt-get update && apt-get install -y tzdata keyboard-configuration -RUN apt-get -y install curl unzip groff less jq pip python3 python3-venv openssh-client openssl -RUN pip install boto3 +# RUN DEBIAN_FRONTEND="noninteractive" apt update && apt install -y tzdata keyboard-configuration +# RUN apt -y install curl unzip groff less jq pip python3 python3-venv openssh-client openssl cron +# RUN pip install boto3 +RUN apk add bash curl unzip groff jq python3 openssh openssl +RUN python3 -m ensurepip +RUN pip3 install boto3 COPY . /app WORKDIR /app RUN chmod +x generate-certificate-curl.sh - -ENTRYPOINT ["/bin/bash", "-c", "./generate-certificate-curl.sh", " ${CA_ACTION}" , "${CA_LAMBDA_URL}", "${USER_SSH_DIR}", "${SYSTEM_SSH_DIR}", "${SYSTEM_SSL_DIR}", "${AWS_STS_REGION}", "${AWS_PROFILE}"] \ No newline at end of file +RUN chmod +x run.sh +ENTRYPOINT ["/app/run.sh" ] diff --git a/private-ca/client/Docker/run.sh b/private-ca/client/Docker/run.sh new file mode 100644 index 0000000..20afafb --- /dev/null +++ b/private-ca/client/Docker/run.sh @@ -0,0 +1,4 @@ +#!/bin/bash +echo "0 0 */1 * * generate-certificate-curl.sh ${CA_ACTION} ${CA_LAMBDA_URL} ${USER_SSH_DIR} ${SYSTEM_SSH_DIR} ${SYSTEM_SSL_DIR} ${AWS_STS_REGION} ${AWS_PROFILE} > /dev/stdout" > crontab.txt +/usr/bin/crontab crontab.txt +/usr/sbin/crond -f -l 8 From 23f4391171cc37703c7dadc1725b1b6782d0545d Mon Sep 17 00:00:00 2001 From: Srinivasan T M <=> Date: Mon, 25 Sep 2023 14:16:02 +0530 Subject: [PATCH 32/36] added override default cert location for curl --- private-ca/client/Docker/Dockerfile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/private-ca/client/Docker/Dockerfile b/private-ca/client/Docker/Dockerfile index 7a8985b..5bab92d 100644 --- a/private-ca/client/Docker/Dockerfile +++ b/private-ca/client/Docker/Dockerfile @@ -6,7 +6,8 @@ ENV CA_ACTION=${CA_ACTION} \ SYSTEM_SSH_DIR=${SYSTEM_SSH_DIR} \ SYSTEM_SSL_DIR=${SYSTEM_SSL_DIR} \ AWS_STS_REGION=${AWS_STS_REGION} \ - AWS_PROFILE=${AWS_PROFILE} + AWS_PROFILE=${AWS_PROFILE} \ + CURL_CA_BUNDLE="/etc/ssl/certs/ca_bundle.crt" # RUN DEBIAN_FRONTEND="noninteractive" apt update && apt install -y tzdata keyboard-configuration # RUN apt -y install curl unzip groff less jq pip python3 python3-venv openssh-client openssl cron From 312cda7fafac04164b6145ae2590c0e0de914262 Mon Sep 17 00:00:00 2001 From: Srinivasan T M <=> Date: Fri, 6 Oct 2023 16:22:22 +0530 Subject: [PATCH 33/36] workflow to push private ca container to docker hub --- .../workflows/build-private-ca-on-push.yaml | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 .github/workflows/build-private-ca-on-push.yaml diff --git a/.github/workflows/build-private-ca-on-push.yaml b/.github/workflows/build-private-ca-on-push.yaml new file mode 100644 index 0000000..4202c70 --- /dev/null +++ b/.github/workflows/build-private-ca-on-push.yaml @@ -0,0 +1,26 @@ +name: Build Private CA on Push + +on: + push: + paths: + - private-ca/client/Docker/Dockerfile + - .github/workflows/build-private-ca-on-push.yml + +jobs: + + docker_publish: + runs-on: "ubuntu-22.04" + steps: + - uses: actions/checkout@v3 + - name: Build Docker Image + run: | + cd private-ca/client/Docker/Dockerfile + [[ "$GITHUB_REF_NAME" == "main" ]] && export TAG="latest" || TAG="$GITHUB_REF_NAME" + docker build . --tag ghcr.io/getfundwave/network-utils/private-ca:$TAG + - name: Publish docker image + env: + TOKEN: ${{secrets.GITHUB_TOKEN}} + run: | + echo $TOKEN | docker login ghcr.io -u $GITHUB_ACTOR --password-stdin + [[ "$GITHUB_REF_NAME" == "main" ]] && export TAG="latest" || TAG="$GITHUB_REF_NAME" + docker push ghcr.io/getfundwave/network-utils/private-ca:$TAG From 9df7571d7782613ea5d55bac0618d1a789132f3d Mon Sep 17 00:00:00 2001 From: Srinivasan T M <=> Date: Fri, 13 Oct 2023 12:19:30 +0530 Subject: [PATCH 34/36] updated push path for privateCA workflow --- .github/workflows/build-private-ca-on-push.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build-private-ca-on-push.yaml b/.github/workflows/build-private-ca-on-push.yaml index 4202c70..7c6a136 100644 --- a/.github/workflows/build-private-ca-on-push.yaml +++ b/.github/workflows/build-private-ca-on-push.yaml @@ -3,7 +3,7 @@ name: Build Private CA on Push on: push: paths: - - private-ca/client/Docker/Dockerfile + - private-ca/client/Docker/** - .github/workflows/build-private-ca-on-push.yml jobs: From dfa428cc6b7040626f5628875b1cb2938b1896c4 Mon Sep 17 00:00:00 2001 From: Srinivasan T M <=> Date: Thu, 26 Oct 2023 11:08:56 +0530 Subject: [PATCH 35/36] fix: set curl cert bundle path fix: change directory before running cronjob --- private-ca/client/Docker/Dockerfile | 17 ++++------------- private-ca/client/Docker/run.sh | 2 +- 2 files changed, 5 insertions(+), 14 deletions(-) diff --git a/private-ca/client/Docker/Dockerfile b/private-ca/client/Docker/Dockerfile index 5bab92d..f2bb956 100644 --- a/private-ca/client/Docker/Dockerfile +++ b/private-ca/client/Docker/Dockerfile @@ -1,24 +1,15 @@ FROM alpine:latest -ENV CA_ACTION=${CA_ACTION} \ - CA_LAMBDA_URL=${CA_LAMBDA_URL} \ - USER_SSH_DIR=${USER_SSH_DIR} \ - SYSTEM_SSH_DIR=${SYSTEM_SSH_DIR} \ - SYSTEM_SSL_DIR=${SYSTEM_SSL_DIR} \ - AWS_STS_REGION=${AWS_STS_REGION} \ - AWS_PROFILE=${AWS_PROFILE} \ - CURL_CA_BUNDLE="/etc/ssl/certs/ca_bundle.crt" - -# RUN DEBIAN_FRONTEND="noninteractive" apt update && apt install -y tzdata keyboard-configuration -# RUN apt -y install curl unzip groff less jq pip python3 python3-venv openssh-client openssl cron -# RUN pip install boto3 RUN apk add bash curl unzip groff jq python3 openssh openssl RUN python3 -m ensurepip RUN pip3 install boto3 -COPY . /app WORKDIR /app +COPY generate-certificate-curl.sh /app +COPY run.sh /app +COPY aws-auth-header.py /app RUN chmod +x generate-certificate-curl.sh RUN chmod +x run.sh +ENV CURL_CA_BUNDLE="/etc/pki/tls/certs/ca-bundle.crt" ENTRYPOINT ["/app/run.sh" ] diff --git a/private-ca/client/Docker/run.sh b/private-ca/client/Docker/run.sh index 20afafb..48261b9 100644 --- a/private-ca/client/Docker/run.sh +++ b/private-ca/client/Docker/run.sh @@ -1,4 +1,4 @@ #!/bin/bash -echo "0 0 */1 * * generate-certificate-curl.sh ${CA_ACTION} ${CA_LAMBDA_URL} ${USER_SSH_DIR} ${SYSTEM_SSH_DIR} ${SYSTEM_SSL_DIR} ${AWS_STS_REGION} ${AWS_PROFILE} > /dev/stdout" > crontab.txt +echo "0 0 */1 * * cd /app && generate-certificate-curl.sh generateHostSSHCert ${CA_LAMBDA_URL} ${USER_SSH_DIR} ${SYSTEM_SSH_DIR} ${SYSTEM_SSL_DIR} ${AWS_STS_REGION} ${AWS_PROFILE} > /dev/stdout" > crontab.txt /usr/bin/crontab crontab.txt /usr/sbin/crond -f -l 8 From 315bc951b6d5f5a96f741aea01f3069b78a71ae1 Mon Sep 17 00:00:00 2001 From: Srinivasan T M <=> Date: Thu, 26 Oct 2023 13:02:31 +0530 Subject: [PATCH 36/36] fix: changed cd path [skip ci] --- .github/workflows/build-private-ca-on-push.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build-private-ca-on-push.yaml b/.github/workflows/build-private-ca-on-push.yaml index 7c6a136..0004263 100644 --- a/.github/workflows/build-private-ca-on-push.yaml +++ b/.github/workflows/build-private-ca-on-push.yaml @@ -14,7 +14,7 @@ jobs: - uses: actions/checkout@v3 - name: Build Docker Image run: | - cd private-ca/client/Docker/Dockerfile + cd private-ca/client/Docker/ [[ "$GITHUB_REF_NAME" == "main" ]] && export TAG="latest" || TAG="$GITHUB_REF_NAME" docker build . --tag ghcr.io/getfundwave/network-utils/private-ca:$TAG - name: Publish docker image